Showing preview only (631K chars total). Download the full file or copy to clipboard to get everything.
Repository: tersesystems/terse-logback
Branch: master
Commit: 7e86ac867de1
Files: 309
Total size: 534.5 KB
Directory structure:
gitextract_k2vdfda5/
├── .github/
│ └── ISSUE_TEMPLATE/
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .java-version
├── LICENSE
├── README.md
├── RELEASING.md
├── build.gradle
├── docs/
│ ├── guide/
│ │ ├── audio.md
│ │ ├── budget.md
│ │ ├── censor.md
│ │ ├── composite.md
│ │ ├── compression.md
│ │ ├── correlationid.md
│ │ ├── exception-mapping.md
│ │ ├── instrumentation.md
│ │ ├── jdbc.md
│ │ ├── relativens.md
│ │ ├── select.md
│ │ ├── slf4jbridge.md
│ │ ├── tracing.md
│ │ ├── turbomarker.md
│ │ ├── typesafeconfig.md
│ │ └── uniqueid.md
│ ├── index.md
│ └── reading/
│ └── reading.md
├── gradle/
│ ├── LICENSE_HEADER
│ ├── java-publication.gradle
│ ├── release.gradle
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── logback-audio/
│ ├── gradle.properties
│ ├── logback-audio.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── audio/
│ │ ├── AudioAppender.java
│ │ ├── AudioMarker.java
│ │ ├── AudioMarkerAppender.java
│ │ ├── FilePlayer.java
│ │ ├── PlayMethods.java
│ │ ├── Player.java
│ │ ├── PlayerAction.java
│ │ ├── PlayerAttachable.java
│ │ ├── PlayerConverter.java
│ │ ├── PlayerException.java
│ │ ├── ResourcePlayer.java
│ │ ├── SimplePlayer.java
│ │ ├── SystemPlayer.java
│ │ └── URLPlayer.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── audio/
│ │ ├── TestAudio.java
│ │ └── TestNested.java
│ └── resources/
│ ├── bark.ogg
│ ├── drip.ogg
│ ├── glass.ogg
│ ├── logback-with-converter.xml
│ ├── logback-with-marker-appender.xml
│ ├── logback-with-nested-appender.xml
│ ├── message.ogg
│ └── sample.ogg
├── logback-budget/
│ ├── gradle.properties
│ ├── logback-budget.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── budget/
│ │ ├── BudgetEvaluator.java
│ │ ├── BudgetRule.java
│ │ ├── BudgetRuleAction.java
│ │ ├── BudgetRuleAttachable.java
│ │ └── BudgetTurboFilter.java
│ └── test/
│ ├── java/
│ │ └── com.tersesystems.logback.budget/
│ │ ├── BudgetEvaluatorTest.java
│ │ └── BudgetTurboFilterTest.java
│ └── resources/
│ ├── logback-budget.xml
│ └── logback-turbofilter.xml
├── logback-bytebuddy/
│ ├── gradle.properties
│ ├── logback-bytebuddy.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── bytebuddy/
│ │ ├── AdviceConfig.java
│ │ ├── LogbackInstrumentationAgent.java
│ │ ├── LoggingInstrumentationAdvice.java
│ │ ├── LoggingInstrumentationByteBuddyBuilder.java
│ │ ├── MethodInfo.java
│ │ ├── MethodInfoLookup.java
│ │ └── impl/
│ │ ├── DeclaringTypeLoggerResolver.java
│ │ ├── Enter.java
│ │ ├── Exit.java
│ │ ├── FixedLoggerResolver.java
│ │ ├── LoggerResolver.java
│ │ ├── SafeArguments.java
│ │ └── SystemFlow.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── bytebuddy/
│ │ ├── AdviceConfigTest.java
│ │ ├── ClassCalledByAgent.java
│ │ ├── InProcessInstrumentationExample.java
│ │ └── PreloadedInstrumentationExample.java
│ └── resources/
│ ├── logback-test.xml
│ └── logback.conf
├── logback-censor/
│ ├── gradle.properties
│ ├── logback-censor.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── censor/
│ │ ├── Censor.java
│ │ ├── CensorAction.java
│ │ ├── CensorAttachable.java
│ │ ├── CensorConstants.java
│ │ ├── CensorContextAware.java
│ │ ├── CensorConverter.java
│ │ ├── CensorRefAction.java
│ │ ├── CensoringJsonGeneratorDecorator.java
│ │ ├── CensoringPrettyPrintingJsonGeneratorDecorator.java
│ │ └── RegexCensor.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── censor/
│ │ ├── CensorActionTest.java
│ │ ├── CensoringJsonGeneratorDecoratorTest.java
│ │ ├── RegexCensorTest.java
│ │ └── TestAppender.java
│ └── resources/
│ ├── test1.xml
│ ├── test2.xml
│ ├── test3.xml
│ └── test4.xml
├── logback-classic/
│ ├── gradle.properties
│ ├── logback-classic.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── classic/
│ │ ├── ChangeLogLevel.java
│ │ ├── ContainerEventAppender.java
│ │ ├── ContainerProxyLoggingEvent.java
│ │ ├── ContextAwareBasicMarker.java
│ │ ├── ExceptionMessageConverter.java
│ │ ├── FormatParamsDecider.java
│ │ ├── IContainerLoggingEvent.java
│ │ ├── ILoggingEventFactory.java
│ │ ├── LoggerDecider.java
│ │ ├── LoggingEventFactory.java
│ │ ├── MarkerLoggerDecider.java
│ │ ├── NanoTime.java
│ │ ├── NanoTimeComponentAppender.java
│ │ ├── NanoTimeConverter.java
│ │ ├── NanoTimeMarker.java
│ │ ├── NanoTimeSupplier.java
│ │ ├── ProxyLoggingEvent.java
│ │ ├── SLF4JBridgeHandlerAction.java
│ │ ├── SetLoggerLevelsAction.java
│ │ ├── StartTime.java
│ │ ├── StartTimeConverter.java
│ │ ├── StartTimeMarker.java
│ │ ├── StartTimeSupplier.java
│ │ ├── TapFilter.java
│ │ ├── TerseBasicMarker.java
│ │ ├── TerseHighlightConverter.java
│ │ ├── TimeSinceEpochConverter.java
│ │ ├── TurboFilterDecider.java
│ │ ├── Utils.java
│ │ ├── encoder/
│ │ │ └── PatternLayoutEncoder.java
│ │ ├── functional/
│ │ │ ├── GetAppenderFunction.java
│ │ │ ├── GetSiftedAppenderFunction.java
│ │ │ └── RootLoggerSupplier.java
│ │ └── sift/
│ │ ├── DiscriminatingMarker.java
│ │ ├── DiscriminatingMarkerFactory.java
│ │ ├── DiscriminatingValue.java
│ │ └── MarkerBasedDiscriminator.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── classic/
│ │ ├── ChangeLogLevelTest.java
│ │ ├── CorrelationIdMarker.java
│ │ ├── CorrelationIdTurboFilter.java
│ │ ├── EnabledFilterTest.java
│ │ ├── ExceptionMessageConverterTest.java
│ │ ├── SetLoggerLevelsActionTest.java
│ │ ├── TapFilterTest.java
│ │ ├── TerseHighlightConverterTest.java
│ │ └── UtilsTest.java
│ └── resources/
│ ├── logback-tapfilter-correlation.xml
│ └── logback-tapfilter.xml
├── logback-compress-encoder/
│ ├── gradle.properties
│ ├── logback-compress-encoder.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com.tersesystems.logback.compress/
│ │ ├── CompressingEncoder.java
│ │ └── CompressingFileAppender.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── compress/
│ │ └── Utils.java
│ └── resources/
│ └── logback-with-zstd-encoder.xml
├── logback-core/
│ ├── gradle.properties
│ ├── logback-core.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── core/
│ │ ├── AbstractAppender.java
│ │ ├── Component.java
│ │ ├── ComponentContainer.java
│ │ ├── CompositeAppender.java
│ │ ├── DecoratingAppender.java
│ │ ├── DefaultAppenderAttachable.java
│ │ ├── EnabledFilter.java
│ │ ├── SelectAppender.java
│ │ ├── encoder/
│ │ │ └── LayoutWrappingEncoder.java
│ │ └── pattern/
│ │ └── PatternLayoutEncoderBase.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── core/
│ │ ├── CompositeAppenderTest.java
│ │ ├── SelectAppenderTest.java
│ │ └── TestAppender.java
│ └── resources/
│ ├── logback-with-composite-appender.xml
│ └── logback-with-select-appender.xml
├── logback-correlationid/
│ ├── gradle.properties
│ ├── logback-correlationid.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── correlationid/
│ │ ├── CorrelationIdDecider.java
│ │ ├── CorrelationIdFilter.java
│ │ ├── CorrelationIdMarker.java
│ │ ├── CorrelationIdProvider.java
│ │ ├── CorrelationIdTapFilter.java
│ │ └── CorrelationIdUtils.java
│ └── test/
│ ├── java/
│ │ └── com.tersesystems.logback.correlationid/
│ │ ├── CorrelationIdFilterTest.java
│ │ └── CorrelationIdTapFilterTest.java
│ └── resources/
│ ├── logback-correlationid-jdbc.xml
│ ├── logback-correlationid-tapfilter.xml
│ ├── logback-correlationid.xml
│ └── spy.properties
├── logback-exception-mapping/
│ ├── gradle.properties
│ ├── logback-exception-mapping.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── exceptionmapping/
│ │ ├── BeanExceptionMapping.java
│ │ ├── Constants.java
│ │ ├── DefaultExceptionMappingRegistry.java
│ │ ├── ExceptionCauseIterator.java
│ │ ├── ExceptionHierarchyIterator.java
│ │ ├── ExceptionMapping.java
│ │ ├── ExceptionMappingAction.java
│ │ ├── ExceptionMappingRegistry.java
│ │ ├── ExceptionMappingRegistryAction.java
│ │ ├── ExceptionMessageWithMappingsConverter.java
│ │ ├── ExceptionProperty.java
│ │ ├── FunctionExceptionMapping.java
│ │ └── KeyValueExceptionProperty.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── exceptionmapping/
│ │ ├── ExceptionMappingTest.java
│ │ ├── MyCustomException.java
│ │ └── Thrower.java
│ └── resources/
│ └── logback-test.xml
├── logback-exception-mapping-providers/
│ ├── gradle.properties
│ ├── logback-exception-mapping-providers.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── exceptionmapping/
│ │ ├── config/
│ │ │ └── TypesafeConfigMappingsAction.java
│ │ └── json/
│ │ └── ExceptionArgumentsProvider.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── exceptionmapping/
│ │ └── json/
│ │ ├── ExceptionArgumentsProviderTest.java
│ │ ├── MySpecialException.java
│ │ └── TypesafeConfigMappingsActionTest.java
│ └── resources/
│ ├── logback-with-exception-mapping.xml
│ └── logback.conf
├── logback-honeycomb-appender/
│ ├── gradle.properties
│ ├── logback-honeycomb-appender.gradle
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── tersesystems/
│ └── logback/
│ └── honeycomb/
│ └── HoneycombAppender.java
├── logback-honeycomb-client/
│ ├── gradle.properties
│ ├── logback-honeycomb-client.gradle
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── tersesystems/
│ └── logback/
│ └── honeycomb/
│ └── client/
│ ├── HoneycombClient.java
│ ├── HoneycombClientService.java
│ ├── HoneycombHeaders.java
│ ├── HoneycombRequest.java
│ └── HoneycombResponse.java
├── logback-honeycomb-okhttp/
│ ├── gradle.properties
│ ├── logback-honeycomb-okhttp.gradle
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── honeycomb/
│ │ └── okhttp/
│ │ ├── HoneycombOkHTTPClient.java
│ │ └── HoneycombOkHTTPClientService.java
│ └── resources/
│ └── META-INF/
│ └── services/
│ └── com.tersesystems.logback.honeycomb.client.HoneycombClientService
├── logback-jdbc-appender/
│ ├── gradle.properties
│ ├── logback-jdbc-appender.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── jdbc/
│ │ └── JDBCAppender.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── jdbc/
│ │ └── JDBCAppenderTest.java
│ └── resources/
│ ├── logback-reference.conf
│ └── logback-test.xml
├── logback-postgresjson-appender/
│ ├── gradle.properties
│ ├── logback-postgresjson-appender.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── postgresjson/
│ │ └── PostgresJsonAppender.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── postgresjson/
│ │ └── PostgresJsonAppenderTest.java
│ └── resources/
│ ├── db/
│ │ └── migration/
│ │ └── V1__logging_table.sql
│ └── logback-postgres-json.xml
├── logback-tracing/
│ ├── gradle.properties
│ ├── logback-tracing.gradle
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── tersesystems/
│ └── logback/
│ └── tracing/
│ ├── EventInfo.java
│ ├── EventMarkerFactory.java
│ ├── LinkInfo.java
│ ├── LinkMarkerFactory.java
│ ├── Nullable.java
│ ├── SpanInfo.java
│ ├── SpanMarkerFactory.java
│ └── Tracer.java
├── logback-turbomarker/
│ ├── gradle.properties
│ ├── logback-turbomarker.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── turbomarker/
│ │ ├── ContextAwareTurboFilterDecider.java
│ │ ├── ContextAwareTurboMarker.java
│ │ ├── ContextDecider.java
│ │ ├── LoggerContextDecider.java
│ │ ├── MarkerContextDecider.java
│ │ ├── TurboMarker.java
│ │ └── TurboMarkerTurboFilter.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── turbomarker/
│ │ ├── ApplicationContext.java
│ │ ├── DiagnosticLoggingExample.java
│ │ ├── LDMarkerFactory.java
│ │ ├── LDMarkerTest.java
│ │ ├── UserMarker.java
│ │ ├── UserMarkerFactory.java
│ │ └── UserMarkerTest.java
│ └── resources/
│ └── logback-test.xml
├── logback-typesafe-config/
│ ├── gradle.properties
│ ├── logback-typesafe-config.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── typesafeconfig/
│ │ ├── ConfigConstants.java
│ │ ├── ConfigConversion.java
│ │ ├── ConfigListConverter.java
│ │ └── TypesafeConfigAction.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── typesafeconfig/
│ │ ├── ConfigListConverterTest.java
│ │ └── TypesafeConfigActionTest.java
│ └── resources/
│ ├── logback-test.conf
│ └── typesafeconfig/
│ ├── config-with-context.xml
│ ├── config-with-default.xml
│ └── config-with-local.xml
├── logback-uniqueid-appender/
│ ├── gradle.properties
│ ├── logback-uniqueid-appender.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── uniqueid/
│ │ ├── FlakeIdGenerator.java
│ │ ├── IdGenerator.java
│ │ ├── KsuidSubsecondIdGenerator.java
│ │ ├── RandomUUIDIdGenerator.java
│ │ ├── TsidIdgenerator.java
│ │ ├── UlidIdGenerator.java
│ │ ├── UniqueIdComponentAppender.java
│ │ ├── UniqueIdConverter.java
│ │ └── UniqueIdProvider.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── tersesystems/
│ │ └── logback/
│ │ └── uniqueid/
│ │ └── UniqueIdAppenderTest.java
│ └── resources/
│ └── logback-with-uniqueid-appender.xml
├── mkdocs.yml
├── settings.gradle
└── version.properties
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .gitignore
================================================
# Ignore Gradle project-specific cache directory
.gradle
logback-sigar/native/
# Ignore Gradle build output directory
build
target/
site/
.idea
log/
out/
logback-example/log/*
.classpath
.project
.settings
.vscode/
bin/
*.iml
*.ipr
*.iws
================================================
FILE: .java-version
================================================
1.8
================================================
FILE: LICENSE
================================================
License
-------
Written in 2019 by Will Sargent will@tersesystems.com
To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty.
You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
================================================
FILE: README.md
================================================
[](https://search.maven.org/search?q=g:com.tersesystems.logback) [](https://tldrlegal.com/license/creative-commons-cc0-1.0-universal)
# Terse Logback
Terse Logback is a collection of [Logback](https://logback.qos.ch/) extensions that shows how to use [Logback](https://logback.qos.ch/manual/index.html) effectively.
Other logging projects you may be interested in:
* [Blacklite](https://github.com/tersesystems/blacklite/), an SQLite appender with memory-mapping and zstandard dictionary compression that clocks around 800K statements per second.
* [Blindsight](https://github.com/tersesystems/blindsight), a Scala logging API that extends SLF4J.
* [Echopraxia](https://github.com/tersesystems/echopraxia), a Java and Scala logging API built around structured logging.
## Documentation
Documentation is available at [https://tersesystems.github.io/terse-logback](https://tersesystems.github.io/terse-logback/1.0.3).
## Showcase
There is a showcase project at [https://github.com/tersesystems/terse-logback-showcase](https://github.com/tersesystems/terse-logback-showcase).
## Modules
- [Audio](https://tersesystems.github.io/terse-logback/guide/audio): Play audio when you log by attaching markers to your logging statements.
- [Budgeting / Rate Limiting](https://tersesystems.github.io/terse-logback/guide/budget): Limit the amount of debugging or tracing statements in a time period.
- [Censors](https://tersesystems.github.io/terse-logback/guide/censor): Censor sensitive information in logging statements.
- [Composite](https://tersesystems.github.io/terse-logback/guide/composite): Presents a single appender that composes several appenders.
- [Compression](https://tersesystems.github.io/terse-logback/guide/compression): Write to a compressed zstandard file.
- [Correlation Id](https://tersesystems.github.io/terse-logback/guide/correlationid): Adds markers and filters for correlation id.
- [Exception Mapping](https://tersesystems.github.io/terse-logback/guide/exception-mapping): Show the important details of an exception, including the root cause in a summary format.
- [Instrumentation](https://tersesystems.github.io/terse-logback/guide/instrumentation): Decorates any (including JVM) class with enter and exit logging statements at runtime.
- [JDBC](https://tersesystems.github.io/terse-logback/guide/jdbc): Use Postgres JSON to write structured logging to a single table.
- [JUL to SLF4J Bridge](https://tersesystems.github.io/terse-logback/guide/slf4jbridge): Configure java.util.logging to write to SLF4J with no [manual coding](https://mkyong.com/logging/how-to-load-logging-properties-for-java-util-logging/).
- [Relative Nanos](https://tersesystems.github.io/terse-logback/guide/relativens): Composes a logging event to contain relative nanoseconds based off `System.nanoTime`.
- [Select Appender](https://tersesystems.github.io/terse-logback/guide/select): Appender that selects an appender from a list based on key.
- [Tracing](https://tersesystems.github.io/terse-logback/guide/tracing): Sends logging events and traces to [Honeycomb Event API](https://docs.honeycomb.io/api/events/).
- [Typesafe Config](https://tersesystems.github.io/terse-logback/guide/typesafeconfig): Configure Logback properties using [HOCON](https://github.com/lightbend/config/blob/main/HOCON.md).
- [Turbo Markers](https://tersesystems.github.io/terse-logback/guide/turbomarker): [Turbo Filters](https://logback.qos.ch/manual/filters.html#TurboFilter) that depend on arbitrary deciders that can log at debug level for sessions.
- [Unique ID Appender](https://tersesystems.github.io/terse-logback/guide/uniqueid): Composes logging event to contain a unique id across multiple appenders.
================================================
FILE: RELEASING.md
================================================
## Release
To make sure everything works:
```bash
./gradlew clean build check
```
To format everything using [Spotless](https://github.com/diffplug/spotless/tree/master/plugin-gradle):
```bash
./gradlew spotlessApply
```
First, try publishing to maven local:
```bash
./gradlew publishToMavenLocal
```
If that works, then publish to Sonatype's staging repository and close:
```bash
./gradlew publishToSonatype closeSonatypeStagingRepository
```
Inspect this in Sonatype OHSSH repository. Delete the staging repository after inspection.
And then to promote it:
```bash
./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
```
If it looks weird that you have to specify "publishToSonatype" with another task, that's because [it is weird](https://github.com/gradle-nexus/publish-plugin/issues/19).
## Gradle Signing
If you run into errors with signing doing a `publishToSonaType`, this is common and underdocumented.
```
No value has been specified for property 'signatory.keyId'.
```
For the `signatory.keyId` error message, you need to set `signing.gnupg.keyName` if you
are using GPG 2.1 and a Yubikey 4.
https://docs.gradle.org/current/userguide/signing_plugin.html#sec:signatory_credentials
https://github.com/gradle/gradle/pull/1703/files#diff-6c52391bbdceb4cca64ce7b03e78212fR6
Note you need to use `gpg -K` and pick only the LAST EIGHT CHARS of the public signing key.
> signing.gnupg.keyName = 5F798D53
### PinEntry
Also note that if you are using a Yubikey, it'll require you to type in a PIN, which screws up Gradle.
```
gpg: signing failed: No pinentry
```
So you need to use pinentry-mode loopback, which is helpfully supplied by passphrase.
- https://github.com/sbt/sbt-pgp/pull/142
- https://wiki.archlinux.org/index.php/GnuPG#Unattended_passphrase
- https://github.com/gradle/gradle/pull/1703/files#diff-790036df959521791fdafe474b673924
You want this specified only the command line, i.e.
> $ HISTCONTROL=ignoreboth ./gradlew publishToMavenLocal -Psigning.gnupg.passphrase=$PGP_PASSPHRASE --info
### Cannot Allocate Memory
gpg can't be run in parallel. You'll get this error message.
```
gpg: signing failed: Cannot allocate memory
```
[Gradle is not smart enough to disable this](https://github.com/gradle/gradle/issues/12167).
Do not use `-Porg.gradle.parallel=false` and don't use `--parallel` when publishing.
## Documentation
Documentation is done with [gradle-mkdocs-plugin](https://xvik.github.io/gradle-mkdocs-plugin/) and works best on Linux.
Need to have [Python 3.8](https://tech.serhatteker.com/post/2019-12/upgrade-python38-on-ubuntu/), virtualenv is not enough.
To see documentation:
```bash
./gradlew mkdocsServe --no-daemon
```
To deploy documentation:
```bash
./gradlew mkdocsPublish
```
================================================
FILE: build.gradle
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
plugins {
id 'java'
id "com.github.hierynomus.license" version "0.15.0"
id "com.diffplug.spotless" version "6.11.0"
id 'ru.vyarus.use-python' version '3.0.0'
id 'ru.vyarus.mkdocs' version '2.4.0'
id "io.github.gradle-nexus.publish-plugin" version "1.1.0"
id "org.shipkit.shipkit-auto-version" version "1.1.19"
//id 'org.inferred.processors' version '2.3.0'
}
apply from: "gradle/release.gradle"
mkdocs {
sourcesDir = projectDir
strict = true
}
python {
minPythonVersion = '3.7'
// mkdocs requires 3.7.x
scope = VIRTUALENV
}
spotless {
freshmark {
target 'README.md'
propertiesFile('gradle.properties')
propertiesFile('version.properties')
}
java {
googleJavaFormat()
}
}
allprojects {
repositories {
mavenCentral()
}
}
// Root project shouldn't publish
tasks.withType(PublishToMavenRepository).configureEach { it.enabled = false }
subprojects { subproj ->
apply plugin: 'java'
apply plugin: 'com.diffplug.spotless'
spotless {
java {
googleJavaFormat()
}
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
}
dependencies {
testImplementation 'org.apiguardian:apiguardian-api:1.1.0'
testImplementation 'org.assertj:assertj-core:3.13.2'
testImplementation "junit:junit:$junitVersion"
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion"
testImplementation "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion"
testImplementation "org.junit.vintage:junit-vintage-engine:$junitVintageVersion"
//testImplementation group: 'org.junit.platform', name: 'junit-platform-runner', version: '1.5.0'
}
test {
useJUnitPlatform()
}
}
// Go through all the artifacts and find javadoc for it...
static List<String> javadocFromDependencies(Configuration config) {
List<String> javadocs = []
config.dependencies.each { dep ->
javadocs.add(artifactToJavadoc(dep.group, dep.name, dep.version))
}
javadocs
}
static String jvmToJavadoc(JavaVersion jvmVersion) {
if (jvmVersion.java8) {
'https://docs.oracle.com/javase/8/docs/api/'
} else if (jvmVersion.java9) {
'https://docs.oracle.com/javase/9/docs/api/'
}else if (jvmVersion.java10) {
'https://docs.oracle.com/javase/10/docs/api/'
}else if (jvmVersion.java11) {
'https://docs.oracle.com/en/java/javase/11/docs/api/'
} else {
'https://docs.oracle.com/javase/8/docs/api/'
}
}
static String artifactToJavadoc(String organization, String name, String apiVersion) {
String slashedOrg = organization.replace('.', '/')
"https://oss.sonatype.org/service/local/repositories/releases/archive/$slashedOrg/$name/$apiVersion/$name-$apiVersion-javadoc.jar/!/"
}
================================================
FILE: docs/guide/audio.md
================================================
# Audio
The audio appender uses a system beep configured through `SystemPlayer` to notify on warnings and errors, and limits excessive beeps with a budget evaluator.
## Installation
Add the library dependency using [com.tersesystems.logback:logback-audio](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-audio).
## Usage
The XML is as follows:
```xml
<included>
<appender name="AUDIO-WARN" class="com.tersesystems.logback.audio.AudioAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>NEUTRAL</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<player class="com.tersesystems.logback.audio.SystemPlayer"/>
</appender>
<appender name="AUDIO-ERROR" class="com.tersesystems.logback.audio.AudioAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<player class="com.tersesystems.logback.audio.SystemPlayer"/>
</appender>
<appender name="AUDIO" class="com.tersesystems.logback.core.CompositeAppender">
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator class="com.tersesystems.logback.budget.BudgetEvaluator">
<budgetRule name="WARN" threshold="1" interval="5" timeUnit="seconds"/>
<budgetRule name="ERROR" threshold="1" interval="5" timeUnit="seconds"/>
</evaluator>
<OnMismatch>DENY</OnMismatch>
<OnMatch>NEUTRAL</OnMatch>
</filter>
<appender-ref ref="AUDIO-WARN"/>
<appender-ref ref="AUDIO-ERROR"/>
</appender>
</included>
```
See [Application Logging in Java: Appenders](
https://tersesystems.com/blog/2019/05/27/application-logging-in-java-part-5/) for more details.
================================================
FILE: docs/guide/budget.md
================================================
# Budget Aware Logging
There are instances where logging may be overly chatty, and will log more than necessary.
Rather than hunt down all the individual loggers and whitelist or blacklist the lot of them, you can assign a
budget that will budget INFO messages to 5 statements a second.
This is easy to do with the `logback-budget` module, which uses an internal [circuit breaker](https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/concurrent/CircuitBreaker.html) to regulate the flow of messages.
## Installation
Add the library dependency using [com.tersesystems.logback:logback-budget](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-budget).
## Usage
The time unit corresponds to the text value of `java.util.concurrent.TimeUnit` i.e. `nanoseconds`, `microseconds`, `milliseconds`, `seconds`, `minutes`, `hours`, `days`, case-insensitive.
```xml
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator class="com.tersesystems.logback.budget.BudgetEvaluator">
<budgetRule>
<name>INFO</name>
<threshold>5</threshold>
<interval>1</interval>
<timeUnit>seconds</timeUnit>
</budgetRule>
</evaluator>
<OnMismatch>DENY</OnMismatch>
<OnMatch>NEUTRAL</OnMatch>
</filter>
<encoder>
<pattern>%-5relative %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="TRACE">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
```
## Turbo Filter
You can also apply the budget rule as a turbo filter if you want to have the rule apply across all appenders, using `com.tersesystems.logback.budget.BudgetTurboFilter`.
```xml
<configuration>
<turboFilter class="com.tersesystems.logback.budget.BudgetTurboFilter">
<budgetRule>
<name>INFO</name>
<threshold>5</threshold>
<interval>1</interval>
<timeUnit>second</timeUnit>
</budgetRule>
<OnMismatch>DENY</OnMismatch>
<OnMatch>NEUTRAL</OnMatch>
</turboFilter>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-5relative %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="TRACE">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
```
================================================
FILE: docs/guide/censor.md
================================================
# Censors
There may be sensitive information that you don't want to show up in the logs. You can get around this by passing your information through a censor. This is a custom bit of code written for Logback, but it's not too complex.
There are two rules and a converter that are used in Logback to define and reference censors: `CensorAction`, `CensorRefAction` and the `censor` converter.
## Installation
Add the library dependency using [com.tersesystems.logback:logback-censor](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-censor).
## Usage
```xml
<configuration>
<newRule pattern="*/censor"
actionClass="com.tersesystems.logback.censor.CensorAction"/>
<newRule pattern="*/censor-ref"
actionClass="com.tersesystems.logback.censor.CensorRefAction"/>
<conversionRule conversionWord="censor" converterClass="com.tersesystems.logback.censor.CensorConverter" />
<!-- ... -->
</configuration>
```
The `CensorAction` defines a censor that can be referred to by the `CensorRef` action and the `censor` conversionWord, using the censor name. The default implementation is the regex censor, which will look for a regular expression and replace it with the replacement text defined:
```xml
<configuration>
<censor name="censor-name1" class="com.tersesystems.logback.censor.RegexCensor">
<replacementText>[CENSORED BY CENSOR1]</replacementText>
<regex>hunter1</regex>
</censor>
<censor name="censor-name2" class="com.tersesystems.logback.censor.RegexCensor">
<replacementText>[CENSORED BY CENSOR2]</replacementText>
<regex>hunter2</regex>
</censor>
</configuration>
```
Once you have the censors defined, you can use the censor word by specifying the target as defined in the [pattern encoder format](https://logback.qos.ch/manual/layouts.html#conversionWord), and adding the name as the option list using curly braces, i.e. `%censor(%msg){censor-name1}`. If you don't define the censor, then the first available censor will be picked.
```xml
<configuration>
<appender name="TEST1" class="ch.qos.logback.core.FileAppender">
<file>file1.log</file>
<encoder>
<pattern>%censor(%msg){censor-name1}%n</pattern>
</encoder>
</appender>
<appender name="TEST2" class="ch.qos.logback.core.FileAppender">
<file>file2.log</file>
<encoder>
<pattern>%censor(%msg){censor-name2}%n</pattern>
</encoder>
</appender>
</configuration>
```
If you are working with a componentized framework, you'll want to use the `censor-ref` action instead. Here's an example using logstash-logback-encoder.
```xml
<configuration>
<appender name="TEST3" class="ch.qos.logback.core.FileAppender">
<file>file3.log</file>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<jsonGeneratorDecorator class="com.tersesystems.logback.censor.CensoringJsonGeneratorDecorator">
<censor-ref ref="first-censor"/>
<censor-ref ref="second-censor"/>
</jsonGeneratorDecorator>
</encoder>
</appender>
</configuration>
```
In this case, `CensoringJsonGeneratorDecorator` implements the `CensorAttachable` interface and so will run message text through the censor if it exists.
See [Application Logging in Java: Converters](https://tersesystems.com/blog/2019/05/11/application-logging-in-java-part-3/) for more details.
================================================
FILE: docs/guide/composite.md
================================================
# Composite Appender
The composite appender presents a single appender and appends to several appenders. It is very useful for referring to a list of appenders by a single name.
## Installation
Add the library dependency using [com.tersesystems.logback:logback-core](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-core).
## Usage
```xml
<configuration debug="true">
<appender name="CONSOLE" class="ch.qos.logback.core.read.ListAppender">
</appender>
<appender name="FILE" class="ch.qos.logback.core.read.ListAppender">
</appender>
<appender name="CONSOLE_AND_FILE" class="com.tersesystems.logback.core.CompositeAppender">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</appender>
<root level="TRACE">
<appender-ref ref="CONSOLE_AND_FILE"/>
</root>
</configuration>
```
You can leverage nesting to keep your filtering logic under control. For example, you may want to have several things happen when you hit an error in your logs. Appenders will always write when they receive an event, unless they are filtered.
Using nesting, you can declare the filter once, and have the child appenders "inherit" that filter:
<configuration>
<newRule pattern="*/player"
actionClass="com.tersesystems.logback.audio.PlayerAction"/>
<!-- Filter is on the appender chain -->
<appender name="ERROR-APPENDER" class="com.tersesystems.logback.CompositeAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<appender class="ch.qos.logback.core.FileAppender">
<file>error.log</file>
<encoder>
<pattern>%date - %message</pattern>
</encoder>
</appender>
<appender class="com.tersesystems.logback.audio.AudioAppender">
<player class="com.tersesystems.logback.audio.ResourcePlayer">
<resource>/error.ogg</resource>
</player>
</appender>
</appender>
<root level="TRACE">
<appender-ref ref="ALL-APPENDER"/>
<appender-ref ref="TRACE-APPENDER"/>
<appender-ref ref="DEBUG-APPENDER"/>
<appender-ref ref="INFO-APPENDER"/>
<appender-ref ref="WARN-APPENDER"/>
<appender-ref ref="ERROR-APPENDER"/>
</root>
</configuration>
This makes your appender logic much cleaner.
See [Application Logging in Java: Appenders](https://tersesystems.com/blog/2019/05/27/application-logging-in-java-part-5/) for more details.
================================================
FILE: docs/guide/compression.md
================================================
# Compression
Encoders are powerful and useful. They give you access to the raw bytes, and let you manipulate them before they get to an appender. But you'll have to put them together inside an appender if you want to do byte transformation.
## Installation
Add the library dependency using [com.tersesystems.logback:logback-compress-encoder](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-compress-encoder).
## Usage
As an example, say that we want to write out files directly in [zstandard](http://facebook.github.io/zstd/) or [brotli](https://en.wikipedia.org/wiki/Brotli) using Logback. The easiest way to do this is to provide a `FileAppender` with a swapped out compression encoder, while presenting a public API that looks just like a regular encoder.
Here's the appender as `logback.xml` sees it:
```xml
<appender name="COMPRESS_FILE" class="com.tersesystems.logback.compress.CompressingFileAppender">
<file>encoded.zst</file>
<compressAlgo>zstd</compressAlgo>
<bufferSize>1024000</bufferSize>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<charset>UTF-8</charset>
<pattern>%-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
```
Under the hood, `CompressingFileAppender` delegates to a regular file appender, but uses [commons-compress](https://commons.apache.org/proper/commons-compress/) and a `CompressingEncoder` to wrap `PatternLayoutEncoder`:
```java
public class CompressingFileAppender<E> extends UnsynchronizedAppenderBase<E> {
// ...
@Override
public void start() {
fileAppender = new FileAppender<>();
fileAppender.setContext(getContext());
fileAppender.setFile(getFile());
fileAppender.setImmediateFlush(false);
fileAppender.setPrudent(isPrudent());
fileAppender.setAppend(isAppend());
fileAppender.setName(name+"-embedded-file");
CompressingEncoder<E> compressedEncoder = createCompressingEncoder(getEncoder());
fileAppender.setEncoder(compressedEncoder);
fileAppender.start();
super.start();
}
public void stop() {
fileAppender.stop();
super.stop();
}
@Override
protected void append(E eventObject) {
fileAppender.doAppend(eventObject);
}
protected CompressingEncoder<E> createCompressingEncoder(Encoder<E> e) {
int bufferSize = getBufferSize();
String compressAlgo = getCompressAlgo();
CompressorStreamFactory factory = CompressorStreamFactory.getSingleton();
Set<String> names = factory.getOutputStreamCompressorNames();
if (names.contains(getCompressAlgo())) {
try {
return new CompressingEncoder<>(e, compressAlgo, factory, bufferSize);
} catch (CompressorException ex) {
throw new RuntimeException("Cannot create CompressingEncoder", ex);
}
} else {
throw new RuntimeException("No such compression algorithm: " + compressAlgo);
}
}
}
```
From there, the encoder will shove all the input bytes into a compressed stream until there's enough data to make compression worthwhile, and then flush the compressed bytes out through a byte array output stream:
```java
public class CompressingEncoder<E> extends EncoderBase<E> {
private final Accumulator accumulator;
private final Encoder<E> encoder;
public CompressingEncoder(Encoder<E> encoder,
String compressAlgo,
CompressorStreamFactory factory,
int bufferSize) throws CompressorException {
this.encoder = encoder;
this.accumulator = new Accumulator(compressAlgo, factory, bufferSize);
}
@Override
public byte[] headerBytes() {
try {
return accumulator.apply(encoder.headerBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public byte[] encode(E event) {
try {
return accumulator.apply(encoder.encode(event));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public byte[] footerBytes() {
try {
return accumulator.drain(encoder.footerBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
static class Accumulator {
private final ByteArrayOutputStream byteOutputStream;
private final CompressorOutputStream stream;
private final LongAdder count = new LongAdder();
private final int bufferSize;
public Accumulator(String compressAlgo,
CompressorStreamFactory factory,
int bufferSize) throws CompressorException {
this.bufferSize = bufferSize;
this.byteOutputStream = new ByteArrayOutputStream();
this.stream = factory.createCompressorOutputStream(compressAlgo, byteOutputStream);
}
boolean isFlushable() {
return count.intValue() >= bufferSize;
}
byte[] apply(byte[] bytes) throws IOException {
count.add(bytes.length);
stream.write(bytes);
if (isFlushable()) {
stream.flush();
byte[] output = byteOutputStream.toByteArray();
byteOutputStream.reset();
count.reset();
return output;
} else {
return new byte[0];
}
}
byte[] drain(byte[] inputBytes) throws IOException {
if (inputBytes != null) {
stream.write(inputBytes);
}
stream.close();
count.reset();
return byteOutputStream.toByteArray();
}
}
}
```
This keeps both `FileAppender` and `PatternLayoutEncoder` happy, while feeding compressed bytes as the stream. Using delegation is generally much easier than trying to extend from `FileAppender`, because `FileAppender` has very definite ideas about what kind of output stream it is using, and has all the logic of file rotation and backups encorporated into it, including its own gzip compression scheme for rotated files.
You can also extend this to add [dictionary support](https://facebook.github.io/zstd/#small-data) for ZStandard, and that would remove the need for a buffer to provide effective compression. This does come with the downside of needing to pass the dictionary out of band though.
See [Application Logging in Java: Encoders](https://tersesystems.com/blog/2019/06/09/application-logging-in-java-part-7/) for more details.
================================================
FILE: docs/guide/correlationid.md
================================================
# Correlation ID
The `logback-correlationid` module is a set of classes designed to encompass the idea of a correlation id in events.
## Installation
Add the library dependency using [com.tersesystems.logback:logback-correlationid](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-correlationid).
## Usage
It consists of a correlation id filter, a tap filter that always logs events with a correlation id to an appender, and a correlation id marker.
### Correlation ID Filter
A correlation id filter will filter for a correlation id set either as an MDC value, or as a marker created from `CorrelationIdMarker`.
```xml
<appender name="LIST" class="ch.qos.logback.core.read.ListAppender">
<filter class="com.tersesystems.logback.correlationid.CorrelationIdFilter">
<mdcKey>correlationId</mdcKey>
</filter>
</appender>
```
If an appender passes the filter, it will log the event.
```java
public class CorrelationIdFilterTest {
public void testFilter() {
// Write something that never gets logged explicitly...
Logger logger = loggerFactory.getLogger("com.example.Debug");
String correlationId = "12345";
CorrelationIdMarker correlationIdMarker = CorrelationIdMarker.create(correlationId);
// should be logged because marker
logger.info(correlationIdMarker, "info one");
logger.info("info two"); // should not be logged
// Everything below this point should be logged.
MDC.put("correlationId", correlationId);
logger.info("info three"); // should not be logged
logger.info(correlationIdMarker, "info four");
}
}
```
### CorrelationIdTapFilter
The `CorrelationIdTapFilter` is a turbofilter that always logs to a given appender if the correlation id appears, even if the appender is not configured for logging.
This functions as a <a href="https://www.enterpriseintegrationpatterns.com/patterns/messaging/WireTap.html">wiretap</a>.
Tap Filters are very useful as a way to send data to an appender. They completely bypass any kind of logging level configured on the front end, so you can set a logger to INFO level but still have access to all TRACE events when an error occurs, through the tap filter's appenders.
For example, a tap filter can automatically log everything with a correlation id at a TRACE level, without requiring filters or altering the log level as a whole. Let's run a simple HTTP client program that calls out to Google and prints a result.
```xml
<configuration>
<newRule pattern="configuration/turboFilter/appender-ref"
actionClass="ch.qos.logback.core.joran.action.AppenderRefAction"/>
<appender name="TAP_LIST" class="ch.qos.logback.core.read.ListAppender">
</appender>
<turboFilter class="com.tersesystems.logback.correlationid.CorrelationIdTapFilter">
<mdcKey>correlationId</mdcKey>
<appender-ref ref="TAP_LIST"/>
</turboFilter>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-5relative %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
</configuration>
```
### CorrelationIdMarker
A `CorrelationIdMarker` implements the `CorrelationIdProvider` interface to expose a marker which is known to contain a correlation id.
```java
CorrelationIdMarker correlationIdMarker = CorrelationIdMarker.create(correlationId);
String sameId = correlationIdMarker.getCorrelationId();
```
### CorrelationIdUtils
`CorrelationIdUtils` contains utility methods like `get` which retrieve a correlation id from either a marker or MDC.
================================================
FILE: docs/guide/exception-mapping.md
================================================
# Exception Mapping
Exception Mapping is done to show the important details of an exception, including the root cause in a summary format. This is especially useful in line oriented formats, because rendering a stacktrace can take up screen real estate without providing much value.
## Installation
Add the library dependency using [com.tersesystems.logback:logback-exception-mapping](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-exception-mapping) and [com.tersesystems.logback:logback-exception-mapping-providers](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-exception-mapping-providers).
## Usage
Given the following program:
```java
public class Thrower {
private static final Logger logger = LoggerFactory.getLogger(Thrower.class);
public static void main(String[] progArgs) {
try {
doSomethingExceptional();
} catch (RuntimeException e) {
logger.error("domain specific message", e);
}
}
static void doSomethingExceptional() {
Throwable cause = new BatchUpdateException();
throw new MyCustomException("This is my message", "one is one", "two is more than one", "three is more than two and one", cause);
}
}
public class MyCustomException extends RuntimeException {
public MyCustomException(String message, String one, String two, String three, Throwable cause) {
// ...
}
public String getOne() { return one; }
public String getTwo() { return two; }
public String getThree() { return three; }
}
```
and the Logback file:
```xml
<configuration>
<newRule pattern="*/exceptionMappings"
actionClass="com.tersesystems.logback.exceptionmapping.ExceptionMappingRegistryAction"/>
<newRule pattern="*/exceptionMappings/mapping"
actionClass="com.tersesystems.logback.exceptionmapping.ExceptionMappingAction"/>
<conversionRule conversionWord="richex" converterClass="com.tersesystems.logback.exceptionmapping.ExceptionMessageWithMappingsConverter" />
<exceptionMappings>
<!-- comes with default mappings for JDK exceptions, but you can add your own -->
<mapping name="com.tersesystems.logback.exceptionmapping.MyCustomException" properties="one,two,three"/>
</exceptionMappings>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-5relative %-5level %logger{35} - %msg%richex{1, 10, exception=[}%n</pattern>
</encoder>
</appender>
<root level="TRACE">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
```
Then this renders the following:
```
184 ERROR c.t.l.exceptionmapping.Thrower - domain specific message exception=[com.tersesystems.logback.exceptionmapping.MyCustomException(one="one is one" two="two is more than one" three="three is more than two and one" message="This is my message") > java.sql.BatchUpdateException(updateCounts="null" errorCode="0" SQLState="null" message="null")]
```
You can integrate exception mapping with Typesafe Config and `logstash-logback-encoder` by adding extra mappings.
For example, you can map a whole bunch of exceptions at once in HOCON, and not have to do it line by line in XML:
```xml
<configuration>
<newRule pattern="*/exceptionMappings/configMappings"
actionClass="com.tersesystems.logback.exceptionmapping.config.TypesafeConfigMappingsAction"/>
<exceptionMappings>
<!-- Or point to HOCON path -->
<configMappings path="exceptionmappings"/>
</exceptionMappings>
</configuration>
```
and
```hocon
exceptionmappings {
example.MySpecialException: ["timestamp"]
}
```
and configure it in JSON using `ExceptionArgumentsProvider`:
```xml
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<provider class="com.tersesystems.logback.exceptionmapping.json.ExceptionArgumentsProvider">
<fieldName>exception</fieldName>
</provider>
</providers>
</encoder>
```
and get the following `exception` that contains an array of exceptions and the associated properties, in this case `timestamp`:
```json
{
"id" : "Fa6x8H0EqomdHaINzdiAAA",
"sequence" : 3,
"@timestamp" : "2019-07-06T03:52:48.730+00:00",
"@version" : "1",
"message" : "I am an error",
"logger_name" : "example.Main$Runner",
"thread_name" : "pool-1-thread-1",
"level" : "ERROR",
"stack_hash" : "233f3cf1",
"exception" : [ {
"name" : "example.MySpecialException",
"properties" : {
"message" : "Level 1",
"timestamp" : "2019-07-06T03:52:48.728Z"
}
}, {
"name" : "example.MySpecialException",
"properties" : {
"message" : "Level 2",
"timestamp" : "2019-07-06T03:52:48.728Z"
}
}, {
"name" : "example.MySpecialException",
"properties" : {
"message" : "Level 3",
"timestamp" : "2019-07-06T03:52:48.728Z"
}
}, {
"name" : "example.MySpecialException",
"properties" : {
"message" : "Level 4",
"timestamp" : "2019-07-06T03:52:48.728Z"
}
}, {
"name" : "example.MySpecialException",
"properties" : {
"message" : "Level 5",
"timestamp" : "2019-07-06T03:52:48.728Z"
}
}, {
"name" : "example.MySpecialException",
"properties" : {
"message" : "Level 6",
"timestamp" : "2019-07-06T03:52:48.728Z"
}
}, {
"name" : "example.MySpecialException",
"properties" : {
"message" : "Level 7",
"timestamp" : "2019-07-06T03:52:48.728Z"
}
}, {
"name" : "example.MySpecialException",
"properties" : {
"message" : "Level 8",
"timestamp" : "2019-07-06T03:52:48.728Z"
}
}, {
"name" : "example.MySpecialException",
"properties" : {
"message" : "Level 9",
"timestamp" : "2019-07-06T03:52:48.728Z"
}
} ],
"stack_trace" : "<#1165e3b1> example.MySpecialException: Level 9\n\tat example.Main$Runner.nestException(Main.java:56)\n\t... 9 common frames omitted\nWrapped by: <#eb336a2d> example.MySpecialException: Level 8\n\tat example.Main$Runner.nestException(Main.java:56)\n\t... 10 common frames omitted\nWrapped by: <#cc1fb404> example.MySpecialException: Level 7\n\tat example.Main$Runner.nestException(Main.java:56)\n\t... 11 common frames omitted\nWrapped by: <#2af187a0> example.MySpecialException: Level 6\n\tat example.Main$Runner.nestException(Main.java:56)\n\t... 12 common frames omitted\nWrapped by: <#7dac62d1> example.MySpecialException: Level 5\n\tat example.Main$Runner.nestException(Main.java:56)\n\t... 13 common frames omitted\nWrapped by: <#2ea4460d> example.MySpecialException: Level 4\n\tat example.Main$Runner.nestException(Main.java:56)\n\t... 14 common frames omitted\nWrapped by: <#261bed64> example.MySpecialException: Level 3\n\tat example.Main$Runner.nestException(Main.java:56)\n\t... 15 common frames omitted\nWrapped by: <#e660d440> example.MySpecialException: Level 2\n\tat example.Main$Runner.nestException(Main.java:56)\n\t... 16 common frames omitted\nWrapped by: <#233f3cf1> example.MySpecialException: Level 1\n\tat example.Main$Runner.nestException(Main.java:56)\n\tat example.Main$Runner.nestException(Main.java:57)\n\tat example.Main$Runner.nestException(Main.java:57)\n\tat example.Main$Runner.nestException(Main.java:57)\n\tat example.Main$Runner.nestException(Main.java:57)\n\tat example.Main$Runner.nestException(Main.java:57)\n\tat example.Main$Runner.nestException(Main.java:57)\n\tat example.Main$Runner.nestException(Main.java:57)\n\tat example.Main$Runner.nestException(Main.java:57)\n\tat example.Main$Runner.generateException(Main.java:51)\n\tat example.Main$Runner.doError(Main.java:44)\n\tat java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)\n\tat java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)\n\tat java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)\n\tat java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)\n\tat java.lang.Thread.run(Thread.java:748)\n"
}
```
This is a lot easier for structured logging parsers to grok than the associated stacktrace.
See [How to Log an Exception](https://tersesystems.com/blog/2019/06/29/how-to-log-an-exception/) and [How to Log an Exception, Part 2](https://tersesystems.com/blog/2019/07/06/how-to-log-an-exception-part-2/) for more details.
================================================
FILE: docs/guide/instrumentation.md
================================================
# Instrumentation
If you have library code that doesn't pass around `ILoggerFactory` and doesn't let you add information to logging, then you can get around this by instrumenting the code with [Byte Buddy](https://bytebuddy.net/). Using Byte Buddy, you can do fun things like override `Security.setSystemManager` with [your own implementation](https://tersesystems.com/blog/2016/01/19/redefining-java-dot-lang-dot-system/), so using Byte Buddy to decorate code with `enter` and `exit` logging statements is relatively straightforward.
Instrumentation is configuration driven and simple. Instead of debugging using printf statements and recompiling or stepping through a debugger, you can just add lines to a config file.
I like this approach better than the annotation or aspect-oriented programming approaches, because it is completely transparent to the code and gives roughly the same performance as inline code, adding [130 ns/op](https://github.com/raphw/byte-buddy/issues/714) by calling `class.getMethod`.
A major advantage of instrumentation is that because it logs `throwing` exceptions in instrumented code, you can log exceptions that would be swallowed by the caller. For example, imagine that a library has the following method:
```java
public class Foo {
public void throwException() throws Exception {
throw new PlumException("I am sweet and cold");
}
public void swallowException() {
try {
throwException();
} catch (Exception e) {
// forgive me, the exception was delicious
}
}
}
```
By instrumenting the `throwException` method, you can see the logged exception at runtime when `swallowException` is called.
See [Application Logging in Java: Tracing 3rd Party Code](https://tersesystems.com/blog/2019/06/11/application-logging-in-java-part-8/) and [Hierarchical Instrumented Tracing with Logback](https://tersesystems.com/blog/2019/09/15/hierarchical-instrumented-tracing-with-logback/) for more details.
## Installation
You'll need to install [com.tersesystems.logback:logback-bytebuddy](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-bytebuddy) and [com.tersesystems.logback:logback-tracing](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-tracing).
You should also install [byte-buddy](https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy).
```
implementation group: 'com.tersesystems.logback', name: 'logback-classic', version: 'LATEST'
implementation group: 'com.tersesystems.logback', name: 'logback-bytebuddy', version: 'LATEST'
implementation group: 'com.tersesystems.logback', name: 'logback-tracing', version: 'LATEST'
implementation group: 'net.bytebuddy', name: 'byte-buddy', version: 'LATEST'
```
There are two ways you can install instrumentation -- you can do it using an agent, or you can do it manually.
> NOTE: Because Byte Buddy must inspect each class on JVM initialization, it will have a (generally small) impact on the start up time of your application.
### Agent Installation
Using the agent is generally easier (less code) and more powerful (can change JDK classes), but it does require some explicit command line options.
First, you set the java agent, either directly on the command line:
```bash
java \
-javaagent:path/to/logback-bytebuddy-x.x.x.jar=debug \
-Dterse.logback.configurationFile=conf/logback.conf \
-Dlogback.configurationFile=conf/logback-test.xml \
com.example.PreloadedInstrumentationExample
```
or by using the [`JAVA_TOOLS_OPTIONS` environment variable](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html).
```bash
export JAVA_TOOLS_OPTIONS="..."
```
Generally you'll be setting up these options in a build system. There are example projects in Gradle and sbt set up with agent-based instrumentation at [https://github.com/tersesystems/logging-instrumentation-example](https://github.com/tersesystems/logging-instrumentation-example).
### Manual Installation
You also have the option of installing the agent manually.
The in process instrumentation is done with `com.tersesystems.logback.bytebuddy.LoggingInstrumentationByteBuddyBuilder`, which takes in some configuration and then installs itself on the byte buddy agent.
```java
new LoggingInstrumentationByteBuddyBuilder()
.builderFromConfig(loggingInstrumentationAdviceConfig)
.with(debugListener)
.installOnByteBuddyAgent();
```
## Configuration
There are two parts to seeing tracing logs with instrumentation -- indicating the classes and methods you want instrumented, and then setting those loggers to TRACE.
### Setting Instrumented Classes and Methods
The instrumentation is configured using [HOCON](https://github.com/lightbend/config/blob/main/HOCON.md) in a `logback.conf` file in `src/main/resources`.
Settings are under the `logback.bytebuddy` section. The `tracing` section contains a mapping of class names and methods, or the wildcard "*" to indicate all methods.
```
logback.bytebuddy {
service-name = "my-service"
tracing {
"fully.qualified.class.Name" = ["method1", "method2"]
"play.api.mvc.ActionBuilder" = ["*"]
}
}
```
NOTE: There are some limitations to what you can trace. You can only instrument JDK classes when using the agent, and you cannot instrument native methods like `java.lang.System.currentTimeMillis()` for example.
### Setting Loggers to TRACE
Because instrumentation inserts `logger.trace` calls into the code, you must enable logging at `TRACE` level for those loggers to see output. Setting the level from `logback.xml` works fine:
```xml
<configuration>
<!-- ... -->
<logger name="fully.qualified.class.Name" level="TRACE"/>
<logger name="play.api.mvc.ActionBuilder" level="TRACE"/>
<!-- ... -->
</configuration>
```
If you are using the [Config](typesafeconfig.md) module, you can also do this from `logback.conf`:
```hocon
levels {
fully.qualified.class.Name = TRACE
play.api.mvc.ActionBuilder = TRACE
}
```
Or you can use `ChangeLogLevel` at run time.
## Examples
Instrumentation is a tool that can be hard to explain, so here's some use cases showing how you can quickly instrument your code.
Also don't forget the example projects at [https://github.com/tersesystems/logging-instrumentation-example](https://github.com/tersesystems/logging-instrumentation-example).
### Instrumenting java.lang.Thread
Assuming an agent based instrumentation, in `logback.conf`:
```hocon
levels {
java.lang.Thread = TRACE
}
logback.bytebuddy {
service-name = "some-service"
tracing {
"java.lang.Thread" = [
"run"
]
}
}
```
and the code as follows:
```java
public class PreloadedInstrumentationExample {
public static void main(String[] args) throws Exception {
Thread thread = Thread.currentThread();
thread.run();
}
}
```
yields
```text
[Byte Buddy] DISCOVERY java.lang.Thread [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.lang.Thread [null, null, loaded=true]
[Byte Buddy] COMPLETE java.lang.Thread [null, null, loaded=true]
92 TRACE java.lang.Thread - entering: java.lang.Thread.run() with arguments=[]
93 TRACE java.lang.Thread - exiting: java.lang.Thread.run() with arguments=[] => returnType=void
```
### Instrumenting javax.net.ssl.SSLContext
This is especially helpful when you're trying to debug SSL issues:
```hocon
levels {
sun.security.ssl = TRACE
javax.net.ssl = TRACE
}
logback.bytebuddy {
service-name = "some-service"
tracing {
"javax.net.ssl.SSLContext" = ["*"]
}
}
```
will result in:
```
FcJ3XfsdKnM6O0Qbm7EAAA 12:31:55.498 [TRACE] j.n.s.SSLContext - entering: javax.net.ssl.SSLContext.getInstance(java.lang.String) with arguments=[TLS] from source SSLContext.java:155
FcJ3XfsdKng6O0Qbm7EAAA 12:31:55.503 [TRACE] j.n.s.SSLContext - exiting: javax.net.ssl.SSLContext.getInstance(java.lang.String) with arguments=[TLS] => returnType=javax.net.ssl.SSLContext from source SSLContext.java:157
FcJ3XfsdKng6O0Qbm7EAAB 12:31:55.504 [TRACE] j.n.s.SSLContext - entering: javax.net.ssl.SSLContext.init([Ljavax.net.ssl.KeyManager;,[Ljavax.net.ssl.TrustManager;,java.security.SecureRandom) with arguments=[[org.postgresql.ssl.LazyKeyManager@27a97e08], [org.postgresql.ssl.NonValidatingFactory$NonValidatingTM@5918c260], null] from source SSLContext.java:282
FcJ3XfsdKnk6O0Qbm7EAAA 12:31:55.504 [TRACE] j.n.s.SSLContext - exiting: javax.net.ssl.SSLContext.init([Ljavax.net.ssl.KeyManager;,[Ljavax.net.ssl.TrustManager;,java.security.SecureRandom) with arguments=[[org.postgresql.ssl.LazyKeyManager@27a97e08], [org.postgresql.ssl.NonValidatingFactory$NonValidatingTM@5918c260], null] => returnType=void from source SSLContext.java:283
```
Be warned that JSSE can be extremely verbose in its `toString` output.
### Instrumenting ClassCalledByAgent
If you are already developing an agent, or want finer grained control over Byte Buddy, you can create the agent in process and inspect how Byte Buddy works. This is an advanced use case, but it's useful to get familiar.
With the following code:
```java
public class ClassCalledByAgent {
public void printStatement() {
System.out.println("I am a simple println method with no logging");
}
public void printArgument(String arg) {
System.out.println("I am a simple println, printing " + arg);
}
public void throwException(String arg) {
throw new RuntimeException("I'm a squirrel!");
}
}
```
And the following configuration in `logback.conf`:
```hocon
logback.bytebuddy {
service-name = "example-service"
tracing {
"com.tersesystems.logback.bytebuddy.ClassCalledByAgent" = [
"printStatement",
"printArgument",
"throwException",
]
}
}
```
and have `com.tersesystems.logback.bytebuddy.ClassCalledByAgent` logging level set to `TRACE` in `logback.xml`.
We can start up the agent, add in the builder and run through the methods:
```java
public class InProcessInstrumentationExample {
public static AgentBuilder.Listener createDebugListener(List<String> classNames) {
return new AgentBuilder.Listener.Filtering(
LoggingInstrumentationAdvice.stringMatcher(classNames),
AgentBuilder.Listener.StreamWriting.toSystemOut());
}
public static void main(String[] args) throws Exception {
// Helps if you install the byte buddy agents before anything else at all happens...
ByteBuddyAgent.install();
Logger logger = LoggerFactory.getLogger(InProcessInstrumentationExample.class);
SystemFlow.setLoggerResolver(new FixedLoggerResolver(logger));
Config config = LoggingInstrumentationAdvice.generateConfig(ClassLoader.getSystemClassLoader(), false);
LoggingInstrumentationAdviceConfig adviceConfig = LoggingInstrumentationAdvice.generateAdviceConfig(config);
// The debugging listener shows what classes are being picked up by the instrumentation
Listener debugListener = createDebugListener(adviceConfig.classNames());
new LoggingInstrumentationByteBuddyBuilder()
.builderFromConfig(adviceConfig)
.with(debugListener)
.installOnByteBuddyAgent();
// No code change necessary here, you can wrap completely in the agent...
ClassCalledByAgent classCalledByAgent = new ClassCalledByAgent();
classCalledByAgent.printStatement();
classCalledByAgent.printArgument("42");
try {
classCalledByAgent.throwException("hello world");
} catch (Exception e) {
// I am too lazy to catch this exception. I hope someone does it for me.
}
}
}
```
And get the following:
```text
[Byte Buddy] DISCOVERY com.tersesystems.logback.bytebuddy.ClassCalledByAgent [sun.misc.Launcher$AppClassLoader@75b84c92, null, loaded=true]
[Byte Buddy] TRANSFORM com.tersesystems.logback.bytebuddy.ClassCalledByAgent [sun.misc.Launcher$AppClassLoader@75b84c92, null, loaded=true]
[Byte Buddy] COMPLETE com.tersesystems.logback.bytebuddy.ClassCalledByAgent [sun.misc.Launcher$AppClassLoader@75b84c92, null, loaded=true]
524 TRACE c.t.l.b.InProcessInstrumentationExample - entering: com.tersesystems.logback.bytebuddy.ClassCalledByAgent.printStatement() with arguments=[] from source ClassCalledByAgent.java:18
I am a simple println method with no logging
529 TRACE c.t.l.b.InProcessInstrumentationExample - exiting: com.tersesystems.logback.bytebuddy.ClassCalledByAgent.printStatement() with arguments=[] => returnType=void from source ClassCalledByAgent.java:19
529 TRACE c.t.l.b.InProcessInstrumentationExample - entering: com.tersesystems.logback.bytebuddy.ClassCalledByAgent.printArgument(java.lang.String) with arguments=[42] from source ClassCalledByAgent.java:22
I am a simple println, printing 42
529 TRACE c.t.l.b.InProcessInstrumentationExample - exiting: com.tersesystems.logback.bytebuddy.ClassCalledByAgent.printArgument(java.lang.String) with arguments=[42] => returnType=void from source ClassCalledByAgent.java:23
529 TRACE c.t.l.b.InProcessInstrumentationExample - entering: com.tersesystems.logback.bytebuddy.ClassCalledByAgent.throwException(java.lang.String) with arguments=[hello world] from source ClassCalledByAgent.java:26
532 ERROR c.t.l.b.InProcessInstrumentationExample - throwing: com.tersesystems.logback.bytebuddy.ClassCalledByAgent.throwException(java.lang.String) with arguments=[hello world] ! thrown=java.lang.RuntimeException: I'm a squirrel!
java.lang.RuntimeException: I'm a squirrel!
at com.tersesystems.logback.bytebuddy.ClassCalledByAgent.throwException(ClassCalledByAgent.java:26)
at com.tersesystems.logback.bytebuddy.InProcessInstrumentationExample.main(InProcessInstrumentationExample.java:65)
```
The `[Byte Buddy]` statements up top are caused by the debug listener, and let you know that Byte Buddy has successfully instrumented the class. Note also that there is no runtime overhead in pulling line numbers or source files into the enter/exit methods, as these are pulled directly from bytecode and do not involve `fillInStackTrace`.
================================================
FILE: docs/guide/jdbc.md
================================================
# JDBC
There is a JDBC appender included which can be subclassed and extended as necessary in the `logback-jdbc-appender` module. Using a database for logging can be a big help when you just want to get at the logs of the last 30 seconds from inside the application. Because JDBC is both accessible and understandable, there's very little work required for querying.
Also consider using [Blacklite](https://github.com/tersesystems/blacklite/), an SQLite appender configured for low latency and high throughput.
Logback **does** have a native JDBC appender, but unfortunately it requires three tables and is not set up for easy subclassing. This one is better.
This implementation assumes a single table, with a user defined extensible schema, and is set up with [HikariCP](https://github.com/brettwooldridge/HikariCP) and a thread pool executor to serve JDBC with minimal blocking. Note that you should **always** use a JDBC appender behind an async appender like `LoggingEventAsyncDisruptorAppender` and you should have an [appropriately sized connection pool](https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing) for your database traffic.
Database timestamps record time with microsecond resolution, whereas millisecond resolution is commonplace for logging, so for convenience both the timestamp with time zone and the time since epoch are recorded. For span information, the start time must also be recorded as TSE. Likewise, the level is recorded as both a text string for visual reference, and a level value so that you can order and filter database queries.
Querying a database can be helpful when errors occur, because you can pull out all logs with a correlation id. See the [correlationid module](correlationid.md) for an example.
## Installation
Add the library dependency using [com.tersesystems.logback:logback-jdbc-appender](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-jdbc-appender).
## Usage
### Logging using in-memory H2 Database
Using an in memory H2 database is a cheap and easy way to expose logs from inside the application without having to parse files.
```xml
<appender name="H2_JDBC" class="com.tersesystems.logback.jdbc.JDBCAppender">
<driver>jdbc:h2:mem:logback</driver>
<url>org.h2.Driver</url>
<username>sa</username>
<password></password>
<createStatements>
CREATE TABLE IF NOT EXISTS events (
ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
ts TIMESTAMP(9) WITH TIME ZONE NOT NULL,
tse_ms numeric NOT NULL,
start_ms numeric NULL,
level_value int NOT NULL,
level VARCHAR(7) NOT NULL,
evt JSON NOT NULL
);
</createStatements>
<insertStatement>insert into events(ts, tse_ms, start_ms, level_value, level, evt) values(?, ?, ?, ?, ?, ?)</insertStatement>
<reaperStatement>delete from events where ts < ?</reaperStatement>
<reaperSchedule>PT30</reaperSchedule>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
</encoder>
</appender>
```
### Logging using PostgresSQL
If you want something larger scale, you'll probably be using Postgres instead of H2. You can log JSON to PostgreSQL, using the [built-in JSON datatype](https://www.postgresql.org/docs/current/functions-json.html). Postgres uses a custom JDBC type of `PGObject`, so the `insertEvent` method must be subclassed. This is what's in the `logback-postgresjson-appender` module:
```java
public class PostgresJsonAppender extends JDBCAppender {
private String objectType = "json";
public String getObjectType() {
return objectType;
}
public void setObjectType(String objectType) {
this.objectType = objectType;
}
@Override
public void start() {
super.start();
setDriver("org.postgresql.Driver");
}
@Override
protected void insertEvent(ILoggingEvent event, LongAdder adder, PreparedStatement statement)
throws SQLException {
PGobject jsonObject = new PGobject();
jsonObject.setType(getObjectType());
byte[] bytes = getEncoder().encode(event);
jsonObject.setValue(new String(bytes, StandardCharsets.UTF_8));
statement.setObject(adder.intValue(), jsonObject);
adder.increment();
}
}
```
First, install PostgreSQL, create a database `logback`, a role `logback` and a password `logback` and add the following table:
```sql
CREATE TABLE logging_table (
ID serial NOT NULL PRIMARY KEY,
ts TIMESTAMPTZ(6) NOT NULL,
tse_ms numeric NOT NULL,
start_ms numeric NULL,
level_value int NOT NULL,
level VARCHAR(7) NOT NULL,
evt jsonb NOT NULL
);
CREATE INDEX idxgin ON logging_table USING gin (evt);
```
Because logs are inherently time-series data, you can use the [timescaleDB postgresql extension](https://docs.timescale.com/latest/introduction) as described in [Store application logs in timescaleDB/postgres](https://www.komu.engineer/blogs/timescaledb/timescaledb-for-logs), but that's not required.
Then, add the following `logback.xml`:
```xml
<configuration>
<!-- async appender needs a shutdown hook to make sure this clears -->
<shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>
<conversionRule conversionWord="startTime" converterClass="com.tersesystems.logback.classic.StartTimeConverter" />
<!-- SQL is blocking, so use an async lmax appender here -->
<appender name="ASYNC_POSTGRES" class="net.logstash.logback.appender.LoggingEventAsyncDisruptorAppender">
<appender class="com.tersesystems.logback.postgresjson.PostgresJsonAppender">
<createStatements>
CREATE TABLE IF NOT EXISTS logging_table (
ID serial NOT NULL PRIMARY KEY,
ts TIMESTAMPTZ(6) NOT NULL,
tse_ms bigint NOT NULL,
start_ms bigint NULL,
level_value int NOT NULL,
level VARCHAR(7) NOT NULL,
evt jsonb NOT NULL
);
CREATE INDEX idxgin ON logging_table USING gin (evt);
</createStatements>
<!-- SQL statement takes a TIMESTAMP, LONG, INT, VARCHAR, PGObject -->
<insertStatement>insert into logging_table(ts, tse_ms, start_ms, level_value, level, evt) values(?, ?, ?, ?, ?, ?)</insertStatement>
<url>jdbc:postgresql://localhost:5432/logback</url>
<username>logback</username>
<password>logback</password>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<message/>
<loggerName/>
<threadName/>
<logLevel/>
<stackHash/>
<mdc/>
<logstashMarkers/>
<pattern>
<pattern>
{ "start_ms": "#asLong{%%startTime}" }
</pattern>
</pattern>
<arguments/>
<stackTrace>
<throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter">
<rootCauseFirst>true</rootCauseFirst>
</throwableConverter>
</stackTrace>
</providers>
</encoder>
</appender>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-5relative %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="ASYNC_POSTGRES"/>
<appender-ref ref="STDOUT"/>
</root>
</configuration>
```
[Querying](http://clarkdave.net/2013/06/what-can-you-do-with-postgresql-and-json/) requires a little bit of extra syntax, using `evt->'myfield'` to select:
```sql
select
ts as end_date,
start_ms as epoch_start,
tse_ms as epoch_end,
evt->'trace.span_id' as span_id,
evt->'name' as name,
evt->'message' as message,
evt->'trace.parent_id' as parent,
evt->'duration_ms' as duration_ms
from logging_table where evt->'trace.trace_id' IS NOT NULL order by ts desc limit 5
```
If you have extra logs that you want to import into PostgreSQL, you can [use PSQL to do that](https://stackoverflow.com/questions/39224382/how-can-i-import-a-json-file-into-postgresql/57445995#57445995).
### Extending JDBC Appender with extra fields
The JDBC appender can be extended so you can add extra information to the table.
In the `logback-correlationid` module, there's a `CorrelationIdJdbcAppender` that adds extra information into the event so you can query by the correlation id specifically, by using the `insertAdditionalData` hook:
```java
public class CorrelationIdJdbcAppender extends JDBCAppender {
private String mdcKey = "correlation_id";
public String getMdcKey() {
return mdcKey;
}
public void setMdcKey(String mdcKey) {
this.mdcKey = mdcKey;
}
protected CorrelationIdUtils utils;
@Override
public void start() {
super.start();
utils = new CorrelationIdUtils(mdcKey);
}
@Override
protected void insertAdditionalData(
ILoggingEvent event, LongAdder adder, PreparedStatement statement) throws SQLException {
insertCorrelationId(event, adder, statement);
}
private void insertCorrelationId(
ILoggingEvent event, LongAdder adder, PreparedStatement statement) throws SQLException {
Optional<String> maybeCorrelationId = utils.get(event.getMarker());
if (maybeCorrelationId.isPresent()) {
statement.setString(adder.intValue(), maybeCorrelationId.get());
} else {
statement.setNull(adder.intValue(), Types.VARCHAR);
}
adder.increment();
}
}
```
Then set up the table and add an index on the correlation id:
```sql
CREATE TABLE IF NOT EXISTS events (
ID NUMERIC NOT NULL PRIMARY KEY AUTO_INCREMENT,
ts TIMESTAMP(9) WITH TIME ZONE NOT NULL,
tse_ms numeric NOT NULL,
start_ms numeric NULL,
level_value int NOT NULL,
level VARCHAR(7) NOT NULL,
evt JSON NOT NULL,
correlation_id VARCHAR(255) NULL
);
CREATE INDEX correlation_id_idx ON events(correlation_id);
```
And then you can query from there.
See [Logging Structured Data to Database](https://tersesystems.com/blog/2019/09/18/logging-structured-data-to-database/) for more details.
================================================
FILE: docs/guide/relativens.md
================================================
# Relative Nanoseconds Appender
`LoggingEvent` already has a timestamp associated with it, but that timestamp is generated by `System.currentTimeMillis`. When your logging is fast enough that you can log several statements in the same millisecond, it can be frustrating to not know which came first. Adding a `relative_ns` field provides resolution down to the nanosecond, [sort of](https://shipilev.net/blog/2014/nanotrusting-nanotime/).
For example, here's two different records with the same millisecond.
```json
{"id":"FfwJtsNLLWo6O0Qbm7EAAA","relative_ns":11808036,"tse_ms":1584163603315,"start_ms":null,"@timestamp":"2020-03-14T05:26:43.315Z","@version":"1","message":"HikariPool-2 - Start completed.","logger_name":"com.zaxxer.hikari.HikariDataSource","thread_name":"play-dev-mode-akka.actor.default-dispatcher-7","level":"INFO","level_value":20000}
{"id":"FfwJtsNLLWo6O0Qbm7EAAB","relative_ns":11981656,"tse_ms":1584163603315,"start_ms":null,"@timestamp":"2020-03-14T05:26:43.315Z","@version":"1","message":"jdbc-appender-pool-1584163602961 - Start completed.","logger_name":"com.zaxxer.hikari.HikariDataSource","thread_name":"logback-appender-ASYNC_JDBC-2","level":"INFO","level_value":20000}
```
Note that the timestamp is `2020-03-14T05:26:43.315Z` and the time since epoch is `1584163603315`. The flake ids distinguish between log entries by using a counter when millisecond precision is exceeded, so the first record is `FfwJtsNLLWo6O0Qbm7EAAA` ending in `A` and the second record is `FfwJtsNLLWo6O0Qbm7EAAB` ending in `B`. The relative time tells us exactly how much time has elapsed between the two events: `11981656 - 11808036` is 0.17362 milliseconds.
All logging events are computed using `System.nanoTime - NanoTime.start`, where `NanoTime.start` is a static final field initialized JVM start time (technically at class loading but close enough). This value may be negative to begin with, but always increments.
See the [showcase](https://github.com/tersesystems/terse-logback-showcase) for an example.
## Installation
Add the library dependency using [com.tersesystems.logback:logback-classic](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-classic).
## Usage
```xml
<appender class="com.tersesystems.logback.classic.NanoTimeComponentAppender">
<appender ...>
</appender>
</appender>
```
You can extract the nanotime using a converter:
```xml
<!-- available as "%nanoTime" in a pattern layout -->
<conversionRule conversionWord="nanoTime"
converterClass="com.tersesystems.logback.classic.NanoTimeConverter" />
```
There are no configuration options.
================================================
FILE: docs/guide/select.md
================================================
# Select Appender
Different appenders are useful in different environments.
Development wants:
* Want colorized output on their consoles, with line oriented logs.
* Would also like to be able to read through logs with debug, info and warnings in them, to track control flow. If you have the logs seperated, that makes it harder.
* Generally don't want to run a local ELK stack or TCP appenders to see their logs.
Operations wants:
* Really want centralized logging, and a way to drill out on it. Structured logging especially.
* May want to have everything write to STDOUT, as is case for Docker / 12 Factor Apps.
* May have duplicate logs from the underlying architecture, that need to be dedupped.
* May not want redundant / repeated messages, which developers are not as sensitive to.
* Really hate getting paged with the same error repeatedly.
Logback is not aware of different environments. There's no out of the box way to say "in this environment I want these sets of appenders, but in this other environment I want these other sets of appenders."
## Installation
Add the library dependency using [com.tersesystems.logback:logback-core](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-core).
## Usage
The logback appenders under selection must have the name defined as an element, because Logback only looks for the name attribute at the top level, but otherwise they're the same. Here, we select the set of appenders we want based on the `LOGBACK_ENVIRONMENT` environment variable.
```xml
<configuration>
<appender name="SELECT" class="com.tersesystems.logback.core.SelectAppender">
<appenderKey>${LOGBACK_ENVIRONMENT}</appenderKey>
<appender class="com.tersesystems.logback.core.CompositeAppender">
<name>test</name>
<appender class="ch.qos.logback.core.read.ListAppender">
<name>test-list</name>
</appender>
</appender>
<appender class="com.tersesystems.logback.core.CompositeAppender">
<name>development</name>
<appender class="ch.qos.logback.core.ConsoleAppender">
<name>development-console</name>
<encoder>
<pattern>%-5relative %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
</appender>
<appender class="com.tersesystems.logback.core.CompositeAppender">
<name>staging</name>
<appender class="ch.qos.logback.core.ConsoleAppender">
<name>staging-console</name>
<encoder>
<pattern>%-5relative %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<appender class="ch.qos.logback.core.FileAppender">
<name>staging-file</name>
<file>file.log</file>
<encoder>
<pattern>%-5relative %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
</appender>
</appender>
<root level="TRACE">
<appender-ref ref="SELECT"/>
</root>
</configuration>
```
This is a much cleaner way to organize appenders than putting Janino logic into the configuration.
================================================
FILE: docs/guide/slf4jbridge.md
================================================
# JUL to SLF4J Bridge
It's easy to assume that all Java libraries will depend on SLF4J. But one of the oddities of Java logging is that there's a built-in logging framework called `java.util.logging` (JUL) which is rarely used but does appear in libraries such as [Guice](https://groups.google.com/g/google-guice/c/J2M64gM6Yao), [GRPC](https://github.com/grpc/grpc-java/issues/1577), and [Guava](https://github.com/google/guava/issues/829).
When errors happen in these frameworks, they may never show up in logging at all, because JUL will write out to standard output and standard error by default.
## Installation
Add the library dependency using [com.tersesystems.logback:logback-classic](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-classic).
## Usage
`SLF4JBridgeHandler` is a logging bridge, which is available in [jul-to-slf4j](http://www.slf4j.org/legacy.html#jul-to-slf4j). It does the job, but it does require some custom code to be added on startup to tell JUL that the handler is SLF4J:
```java
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
```
This isn't ideal, as it's very easy to miss that you have to add these lines of code. Some frameworks such as [Play Framework]() are [smart enough](https://github.com/playframework/playframework/blob/master/core/play-logback/src/main/scala/play/api/libs/logback/LogbackLoggerConfigurator.scala#L86) are smart enough to handle this for you, but there are cases where you're not using those frameworks, and we'd like JUL to just work.
This isn't so easy. JUL is very basic, and accepts configuration from system properties. The LogManager has two system properties:
- java.util.logging.config.class
- java.util.logging.config.file
If it doesn't find either, then it looks in `${java.home}/conf/logging.properties` if you're on JDK 11. There's no way to configure it from classpath, you have to do that [by hand](https://mkyong.com/logging/how-to-load-logging-properties-for-java-util-logging/).
There is discussion on [Stack Overflow](https://stackoverflow.com/a/11245040/5266) and the [SLF4J mailing list](https://www.mail-archive.com/slf4j-dev@qos.ch/msg00738.html) suggesting that JUL looks for `logging.properties` in the classpath. This is incorrect -- the only way you'll see `logging.properties` is from setting `java.util.logging.config.file` or if you're overwriting `${java.home}/logging.properties`. Here's the [source code](https://github.com/AdoptOpenJDK/openjdk-jdk/blob/master/src/java.logging/share/classes/java/util/logging/LogManager.java#L1347) so you can check for yourself.
However, since we're using Logback, we can leverage the fact that Logback searches through the classpath for `logback.xml`. All we need is a custom action to wrap `SLF4JBridgeHandler` and we can have a code free solution. This is what `SLF4JBridgeHandlerAction` does. You should also configure the `LevelChangePropagator`, to [reduce the impact of logging](http://logback.qos.ch/manual/configuration.html#LevelChangePropagator), and you must make sure that the `LoggerFactory` is called before any JUL dependent code.
You should set your `logback.xml` roughly as follows:
```xml
<configuration>
<!-- set up the rule -->
<newRule pattern="configuration/slf4jBridgeHandler"
actionClass="com.tersesystems.logback.classic.SLF4JBridgeHandlerAction"/>
<!-- calls removeHandlersForRootLogger / install -->
<slf4jBridgeHandler/>
<!-- reset all previous level configurations of all j.u.l. loggers -->
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
<resetJUL>true</resetJUL>
</contextListener>
<!-- Add Guice tracing -->
<logger name="com.google.inject" level="TRACE"/>
<root level="INFO">
<appender class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date{H:mm:ss.SSS} [%highlight(%-5level)] %logger - %message%ex%n</pattern>
</encoder>
</appender>
</root>
</configuration>
```
And then you should call `org.slf4j.LoggerFactory.getLogger` as a static final to prevent any initialization problems:
```java
package example;
import com.google.inject.*;
import org.slf4j.*;
public class App {
// Ensure that logback.xml is parsed by LoggerFactory _before_ Guice calls JUL.
private static final Logger logger = org.slf4j.LoggerFactory.getLogger(App.class);
public String getGreeting() {
return "Hello World!";
}
public static void main(String[] args) {
final Injector injector = Guice.createInjector();
final App instance = injector.getInstance(App.class);
logger.info(instance.getGreeting());
}
}
```
And that should render the following:
```
19:51:45.493 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Module execution: 64ms
19:51:45.494 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Interceptors creation: 2ms
19:51:45.496 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - TypeListeners & ProvisionListener creation: 1ms
19:51:45.511 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Scopes creation: 15ms
19:51:45.511 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Converters creation: 0ms
19:51:45.514 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Binding creation: 2ms
19:51:45.515 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Module annotated method scanners creation: 0ms
19:51:45.515 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Private environment creation: 0ms
19:51:45.515 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Injector construction: 0ms
19:51:45.515 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Binding initialization: 0ms
19:51:45.515 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Binding indexing: 0ms
19:51:45.515 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Collecting injection requests: 0ms
19:51:45.515 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Binding validation: 0ms
19:51:45.515 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Static validation: 0ms
19:51:45.515 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Instance member validation: 0ms
19:51:45.516 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Provider verification: 0ms
19:51:45.516 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Delayed Binding initialization: 0ms
19:51:45.516 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Static member injection: 0ms
19:51:45.516 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Instance injection: 0ms
19:51:45.516 [DEBUG] com.google.inject.internal.util.ContinuousStopwatch - Preloading singletons: 0ms
19:51:45.545 [INFO ] example.App - Hello World!
```
================================================
FILE: docs/guide/tracing.md
================================================
# Tracing to Honeycomb
You can connect Logback to Honeycomb directly through the Honeycomb Logback appender, using the [Events API](https://docs.honeycomb.io/api/events/). Posting data directly to Honeycomb lets you leverage Honeycomb's trace API to show logs as hierarchical traces and spans.
Bear in mind that the tracing feature here is optional -- you can use the Honeycomb appender out of the box without tracing with just plain logs.
However, adding tracing through logging is interesting in a couple of different ways. Using Honeycomb means logs can be immediately visualized and queried without setting up extensive infrastructure. From a tracing perspective, it completely avoids the OpenTelemetry manual instrumentation usually needed for tracing, and allows for tweaks and customization without the sampling or collector assumptions involved.
## Implementation
Add the library dependency using [com.tersesystems.logback:logback-honeycomb-okhttp](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-honeycomb-okhttp) for the honeycomb appender.
To set up tracing, add [com.tersesystems.logback:logback-tracing](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-tracing) and [com.tersesystems.logback:logback-classic](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-classic) for the start time converter.
## Usage
The appender is of type `com.tersesystems.logback.honeycomb.HoneycombAppender`, and makes use of the client under the hood. Because the honeycomb appender uses an HTTP client under the hood, there are a couple of important notes.
> **NOTE**: Because the HTTP client runs on a different thread, you should make sure you either shutdown Logback explicitly by calling `loggerContext.stop`, or use [shutdown hook](http://logback.qos.ch/manual/configuration.html#shutdownHook) configured so that shutting down can be delayed until the events are posted.
The appender is as follows:
```xml
<configuration>
<shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook">
<delay>1000</delay>
</shutdownHook>
<conversionRule conversionWord="startTime" converterClass="com.tersesystems.logback.classic.StartTimeConverter" />
<appender name="HONEYCOMB" class="com.tersesystems.logback.honeycomb.HoneycombAppender">
<apiKey>${HONEYCOMB_API_KEY}</apiKey>
<dataSet>terse-logback</dataSet>
<sampleRate>1</sampleRate>
<queueSize>10</queueSize>
<batch>true</batch>
<includeCallerData>false</includeCallerData>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<message/>
<loggerName/>
<threadName/>
<logLevel/>
<stackHash/>
<mdc/>
<logstashMarkers/>
<pattern>
<pattern>
{ "start_ms": "#asLong{%startTime}" }
</pattern>
</pattern>
<arguments/>
<stackTrace>
<throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter">
<rootCauseFirst>true</rootCauseFirst>
</throwableConverter>
</stackTrace>
</providers>
</encoder>
</appender>
<!--
don't send the logs from the http engine to the appender or you
may end up in a loop
-->
<logger name="okhttp" level="ERROR"/>
<root level="INFO">
<appender-ref ref="HONEYCOMB" />
</root>
</configuration>
```
You can also send tracing information to Honeycomb through SLF4J markers, using the `SpanMarkerFactory`. Underneath the hood, the SpanInfo puts together logstash markers according to [manual tracing](https://docs.honeycomb.io/working-with-your-data/tracing/send-trace-data/#manual-tracing).
The way this works in practice is that you start up a `SpanInfo` at the beginning of a request, and call `buildNow` to mark the start of the span. At the end of the operation, you log with a marker, by passing through the marker factory:
```java
SpanInfo spanInfo = builder.setRootSpan("index").buildNow();
// ...
logger.info(markerFactory.apply(spanInfo), "completed successfully!");
```
If you want to create a child span, you can do it from the parent using `withChild`:
```java
return spanInfo.withChild("doSomething", childInfo -> {
return doSomething(childInfo);
});
```
or asking for a child builder that you can build yourself:
```java
SpanInfo childInfo = spanInfo.childBuilder().setSpanName("doSomething").buildNow();
```
The start time information is captured in a `StartTimeMarker` which can be extracted by `StartTime.from` and is used in building the Honeycomb Request. The event timestamp serves as the span's end time. This is useful in Honeycomb Tracing, as the timestamp is the start time, not the time that the log entry was posted.
For example, in Play you might run a controller as follows:
```scala
import com.tersesystems.logback.tracing.SpanMarkerFactory
import com.tersesystems.logback.tracing.SpanInfo
import javax.inject._
import org.slf4j.LoggerFactory
import play.api.libs.concurrent.Futures
import play.api.mvc._
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}
import scala.concurrent.duration._
@Singleton
class HomeController @Inject()(cc: ControllerComponents, futures: Futures)
(implicit ec: ExecutionContext) extends AbstractController(cc) {
private val markerFactory = new SpanMarkerFactory()
private val logger = LoggerFactory.getLogger(getClass)
private def builder: SpanInfo.Builder = SpanInfo.builder().setServiceName("play_hello_world")
def index(): Action[AnyContent] = Action.async { implicit request: Request[AnyContent] =>
val spanInfo = builder.setRootSpan("index").buildNow()
val f: Future[Result] = spanInfo.withChild("renderPage", renderPage(_))
f.andThen {
case Success(_) =>
logger.info(markerFactory(spanInfo), "index completed successfully!")
case Failure(e) =>
logger.error(markerFactory(spanInfo), "index completed with error", e)
}
}
def renderPage(spanInfo: SpanInfo): Future[Result] = {
futures.delay(5.seconds).map { _ =>
Ok(views.html.index())
}.andThen {
case Success(_) =>
logger.info(markerFactory(spanInfo), "renderPage completed successfully!")
case Failure(e) =>
logger.error(markerFactory(spanInfo), "renderPage completed with error", e)
}
}
}
```
This generates a trace with a root span of "index", a child span of "renderPage" each with their own durations.
You can also send [span events](https://docs.honeycomb.io/working-with-your-data/tracing/send-trace-data/#span-events) and [span links](https://docs.honeycomb.io/working-with-your-data/tracing/send-trace-data/#links) using the `LinkMarkerFactory` and `EventMarkerFactory`, similar to the `SpanMarkerFactory`.
See [Tracing With Logback and Honeycomb](https://tersesystems.com/blog/2019/08/22/tracing-with-logback-and-honeycomb/) and [Hierarchical Instrumented Tracing with Logback](https://tersesystems.com/blog/2019/09/15/hierarchical-instrumented-tracing-with-logback/) for more details.
================================================
FILE: docs/guide/turbomarker.md
================================================
# Turbo Markers
[Turbo filters](https://logback.qos.ch/manual/filters.html#TurboFilter) are filters that decide whether a logging event should be created or not. They are not appender specific in the way that normal filters are, and so are used to override logger levels. However, there's a problem with the way that the turbo filter is set up: the two implementing classes are `ch.qos.logback.classic.turbo.MarkerFilter` and `ch.qos.logback.classic.turbo.MDCFilter`. The marker filter will always log if the given marker is applied, and the MDC filter relies on an attribute being populated in the MDC map.
What we'd really like to do is say "for this particular user, log everything he does at DEBUG level" and not have it rely on thread-local state at all, and carry out an arbitrary computation at call time. We can do this by adding a decider to a turbo filter, and adding "turbo markers."
## Installation
Add the library dependency using [com.tersesystems.logback:logback-turbomarker](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-turbomarker).
## Usage
We start by pulling the `decide` method to an interface, [`TurboFilterDecider`](https://github.com/tersesystems/terse-logback/blob/master/logback-classic/src/main/java/com/tersesystems/logback/classic/TurboFilterDecider.java):
```java
package com.tersesystems.logback.classic;
public interface TurboFilterDecider {
FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t);
}
```
And have the turbo filter [delegate to markers that implement the TurboFilterDecider interface](https://github.com/tersesystems/terse-logback/blob/master/logback-turbomarker/src/main/java/com/tersesystems/logback/turbomarker/TurboMarkerTurboFilter.java):
```java
package com.tersesystems.logback.turbomarker;
public class TurboMarkerTurboFilter extends TurboFilter {
@Override
public FilterReply decide(Marker rootMarker, Logger logger, Level level, String format, Object[] params, Throwable t) {
// ...
}
private FilterReply evaluateMarker(Marker marker, Marker rootMarker, Logger logger, Level level, String format, Object[] params, Throwable t) {
if (marker instanceof TurboFilterDecider) {
TurboFilterDecider decider = (TurboFilterDecider) marker;
return decider.decide(rootMarker, logger, level, format, params, t);
}
return FilterReply.NEUTRAL;
}
}
```
This gets us part of the way there. We can then set up a [`ContextAwareTurboFilterDecider`](https://github.com/tersesystems/terse-logback/blob/master/logback-turbomarker/src/main/java/com/tersesystems/logback/turbomarker/ContextAwareTurboFilterDecider.java), which does the same thing but assumes that you have a type `C` that is your external context.
```java
public interface ContextAwareTurboFilterDecider<C> {
FilterReply decide(ContextAwareTurboMarker<C> marker, C context, Marker rootMarker, Logger logger, Level level, String format, Object[] params, Throwable t);
}
```
Then we add a marker class that [incorporates that context in decision making](https://github.com/tersesystems/terse-logback/blob/master/logback-turbomarker/src/main/java/com/tersesystems/logback/turbomarker/ContextAwareTurboMarker.java):
```java
public class ContextAwareTurboMarker<C> extends TurboMarker implements TurboFilterDecider {
private final C context;
private final ContextAwareTurboFilterDecider<C> contextAwareDecider;
// ... initializers and such
@Override
public FilterReply decide(Marker rootMarker, Logger logger, Level level, String format, Object[] params, Throwable t) {
return contextAwareDecider.decide(this, context, rootMarker, logger, level, format, params, t);
}
}
```
This may look good in the abstract, but it may make more sense to see it in action. To do this, we'll set up an example application context:
```java
public class ApplicationContext {
private final String userId;
public ApplicationContext(String userId) {
this.userId = userId;
}
public String currentUserId() {
return userId;
}
}
```
and a factory that contains the decider:
```java
import com.tersesystems.logback.turbomarker.*;
public class UserMarkerFactory {
private final Set<String> userIdSet = new ConcurrentSkipListSet<>();
private final ContextDecider<ApplicationContext> decider = context ->
userIdSet.contains(context.currentUserId()) ? FilterReply.ACCEPT : FilterReply.NEUTRAL;
public void addUserId(String userId) {
userIdSet.add(userId);
}
public void clear() {
userIdSet.clear();
}
public UserMarker create(ApplicationContext applicationContext) {
return new UserMarker("userMarker", applicationContext, decider);
}
}
```
and a `UserMarker`, which is only around for the logging evaluation:
```java
public class UserMarker extends ContextAwareTurboMarker<ApplicationContext> {
public UserMarker(String name,
ApplicationContext applicationContext,
ContextAwareTurboFilterDecider<ApplicationContext> decider) {
super(name, applicationContext, decider);
}
}
```
and then we can set up logging that will only work for user "28":
```java
String userId = "28";
ApplicationContext applicationContext = new ApplicationContext(userId);
UserMarkerFactory userMarkerFactory = new UserMarkerFactory();
userMarkerFactory.addUserId(userId); // say we want logging events created for this user id
UserMarker userMarker = userMarkerFactory.create(applicationContext);
logger.info(userMarker, "Hello world, I am info and log for everyone");
logger.debug(userMarker, "Hello world, I am debug and only log for user 28");
```
This works especially well with a configuration management service like [Launch Darkly](https://docs.launchdarkly.com/docs/java-sdk-reference#section-variation), where you can [target particular users](https://docs.launchdarkly.com/docs/targeting-users#section-assigning-users-to-a-variation) and set up logging based on the user variation.
The LaunchDarkly blog has [best practices for operational flags](https://launchdarkly.com/blog/operational-flags-best-practices/):
> Verbose logs are great for debugging and troubleshooting but always running an application in debug mode is not viable. The amount of log data generated would be overwhelming. Changing logging levels on the fly typically requires changing a configuration file and restarting the application. A multivariate operational flag enables you to change the logging level from WARNING to DEBUG in real-time.
But we can give an example using the Java SDK. You can set up a factory like so:
```java
import ch.qos.logback.classic.Logger;
import ch.qos.logback.core.spi.FilterReply;
import com.launchdarkly.client.LDClientInterface;
import com.launchdarkly.client.LDUser;
public class LDMarkerFactory {
private final LaunchDarklyDecider decider;
public LDMarkerFactory(LDClientInterface client) {
this.decider = new LaunchDarklyDecider(requireNonNull(client));
}
public LDMarker create(String featureFlag, LDUser user) {
return new LDMarker(featureFlag, user, decider);
}
static class LaunchDarklyDecider implements MarkerContextDecider<LDUser> {
private final LDClientInterface ldClient;
LaunchDarklyDecider(LDClientInterface ldClient) {
this.ldClient = ldClient;
}
@Override
public FilterReply apply(ContextAwareTurboMarker<LDUser> marker, LDUser ldUser) {
return ldClient.boolVariation(marker.getName(), ldUser, false) ?
FilterReply.ACCEPT :
FilterReply.NEUTRAL;
}
}
public static class LDMarker extends ContextAwareTurboMarker<LDUser> {
LDMarker(String name, LDUser context, ContextAwareTurboFilterDecider<LDUser> decider) {
super(name, context, decider);
}
}
}
```
and then use the feature flag as the marker name and target the beta testers group:
```java
public class LDMarkerTest {
private static LDClientInterface client;
@BeforeAll
public static void setUp() {
client = new LDClient("sdk-key");
}
@AfterAll
public static void shutDown() {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void testMatchingMarker() throws JoranException {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
ch.qos.logback.classic.Logger logger = loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
LDMarkerFactory markerFactory = new LDMarkerFactory(client);
LDUser ldUser = new LDUser.Builder("UNIQUE IDENTIFIER")
.firstName("Bob")
.lastName("Loblaw")
.customString("groups", singletonList("beta_testers"))
.build();
LDMarkerFactory.LDMarker ldMarker = markerFactory.create("turbomarker", ldUser);
logger.info(ldMarker, "Hello world, I am info");
logger.debug(ldMarker, "Hello world, I am debug");
ListAppender<ILoggingEvent> appender = (ListAppender<ILoggingEvent>) logger.getAppender("LIST");
assertThat(appender.list.size()).isEqualTo(2);
appender.list.clear();
}
}
```
This is also a reason why [diagnostic logging is better than a debugger](https://lemire.me/blog/2016/06/21/i-do-not-use-a-debugger/). Debuggers are ephemeral, can't be used in production, and don't produce a consistent record of events: debugging log statements are the single best way to dump internal state and manage code flows in an application.
See [Targeted Diagnostic Logging in Production](https://tersesystems.com/blog/2019/07/22/targeted-diagnostic-logging-in-production/) for more details.
================================================
FILE: docs/guide/typesafeconfig.md
================================================
# Typesafe Config
The `TypesafeConfigAction` will search in a variety of places for configuration using [standard fallback behavior](https://github.com/lightbend/config#standard-behavior) for Typesafe Config, which gives a richer experience to end users.
## Installation
Add the library dependency using [com.tersesystems.logback:logback-typesafe-config](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-typesafe-config).
## Usage
The configuration is derived as follows:
```java
Config config = systemProperties // Look for a property from system properties first...
.withFallback(file) // if we don't find it, then look in an explicitly defined file...
.withFallback(testResources) // if not, then if logback-test.conf exists, look for it there...
.withFallback(resources) // then look in logback.conf...
.withFallback(reference) // and then finally in logback-reference.conf.
.resolve(); // Tell config that we want to use ${?ENV_VAR} type stuff.
```
The configuration is then placed in the `LoggerContext` which is available to all of Logback.
```java
lc.putObject(ConfigConstants.TYPESAFE_CONFIG_CTX_KEY, config);
```
And then all properties are made available to Logback, either at the `local` scope or at the `context` scope.
Properties must be strings, but you can also provide Maps and Lists to the Logback Context, through `context.getObject`.
### Log Levels and Properties through Typesafe Config
Configuration of properties and setting log levels is done through [Typesafe Config](https://github.com/lightbend/config#overview), using `TypesafeConfigAction`
Here's the `logback.conf` from the example application. It's in Human-Optimized Config Object Notation or [HOCON](https://github.com/lightbend/config/blob/master/HOCON.md).
```hocon
# Set logger levels here.
levels = {
# Override the default root log level with ROOT_LOG_LEVEL environment variable, if defined...
ROOT = ${?ROOT_LOG_LEVEL}
# You can set a logger with a simple package name.
example = DEBUG
# You can also do nested overrides here.
deeply.nested {
package = TRACE
}
}
# Overrides the properties from logback-reference.conf
local {
logback.environment=production
censor {
regex = """hunter2""" // http://bash.org/?244321
replacementText = "*******"
json.keys += "password" // adding password key will remove the key/value pair entirely
}
# Overwrite text file on every run.
textfile {
append = false
}
# Override the color code in console for info statements
highlight {
info = "black"
}
}
# You can also include settings from other places
include "myothersettings"
```
For tests, there's a `logback-test.conf` that will override (rather than completely replace) any settings that you have in `logback.conf`:
```hocon
include "logback-reference"
levels {
example = TRACE
}
local {
logback.environment=test
textfile {
location = "log/test/application-test.log"
append = false
}
jsonfile {
location = "log/test/application-test.json"
prettyprint = true
}
}
```
There is also a `logback-reference.conf` file that handles the default configuration for the appenders, and those settings can be overridden. They are written out individually in the encoder configuration so I won't go over it here.
Note that appender logic is not available here -- it's all defined through the `structured-config` in `logback.xml`.
Using Typesafe Config is not a requirement -- the point here is to show that there are more options to configuring Logback than using a straight XML file.
See [Application Logging in Java: Adding Configuration](https://tersesystems.com/blog/2019/05/05/application-logging-in-java-part-2/) for more details.
================================================
FILE: docs/guide/uniqueid.md
================================================
# Unique ID Appenders
The unique id appender allows the logging event to carry a unique id. When used in conjunction with `SelectAppender` or `CompositeAppender`, this allows for a log record to use the same id across different logs.
For example, in `application.log`, you'll see a single line that starts with `FfwJtsNHYSw6O0Qbm7EAAA`:
```text
FfwJtsNHYSw6O0Qbm7EAAA 2020-03-14T05:30:14.965+0000 [INFO ] play.api.db.HikariCPConnectionPool in play-dev-mode-akka.actor.default-dispatcher-7 - Creating Pool for datasource 'logging'
```
You can search for this string in `application.json` and see more detail on the log record:
```json
{"id":"FfwJtsNHYSw6O0Qbm7EAAA","relative_ns":20921024,"tse_ms":1584163814965,"start_ms":null,"@timestamp":"2020-03-14T05:30:14.965Z","@version":"1","message":"Creating Pool for datasource 'logging'","logger_name":"play.api.db.HikariCPConnectionPool","thread_name":"play-dev-mode-akka.actor.default-dispatcher-7","level":"INFO","level_value":20000}
```
See the [showcase](https://github.com/tersesystems/terse-logback-showcase) for an example.
## Installation
Add the library dependency using [com.tersesystems.logback:logback-uniqueid-appender](https://mvnrepository.com/artifact/com.tersesystems.logback/logback-uniqueid-appender).
## Usage
```xml
<appender name="selector-with-unique-id" class="com.tersesystems.logback.uniqueid.UniqueIdComponentAppender">
<appender ...>
</appender>
</appender>
```
To extract the unique ID, register a converter:
```xml
<!-- available as "%uniqueId" in a pattern layout -->
<conversionRule conversionWord="uniqueId" converterClass="com.tersesystems.logback.uniqueid.UniqueIdConverter" />
```
## ID Generators
Unique IDs come with several options. Flake ID is the default.
### Flake ID Generator
Flake IDs are decentralized and k-ordered, meaning that they are "roughly time-ordered when sorted lexicographically."
This implementation uses [idem](https://github.com/mguenther/idem) with `Flake128S`.
```xml
<appender name="selector-with-unique-id" class="com.tersesystems.logback.uniqueid.UniqueIdComponentAppender">
<idGenerator class="com.tersesystems.logback.uniqueid.FlakeIdGenerator"/>
<!-- ... -->
</appender>
```
### Random UUID Generator
Generates a Random UUIDv4 using a ThreadLocalRandom according to <a href="https://github.com/f4b6a3/uuid-creator">https://github.com/f4b6a3/uuid-creator</a>.
```xml
<appender name="selector-with-unique-id" class="com.tersesystems.logback.uniqueid.UniqueIdComponentAppender">
<idGenerator class="com.tersesystems.logback.uniqueid.RandomUUIDIdGenerator"/>
<!-- ... -->
</appender>
```
### TSID Generator
Generates a TSID according to <a href="https://github.com/f4b6a3/tsid-creator">https://github.com/f4b6a3/tsid-creator</a>.
**Highly recommended to set a *tsidcreator.node* system property in your application to configure the node id.**
```xml
<appender name="selector-with-unique-id" class="com.tersesystems.logback.uniqueid.UniqueIdComponentAppender">
<idGenerator class="com.tersesystems.logback.uniqueid.TsidIdgenerator"/>
<!-- ... -->
</appender>
```
### ULID Generator
Creates a monotonic ULID using a threadlocal random according to <a href="https://github.com/f4b6a3/ulid-creator">https://github.com/f4b6a3/ulid-creator</a>.
```xml
<appender name="selector-with-unique-id" class="com.tersesystems.logback.uniqueid.UniqueIdComponentAppender">
<idGenerator class="com.tersesystems.logback.uniqueid.UlidIdGenerator"/>
<!-- ... -->
</appender>
```
### KSU ID Generator
Creates a subsecond KSUID according to <a href="https://github.com/f4b6a3/ksuid-creator">https://github.com/f4b6a3/ksuid-creator</a>.
```xml
<appender name="selector-with-unique-id" class="com.tersesystems.logback.uniqueid.UniqueIdComponentAppender">
<idGenerator class="com.tersesystems.logback.uniqueid.KsuidSubsecondIdGenerator"/>
<!-- ... -->
</appender>
```
================================================
FILE: docs/index.md
================================================
# Terse Logback
Terse Logback is a collection of [Logback](https://logback.qos.ch/) modules that extend [Logback](https://logback.qos.ch/manual/index.html) functionality.
I've written about the reasoning and internal architecture in a series of blog posts. The [full list](https://tersesystems.com/category/logging/) is available on [https://tersesystems.com](https://tersesystems.com).
## Showcase
If you want to see a running application, there is a [showcase web application](https://github.com/tersesystems/terse-logback-showcase) that run out of the box that demonstrates some of the more advanced features, and shows you can integrate terse-logback with [Sentry](https://sentry.io) and [Honeycomb](https://www.honeycomb.io).
## Modules
- [Audio](guide/audio.md): Play audio when you log by attaching markers to your logging statements.
- [Budgeting / Rate Limiting](guide/budget.md): Limit the amount of debugging or tracing statements in a time period.
- [Censors](guide/censor.md): Censor sensitive information in logging statements.
- [Composite](guide/composite.md): Presents a single appender that composes several appenders.
- [Compression](guide/compression.md): Write to a compressed zstandard file.
- [Correlation Id](guide/correlationid.md): Adds markers and filters for correlation id.
- [Exception Mapping](guide/exception-mapping.md): Show the important details of an exception, including the root cause in a summary format.
- [Instrumentation](guide/instrumentation.md): Decorates any (including JVM) class with enter and exit logging statements at runtime.
- [JDBC](guide/jdbc.md): Use Postgres JSON to write structured logging to a single table.
- [JUL to SLF4J Bridge](guide/slf4jbridge.md): Configure java.util.logging to write to SLF4J with no [manual coding](https://mkyong.com/logging/how-to-load-logging-properties-for-java-util-logging/).
- [Relative Nanos](guide/relativens.md): Composes a logging event to contain relative nanoseconds based off `System.nanoTime`.
- [Select Appender](guide/select.md): Appender that selects an appender from a list based on key.
- [Tracing](guide/tracing.md): Sends logging events and traces to [Honeycomb Event API](https://docs.honeycomb.io/api/events/).
- [Typesafe Config](guide/typesafeconfig.md): Configure Logback properties using [HOCON](https://github.com/lightbend/config/blob/main/HOCON.md).
- [Turbo Markers](guide/turbomarker.md): [Turbo Filters](https://logback.qos.ch/manual/filters.html#TurboFilter) that depend on arbitrary deciders that can log at debug level for sessions.
- [Unique ID Appender](guide/uniqueid.md): Composes logging event to contain a unique id across multiple appenders.
================================================
FILE: docs/reading/reading.md
================================================
# Further Reading
Everything I write on logging is going to be here:
* [Terse Systems](https://tersesystems.com/category/logging/)
### Best Practices
Many of these are logback specific, but still good overall.
* [9 Logging Best Practices Based on Hands-on Experience](https://www.loomsystems.com/blog/single-post/2017/01/26/9-logging-best-practices-based-on-hands-on-experience)
* [Woofer: logging in (best) practices](https://orange-opensource.github.io/woofer/logging-code/): Spring Boot
* [A whole product concern logging implementation](http://stevetarver.github.io/2016/04/20/whole-product-logging.html)
* [There is more to logging than meets the eye](https://allegro.tech/2015/10/there-is-more-to-logging-than-meets-the-eye.html)
* [Monitoring demystified: A guide for logging, tracing, metrics](https://techbeacon.com/enterprise-it/monitoring-demystified-guide-logging-tracing-metrics)
* [Application-Level Logging Best Practices](https://news.ycombinator.com/item?id=19497788)
Stack Overflow has a couple of good tips on SLF4J and Logging:
* [When to use the different log levels](https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels)
* [Why does the TRACE level exist, and when should I use it rather than DEBUG?](https://softwareengineering.stackexchange.com/questions/279690/why-does-the-trace-level-exist-and-when-should-i-use-it-rather-than-debug)
* [Best practices for using Markers in SLF4J/Logback](https://stackoverflow.com/questions/4165558/best-practices-for-using-markers-in-slf4j-logback)
* [Stackoverflow: Logging best practices in multi-node environment](https://stackoverflow.com/questions/43496695/java-logging-best-practices-in-multi-node-environment)
#### Level Up Logs
[Alberto Navarro](https://looking4q.blogspot.com/) has a great series
<ol>
<li><a href="http://looking4q.blogspot.com/2018/09/level-up-logs-and-elk-introduction.html">Introduction</a> (Everyone)</li>
<li><a href="http://looking4q.blogspot.com/2018/09/level-up-your-logs-and-elk-json-logs.html">JSON as logs format</a> (Everyone)</li>
<li><b><a href="http://looking4q.blogspot.com/2018/09/level-up-logs-and-elk-logging-best.html">Logging best practices with Logback</a> (Targetting Java DEVs)</b></li>
<li><a href="https://looking4q.blogspot.com/2018/11/logging-cutting-edge-practices.html">Logging cutting-edge practices</a> (Targetting Java DEVs) </li>
<li><a href="https://looking4q.blogspot.com/2019/01/level-up-logs-and-elk-contract-first.html">Contract first log generator</a> (Targetting Java DEVs) </li>
<li><a href="http://looking4q.blogspot.com/2018/09/level-up-logs-and-elk-elasticsearch.html">ElasticSearch VRR Estimation Strategy</a> (Targetting OPS)</li>
<li><a href="http://looking4q.blogspot.com/2018/09/level-up-logs-and-elk-vrr-java-logback.html">VRR Java + Logback configuration</a> (Targetting OPS)</li>
<li><a href="http://looking4q.blogspot.com/2018/09/level-up-logs-and-elk-vrr-filebeat.html">VRR FileBeat configuration</a> (Targetting OPS)</li>
<li><a href="http://looking4q.blogspot.com/2018/09/level-up-logs-and-elk-vrr-logstash.html">VRR Logstash configuration and Index templates</a> (Targetting OPS)</li>
<li><a href="http://looking4q.blogspot.com/2018/09/level-up-logs-and-elk-vrr-curator.html">VRR Curator configuration</a> (Targetting OPS)</li>
<li><a href="https://looking4q.blogspot.com/2018/10/level-up-logs-and-elk-logstash-grok.html">Logstash Grok, JSON Filter and JSON Input performance comparison</a> (Targetting OPS) </li>
</ol>
#### Logging Anti Patterns
Logging Anti-Patterns by [Rolf Engelhard](https://rolf-engelhard.de/):
* [Logging Anti-Patterns](http://rolf-engelhard.de/2013/03/logging-anti-patterns-part-i/)
* [Logging Anti-Patterns, Part II](http://rolf-engelhard.de/2013/04/logging-anti-patterns-part-ii/)
* [Logging Anti-Patterns, Part III](https://rolf-engelhard.de/2013/10/logging-anti-patterns-part-iii/)
#### Clean Code, clean logs
[Tomasz Nurkiewicz](https://www.nurkiewicz.com/) has a great series on logging:
* [Clean code, clean logs: use appropriate tools (1/10)](https://www.nurkiewicz.com/2010/05/clean-code-clean-logs-use-appropriate.html)
* [Clean code, clean logs: logging levels are there for you (2/10)](https://www.nurkiewicz.com/2010/05/clean-code-clean-logs-tune-your-pattern.html)
* [Clean code, clean logs: do you know what you are logging? (3/10)](https://www.nurkiewicz.com/2010/05/clean-code-clean-logs-do-you-know-what.html)
* [Clean code, clean logs: avoid side effects (4/10)](https://www.nurkiewicz.com/2010/05/clean-code-clean-logs-avoid-side.html)
* [Clean code, clean logs: concise and descriptive (5/10)](https://www.nurkiewicz.com/2010/05/clean-code-clean-logs-concise-and.html)
* [Clean code, clean logs: tune your pattern (6/10)](https://www.nurkiewicz.com/2010/05/clean-code-clean-logs-tune-your-pattern.html)
* [Clean code, clean logs: log method arguments and return values (7/10)](https://www.nurkiewicz.com/2010/05/clean-code-clean-logs-log-method.html)
* [Clean code, clean logs: watch out for external systems (8/10)](https://www.nurkiewicz.com/2010/05/clean-code-clean-logs-watch-out-for.html)
* [Clean code, clean logs: log exceptions properly (9/10)](https://www.nurkiewicz.com/2010/05/clean-code-clean-logs-log-exceptions.html)
* [Clean code, clean logs: easy to read, easy to parse (10/10)](https://www.nurkiewicz.com/2010/05/clean-code-clean-logs-easy-to-read-easy.html)
* [Condensed 10 Tips on javacodegeeks](https://www.javacodegeeks.com/2011/01/10-tips-proper-application-logging.html)
### JSON Logging
* [Logging in JSON](http://www.asynchronous.org/blog/archives/2006/01/25/logging-in-json)
* [Write Logs for Machines, not Humans](https://paul.querna.org/articles/2011/12/26/log-for-machines-in-json/)
### Maple
* [Maple](https://github.com/Randgalt/maple)
### Eliot
* [Eliot](https://eliot.readthedocs.io/en/stable/quickstart.html)
* [Eliot Tree](https://github.com/jonathanj/eliottree)
### TreeLog
* [Treelog](https://github.com/lancewalton/treelog)
### Bunyan
Bunyan stands out for a number of innovations: ring buffers and JSON specifically.
* [Bunyan](https://timboudreau.com/blog/bunyan/read)
* [Comparison of Winston and Bunyan](https://strongloop.com/strongblog/compare-node-js-logging-winston-bunyan/)
* [Service logging in JSON with Bunyan](https://trentm.com/2012/03/service-logging-in-json-with-bunyan.html)
* [Bunyan Logging in Production at Joyent](https://trentm.com/talk-bunyan-in-prod/#/8)
### Timbre
* [Timbre](https://github.com/ptaoussanis/timbre/blob/master/README.md)
### Logback Encoders and Appenders
* [concurrent-build-logger](https://github.com/takari/concurrent-build-logger) (encoders and appenders both)
* [logzio-logback-appender](https://github.com/logzio/logzio-logback-appender)
* [logback-elasticsearch-appender](https://github.com/internetitem/logback-elasticsearch-appender)
* [logback-more-appenders](https://github.com/sndyuk/logback-more-appenders)
* [logback-steno](https://github.com/ArpNetworking/logback-steno)
* [logslack](https://github.com/gmethvin/logslack)
* [Lessons Learned Writing New Logback Appender](https://logz.io/blog/lessons-learned-writing-new-logback-appender/)
* [Extending logstash-logback-encoder](https://zenidas.wordpress.com/recipes/extending-logstash-logback-encoder/)
================================================
FILE: gradle/LICENSE_HEADER
================================================
SPDX-License-Identifier: CC0-1.0
Copyright ${copyrightYear} ${author}.
Licensed under the CC0 Public Domain Dedication;
You may obtain a copy of the License at
http://creativecommons.org/publicdomain/zero/1.0/
================================================
FILE: gradle/java-publication.gradle
================================================
//Auxiliary jar files required by Maven module publications
task sourcesJar(type: Jar, dependsOn: classes) {
archiveClassifier = 'sources'
from sourceSets.main.allSource
}
//TODO: java.withSourcesJar(), java.withJavadocJar()
task javadocJar(type: Jar, dependsOn: javadoc) {
archiveClassifier = 'javadoc'
from javadoc.destinationDir
}
apply plugin: 'maven-publish'
publishing { //https://docs.gradle.org/current/userguide/publishing_maven.html
publications {
maven(MavenPublication) { //name of the publication
from components.java
artifact sourcesJar
artifact javadocJar
pom {
name = tasks.jar.archiveBaseName
description = "Terse Logback is a collection of Logback extensions that shows how to use Logback effectively for structured logging, ringbuffer logging, system instrumentation, and JDBC."
url = "https://github.com/tersesystems/terse-logback"
licenses {
license {
name = 'Apache2'
url = 'https://github.com/tersesystems/terse-logback/blob/master/LICENSE'
}
}
developers {
developer {
id = 'tersesystems'
name = 'Terse Systems'
url = 'https://github.com/tersesystems'
}
}
scm {
url = 'https://github.com/tersesystems/terse-logback.git'
}
}
}
}
repositories {
// useful for testing - running "publish" will create artifacts/pom in a local dir
maven { url = "$rootDir/build/repo" }
}
}
apply plugin: 'signing'
signing { // https://docs.gradle.org/current/userguide/signing_plugin.html
// Give up on using PGP_KEY, leverage gpg-agent and release locally
useGpgCmd()
sign publishing.publications.maven
}
================================================
FILE: gradle/release.gradle
================================================
apply plugin: "io.github.gradle-nexus.publish-plugin" //https://github.com/gradle-nexus/publish-plugin/
nexusPublishing {
repositories {
if (System.getenv("SONATYPE_PWD")) {
sonatype {
username = System.getenv("SONATYPE_USER")
password = System.getenv("SONATYPE_PWD")
}
}
}
}
allprojects { p ->
plugins.withId("java") {
p.apply from: "$rootDir/gradle/java-publication.gradle"
}
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#
# SPDX-License-Identifier: CC0-1.0
#
# Copyright 2018-2020 Will Sargent.
#
# Licensed under the CC0 Public Domain Dedication;
# You may obtain a copy of the License at
#
# http://creativecommons.org/publicdomain/zero/1.0/
#
#Fri Mar 06 17:39:40 PST 2020
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
================================================
FILE: gradle.properties
================================================
#
# SPDX-License-Identifier: CC0-1.0
#
# Copyright 2018-2020 Will Sargent.
#
# Licensed under the CC0 Public Domain Dedication;
# You may obtain a copy of the License at
#
# http://creativecommons.org/publicdomain/zero/1.0/
#
group = com.tersesystems.logback
org.gradle.caching=true
# Set to true to attach a debugger
org.gradle.debug=false
# Set the memory size of the daemon to be higher because animalsniffer needs it.
#org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
bytebuddyVersion = 1.11.5
junitVersion = 4.12
junitJupiterVersion = 5.0.1
junitVintageVersion = 4.12.1
junitPlatformVersion = 1.0.1
slf4jVersion = 1.7.36
logstashVersion = 6.3
logbackVersion = 1.2.10
configVersion = 1.4.0
zstdVersion = 1.5.2-2
================================================
FILE: gradlew
================================================
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: logback-audio/gradle.properties
================================================
#
# SPDX-License-Identifier: CC0-1.0
#
# Copyright 2018-2020 Will Sargent.
#
# Licensed under the CC0 Public Domain Dedication;
# You may obtain a copy of the License at
#
# http://creativecommons.org/publicdomain/zero/1.0/
#
project_description = Logback Audio Markers
================================================
FILE: logback-audio/logback-audio.gradle
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
dependencies {
implementation project(':logback-core')
implementation project(':logback-classic')
implementation group: 'com.googlecode.soundlibs', name: 'mp3spi', version: '1.9.5.4'
implementation group: 'com.github.trilarion', name: 'vorbis-support', version: '1.1.0'
}
================================================
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/AudioAppender.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.audio;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
public class AudioAppender extends AppenderBase<ILoggingEvent> implements PlayerAttachable {
private Player player;
@Override
protected void append(ILoggingEvent eventObject) {
player.play();
}
@Override
public void addPlayer(Player player) {
this.player = player;
}
@Override
public void clearAllPlayers() {
this.player = null;
}
@Override
public void start() {
if (player == null) {
addError("No player found!");
} else {
super.start();
}
}
}
================================================
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/AudioMarker.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.audio;
import com.tersesystems.logback.classic.TerseBasicMarker;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Path;
public class AudioMarker extends TerseBasicMarker implements Player {
private static final String MARKER_NAME = "TS_AUDIO_MARKER";
private final Player player;
public AudioMarker(URL url) {
super(MARKER_NAME);
player = SimplePlayer.fromURL(url);
}
public AudioMarker(Path path) {
super(MARKER_NAME);
player = SimplePlayer.fromPath(path);
}
public AudioMarker(InputStream inputStream) {
super(MARKER_NAME);
player = SimplePlayer.fromInputStream(inputStream);
}
public AudioMarker(Player player) {
super(MARKER_NAME);
this.player = player;
}
public void play() {
player.play();
}
}
================================================
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/AudioMarkerAppender.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.audio;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
import java.util.Iterator;
import org.slf4j.Marker;
public class AudioMarkerAppender extends AppenderBase<ILoggingEvent> {
@Override
protected void append(ILoggingEvent eventObject) {
writePlayerMarkerIfNecessary(eventObject.getMarker());
}
private void writePlayerMarkerIfNecessary(Marker marker) {
if (marker != null) {
if (isPlayerMarker(marker)) {
((Player) marker).play();
}
if (marker.hasReferences()) {
for (Iterator<Marker> i = marker.iterator(); i.hasNext(); ) {
writePlayerMarkerIfNecessary(i.next());
}
}
}
}
private static boolean isPlayerMarker(Marker marker) {
return marker instanceof Player;
}
}
================================================
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/FilePlayer.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.audio;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.spi.LifeCycle;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class FilePlayer extends ContextAwareBase implements Player, LifeCycle {
private String file;
private Path path;
private volatile boolean started = false;
public FilePlayer() {}
public void setFile(String file) {
this.file = file;
}
@Override
public void play() {
SimplePlayer.fromPath(path).play();
}
@Override
public void start() {
path = Paths.get(file);
if (Files.exists(path)) {
started = true;
} else {
addError(String.format("Path %s does not exist!", path));
started = false;
}
}
@Override
public void stop() {
path = null;
started = false;
}
@Override
public boolean isStarted() {
return started;
}
}
================================================
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/PlayMethods.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.audio;
import java.io.IOException;
import java.util.function.Supplier;
import javax.sound.sampled.*;
public interface PlayMethods {
default void play(Supplier<AudioInputStream> supplier) {
// https://docs.oracle.com/javase/tutorial/sound/playing.html
try (final AudioInputStream in = supplier.get()) {
AudioFormat baseFormat = in.getFormat();
AudioFormat targetFormat =
new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
baseFormat.getSampleRate(),
16,
baseFormat.getChannels(),
baseFormat.getChannels() * 2,
baseFormat.getSampleRate(),
false);
try (final AudioInputStream dataIn = AudioSystem.getAudioInputStream(targetFormat, in)) {
DataLine.Info info = new DataLine.Info(Clip.class, targetFormat);
Clip clip = (Clip) AudioSystem.getLine(info);
if (clip != null) {
clip.addLineListener(
event -> {
if (event.getType() == LineEvent.Type.STOP) clip.close();
});
clip.open(dataIn);
clip.start();
}
}
} catch (LineUnavailableException | IOException e) {
throw new IllegalStateException(e);
}
}
}
================================================
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/Player.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.audio;
/** This can play audio sounds. */
public interface Player {
void play();
}
================================================
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/PlayerAction.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.audio;
import ch.qos.logback.core.Context;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.spi.ActionException;
import ch.qos.logback.core.joran.spi.InterpretationContext;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.spi.LifeCycle;
import ch.qos.logback.core.util.OptionHelper;
import org.xml.sax.Attributes;
public class PlayerAction extends Action {
Player player;
private boolean inError = false;
@Override
public void begin(InterpretationContext ic, String localName, Attributes attributes)
throws ActionException {
Object o = ic.peekObject();
if (!(o instanceof PlayerAttachable)) {
String errMsg =
"Could not find a PlayerAttachable at the top of execution stack. Near ["
+ localName
+ "] line "
+ getLineNumber(ic);
inError = true;
addInfo(errMsg); // This can trigger in an "if" block from janino, so it may not be serious...
return;
}
PlayerAttachable playerAttachable = (PlayerAttachable) o;
String className = attributes.getValue(CLASS_ATTRIBUTE);
if (OptionHelper.isEmpty(className)) {
addError("Missing class name for player. Near [" + localName + "] line " + getLineNumber(ic));
inError = true;
return;
}
try {
addInfo("About to instantiate player of type [" + className + "]");
player = (Player) OptionHelper.instantiateByClassName(className, Player.class, context);
Context icContext = ic.getContext();
if (player instanceof ContextAwareBase) {
((ContextAwareBase) player).setContext(icContext);
}
ic.pushObject(player);
} catch (Exception oops) {
inError = true;
addError("Could not create player.", oops);
throw new ActionException(oops);
}
playerAttachable.addPlayer(player);
}
@Override
public void end(InterpretationContext ic, String name) throws ActionException {
if (inError) {
return;
}
if (player instanceof LifeCycle) {
((LifeCycle) player).start();
}
Object o = ic.peekObject();
if (o != player) {
addWarn("The object at the end of the stack is not the player pushed earlier.");
} else {
ic.popObject();
}
}
}
================================================
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/PlayerAttachable.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.audio;
public interface PlayerAttachable {
void addPlayer(Player player);
void clearAllPlayers();
}
================================================
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/PlayerConverter.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.audio;
import ch.qos.logback.classic.pattern.ClassicConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import java.util.Iterator;
import org.slf4j.Marker;
public class PlayerConverter extends ClassicConverter {
@Override
public String convert(ILoggingEvent event) {
writePlayerMarkerIfNecessary(event.getMarker());
return null;
}
private void writePlayerMarkerIfNecessary(Marker marker) {
if (marker != null) {
if (isPlayerMarker(marker)) {
((Player) marker).play();
}
if (marker.hasReferences()) {
for (Iterator<Marker> i = marker.iterator(); i.hasNext(); ) {
writePlayerMarkerIfNecessary(i.next());
}
}
}
}
private static boolean isPlayerMarker(Marker marker) {
return marker instanceof Player;
}
}
================================================
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/PlayerException.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.audio;
public class PlayerException extends RuntimeException {
public PlayerException() {}
public PlayerException(String s) {
super(s);
}
public PlayerException(String s, Throwable throwable) {
super(s, throwable);
}
public PlayerException(Throwable throwable) {
super(throwable);
}
public PlayerException(String s, Throwable throwable, boolean b, boolean b1) {
super(s, throwable, b, b1);
}
}
================================================
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/ResourcePlayer.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.audio;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.spi.LifeCycle;
import java.net.URL;
public class ResourcePlayer extends ContextAwareBase implements Player, LifeCycle {
private String resource;
private URL resourceURL;
private volatile boolean started = false;
public ResourcePlayer() {}
public void setResource(String resource) {
this.resource = resource;
}
@Override
public void play() {
try {
SimplePlayer.fromURL(resourceURL).play();
} catch (Exception e) {
addError(String.format("Cannot play resource %s", resourceURL), e);
}
}
@Override
public void start() {
resourceURL = getClass().getResource(resource);
if (resourceURL != null) {
started = true;
} else {
addError(String.format("Resource %s does not exist!", resource));
started = false;
}
}
@Override
public void stop() {
resource = null;
started = false;
}
@Override
public boolean isStarted() {
return started;
}
}
================================================
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/SimplePlayer.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.audio;
import static javax.sound.sampled.AudioSystem.getAudioInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Path;
import java.util.function.Supplier;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.UnsupportedAudioFileException;
public class SimplePlayer implements PlayMethods, Player {
private final Supplier<AudioInputStream> supplier;
protected SimplePlayer(Supplier<AudioInputStream> supplier) {
this.supplier = supplier;
}
public static Player fromURL(URL url) {
return new SimplePlayer(
() -> {
try {
return getAudioInputStream(url);
} catch (UnsupportedAudioFileException | IOException e) {
throw new PlayerException(e);
}
});
}
public static Player fromPath(Path path) {
return new SimplePlayer(
() -> {
try {
return getAudioInputStream(path.toFile());
} catch (UnsupportedAudioFileException | IOException e) {
throw new PlayerException(e);
}
});
}
public static Player fromInputStream(InputStream inputStream) {
return new SimplePlayer(
() -> {
try {
return getAudioInputStream(inputStream);
} catch (UnsupportedAudioFileException | IOException e) {
throw new PlayerException(e);
}
});
}
@Override
public void play() {
play(supplier);
}
}
================================================
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/SystemPlayer.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.audio;
import java.awt.*;
public class SystemPlayer implements Player {
@Override
public void play() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
final Runnable exclam = (Runnable) toolkit.getDesktopProperty("win.sound.exclamation");
if (exclam != null) {
exclam.run();
} else {
toolkit.beep();
}
}
}
================================================
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/URLPlayer.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.audio;
import ch.qos.logback.core.spi.ContextAwareBase;
import java.net.URL;
public class URLPlayer extends ContextAwareBase implements Player {
private URL url;
public URLPlayer() {}
public void URLPlayer(URL url) {
this.url = url;
}
@Override
public void play() {
SimplePlayer.fromURL(url).play();
}
}
================================================
FILE: logback-audio/src/test/java/com/tersesystems/logback/audio/TestAudio.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.audio;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.Test;
import org.slf4j.Marker;
public class TestAudio {
@Test
public void testMarkerWithURL() throws JoranException, InterruptedException {
LoggerContext context = new LoggerContext();
URL resource = getClass().getResource("/logback-with-marker-appender.xml");
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(context);
configurator.doConfigure(resource);
Logger logger = context.getLogger("some.random.Logger");
URL audioURL = getClass().getResource("/bark.ogg");
Marker marker = new AudioMarker(audioURL);
logger.info(marker, "Bark!");
Thread.sleep(1000);
}
// Can't keep a path steady with different starting directories..
@Test
public void testMarkerWithURLWithConverter() throws JoranException, InterruptedException {
LoggerContext context = new LoggerContext();
URL resource = getClass().getResource("/logback-with-converter.xml");
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(context);
configurator.doConfigure(resource);
Logger logger = context.getLogger("some.random.Logger");
URL audioURL = getClass().getResource("/bark.ogg");
Marker marker = new AudioMarker(audioURL);
logger.info(marker, "Bark!");
Thread.sleep(1000);
}
// Can't keep a path steady with different starting directories..
// @Test
public void testMarkerWithPath() throws JoranException, InterruptedException {
LoggerContext context = new LoggerContext();
URL resource = getClass().getResource("/logback-with-marker-appender.xml");
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(context);
configurator.doConfigure(resource);
Logger logger = context.getLogger("some.random.Logger");
String path = System.getProperty("user.dir");
Path audioPath = Paths.get(path, "src", "test", "resources", "bark.ogg");
Marker marker = new AudioMarker(audioPath);
logger.warn(marker, "Bark!");
Thread.sleep(1000);
}
}
================================================
FILE: logback-audio/src/test/java/com/tersesystems/logback/audio/TestNested.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.audio;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import java.net.URL;
import org.junit.Test;
public class TestNested {
@Test
public void testLogger() throws JoranException, InterruptedException {
LoggerContext context = new LoggerContext();
URL resource = getClass().getResource("/logback-with-nested-appender.xml");
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(context);
configurator.doConfigure(resource);
Logger logger = context.getLogger("some.random.Logger");
for (int i = 0; i < 1; i++) {
logger.trace("TRACE");
}
for (int i = 0; i < 2; i++) {
logger.debug("DEBUG");
}
for (int i = 0; i < 2; i++) {
logger.info("INFO");
}
for (int i = 0; i < 2; i++) {
logger.warn("WARN");
}
logger.error("ERROR");
}
}
================================================
FILE: logback-audio/src/test/resources/logback-with-converter.xml
================================================
<!--
~ SPDX-License-Identifier: CC0-1.0
~
~ Copyright 2018-2020 Will Sargent.
~
~ Licensed under the CC0 Public Domain Dedication;
~ You may obtain a copy of the License at
~
~ http://creativecommons.org/publicdomain/zero/1.0/
-->
<configuration>
<conversionRule conversionWord="audio" converterClass="com.tersesystems.logback.audio.PlayerConverter" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-5relative %-5level %logger{35} %audio - %msg%n</pattern>
</encoder>
</appender>
<root level="TRACE">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
================================================
FILE: logback-audio/src/test/resources/logback-with-marker-appender.xml
================================================
<!--
~ SPDX-License-Identifier: CC0-1.0
~
~ Copyright 2018-2020 Will Sargent.
~
~ Licensed under the CC0 Public Domain Dedication;
~ You may obtain a copy of the License at
~
~ http://creativecommons.org/publicdomain/zero/1.0/
-->
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-5relative %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<appender name="AUDIO-MARKER" class="com.tersesystems.logback.audio.AudioMarkerAppender">
</appender>
<root level="TRACE">
<appender-ref ref="STDOUT"/>
<appender-ref ref="AUDIO-MARKER"/>
</root>
</configuration>
================================================
FILE: logback-audio/src/test/resources/logback-with-nested-appender.xml
================================================
<!--
~ SPDX-License-Identifier: CC0-1.0
~
~ Copyright 2018-2020 Will Sargent.
~
~ Licensed under the CC0 Public Domain Dedication;
~ You may obtain a copy of the License at
~
~ http://creativecommons.org/publicdomain/zero/1.0/
-->
<configuration>
<newRule pattern="*/player"
actionClass="com.tersesystems.logback.audio.PlayerAction"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-5relative %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<appender name="TRACE" class="com.tersesystems.logback.core.CompositeAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>TRACE</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<appender class="com.tersesystems.logback.audio.AudioAppender">
<player class="com.tersesystems.logback.audio.ResourcePlayer">
<resource>/bark.ogg</resource>
</player>
</appender>
</appender>
<appender name="DEBUG" class="com.tersesystems.logback.core.CompositeAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<appender class="com.tersesystems.logback.audio.AudioAppender">
<player class="com.tersesystems.logback.audio.ResourcePlayer">
<resource>/drip.ogg</resource>
</player>
</appender>
</appender>
<appender name="INFO" class="com.tersesystems.logback.core.CompositeAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<appender class="com.tersesystems.logback.audio.AudioAppender">
<player class="com.tersesystems.logback.audio.ResourcePlayer">
<resource>/glass.ogg</resource>
</player>
</appender>
</appender>
<appender name="WARN" class="com.tersesystems.logback.core.CompositeAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<appender class="com.tersesystems.logback.audio.AudioAppender">
<player class="com.tersesystems.logback.audio.ResourcePlayer">
<resource>/message.ogg</resource>
</player>
</appender>
</appender>
<appender name="ERROR" class="com.tersesystems.logback.core.CompositeAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<appender class="com.tersesystems.logback.audio.AudioAppender">
<player class="com.tersesystems.logback.audio.ResourcePlayer">
<resource>/sample.ogg</resource>
</player>
</appender>
</appender>
<root level="TRACE">
<appender-ref ref="ALL-APPENDER"/>
<appender-ref ref="TRACE-APPENDER"/>
<appender-ref ref="DEBUG-APPENDER"/>
<appender-ref ref="INFO-APPENDER"/>
<appender-ref ref="WARN-APPENDER"/>
<appender-ref ref="ERROR-APPENDER"/>
</root>
</configuration>
================================================
FILE: logback-budget/gradle.properties
================================================
#
# SPDX-License-Identifier: CC0-1.0
#
# Copyright 2018-2020 Will Sargent.
#
# Licensed under the CC0 Public Domain Dedication;
# You may obtain a copy of the License at
#
# http://creativecommons.org/publicdomain/zero/1.0/
#
project_description = Logback Budget
================================================
FILE: logback-budget/logback-budget.gradle
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
dependencies {
implementation project(':logback-core')
implementation project(':logback-classic')
// https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.9'
}
================================================
FILE: logback-budget/src/main/java/com/tersesystems/logback/budget/BudgetEvaluator.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.budget;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.boolex.EventEvaluatorBase;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.concurrent.CircuitBreaker;
import org.apache.commons.lang3.concurrent.EventCountCircuitBreaker;
/** Returns true if logging the event is within budget, false otherwise. */
public class BudgetEvaluator extends EventEvaluatorBase<ILoggingEvent>
implements BudgetRuleAttachable {
private List<BudgetRule> budgetRules = new ArrayList<>();
private Map<String, CircuitBreaker<Integer>> levelRules = new HashMap<>();
@Override
public void start() {
for (BudgetRule budgetRule : budgetRules) {
CircuitBreaker<Integer> breaker = createCircuitBreaker(budgetRule);
levelRules.put(budgetRule.getName(), breaker);
}
super.start();
}
private CircuitBreaker<Integer> createCircuitBreaker(BudgetRule budgetRule) {
addInfo("budgetRule = " + budgetRule);
final int threshold = budgetRule.getThreshold();
final long checkInterval = budgetRule.getInterval();
String timeUnit = budgetRule.getTimeUnit();
if (timeUnit == null) {
addError("No time unit found for budget rule");
throw new IllegalStateException("No time unit found for budget rule " + budgetRule);
} else {
TimeUnit checkUnit;
try {
checkUnit = TimeUnit.valueOf(timeUnit.toUpperCase());
} catch (IllegalArgumentException iae) {
try {
// Try adding an S on the end
checkUnit = TimeUnit.valueOf(timeUnit.toUpperCase() + "S");
} catch (Exception e) {
addError(
"Invalid time unit found for budget rule, use java.util.concurrent.TimeUnit enums "
+ budgetRule,
e);
throw e;
}
}
return new EventCountCircuitBreaker(threshold, checkInterval, checkUnit);
}
}
@Override
public boolean evaluate(ILoggingEvent event) {
if (levelRules.isEmpty()) {
return true; // not applicable
}
Level level = event.getLevel();
CircuitBreaker<Integer> breaker = levelRules.get(level.levelStr);
if (breaker == null) {
return true; // does not apply to this level
}
if (breaker.checkState()) {
return breaker.incrementAndCheckState(1);
} else {
return false;
}
}
@Override
public void addBudgetRule(BudgetRule budget) {
budgetRules.add(budget);
}
@Override
public void clearAllBudgetRules() {
budgetRules.clear();
}
}
================================================
FILE: logback-budget/src/main/java/com/tersesystems/logback/budget/BudgetRule.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.budget;
import org.apache.commons.lang3.builder.ToStringBuilder;
public class BudgetRule {
private String name;
private int threshold;
private long interval;
private String timeUnit;
public BudgetRule() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getThreshold() {
return threshold;
}
public void setThreshold(int threshold) {
this.threshold = threshold;
}
public long getInterval() {
return interval;
}
public void setInterval(long interval) {
this.interval = interval;
}
public String getTimeUnit() {
return timeUnit;
}
public void setTimeUnit(String timeUnit) {
this.timeUnit = timeUnit;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
================================================
FILE: logback-budget/src/main/java/com/tersesystems/logback/budget/BudgetRuleAction.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.budget;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.spi.ActionException;
import ch.qos.logback.core.joran.spi.InterpretationContext;
import ch.qos.logback.core.util.OptionHelper;
import org.xml.sax.Attributes;
public class BudgetRuleAction extends Action {
private BudgetRule budgetRule;
private boolean inError = false;
@Override
public void begin(InterpretationContext ic, String localName, Attributes attributes)
throws ActionException {
Object o = ic.peekObject();
if (!(o instanceof BudgetRuleAttachable)) {
String errMsg =
"Could not find a BudgetRuleAttachable at the top of execution stack. Near ["
+ localName
+ "] line "
+ getLineNumber(ic);
inError = true;
addInfo(errMsg); // This can trigger in an "if" block from janino, so it may not be serious...
return;
}
BudgetRuleAttachable budgetRuleAttachable = (BudgetRuleAttachable) o;
String className = attributes.getValue(CLASS_ATTRIBUTE);
if (OptionHelper.isEmpty(className)) {
className = BudgetRule.class.getName();
}
String name = attributes.getValue(NAME_ATTRIBUTE);
long interval = Long.parseLong(attributes.getValue("interval"));
int threshold = Integer.parseInt(attributes.getValue("threshold"));
String timeUnit = attributes.getValue("timeUnit");
try {
addInfo("About to instantiate budgetRule of type [" + className + "]");
budgetRule =
(BudgetRule) OptionHelper.instantiateByClassName(className, BudgetRule.class, context);
budgetRule.setName(name);
budgetRule.setInterval(interval);
budgetRule.setThreshold(threshold);
budgetRule.setTimeUnit(timeUnit);
ic.pushObject(budgetRule);
} catch (Exception oops) {
inError = true;
addError("Could not create budgetRule.", oops);
throw new ActionException(oops);
}
budgetRuleAttachable.addBudgetRule(budgetRule);
}
@Override
public void end(InterpretationContext ic, String name) throws ActionException {
if (inError) {
return;
}
Object o = ic.peekObject();
if (o != budgetRule) {
addWarn("The object at the end of the stack is not the budgetRule pushed earlier.");
} else {
ic.popObject();
}
}
}
================================================
FILE: logback-budget/src/main/java/com/tersesystems/logback/budget/BudgetRuleAttachable.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.budget;
public interface BudgetRuleAttachable {
void addBudgetRule(BudgetRule budget);
void clearAllBudgetRules();
}
================================================
FILE: logback-budget/src/main/java/com/tersesystems/logback/budget/BudgetTurboFilter.java
================================================
package com.tersesystems.logback.budget;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.turbo.TurboFilter;
import ch.qos.logback.core.spi.FilterReply;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.concurrent.CircuitBreaker;
import org.apache.commons.lang3.concurrent.EventCountCircuitBreaker;
import org.slf4j.Marker;
public class BudgetTurboFilter extends TurboFilter implements BudgetRuleAttachable {
private List<BudgetRule> budgetRules = new ArrayList<>();
private Map<String, CircuitBreaker<Integer>> levelRules = new HashMap<>();
public void start() {
for (BudgetRule budgetRule : budgetRules) {
CircuitBreaker<Integer> breaker = createCircuitBreaker(budgetRule);
levelRules.put(budgetRule.getName(), breaker);
}
super.start();
}
private CircuitBreaker<Integer> createCircuitBreaker(BudgetRule budgetRule) {
addInfo("budgetRule = " + budgetRule);
final int threshold = budgetRule.getThreshold();
final long checkInterval = budgetRule.getInterval();
String timeUnit = budgetRule.getTimeUnit();
if (timeUnit == null) {
addError("No time unit found for budget rule");
throw new IllegalStateException("No time unit found for budget rule " + budgetRule);
} else {
TimeUnit checkUnit;
try {
checkUnit = TimeUnit.valueOf(timeUnit.toUpperCase());
} catch (IllegalArgumentException iae) {
try {
// Try adding an S on the end
checkUnit = TimeUnit.valueOf(timeUnit.toUpperCase() + "S");
} catch (Exception e) {
addError(
"Invalid time unit found for budget rule, use java.util.concurrent.TimeUnit enums "
+ budgetRule,
e);
throw e;
}
}
return new EventCountCircuitBreaker(threshold, checkInterval, checkUnit);
}
}
@Override
public FilterReply decide(
Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) {
if (levelRules.isEmpty()) {
return getOnMatch(); // not applicable
}
CircuitBreaker<Integer> breaker = levelRules.get(level.levelStr);
if (breaker == null) {
return getOnMatch(); // does not apply to this level
}
if (breaker.checkState()) {
return breaker.incrementAndCheckState(1) ? getOnMatch() : getOnMismatch();
} else {
return getOnMismatch();
}
}
@Override
public void addBudgetRule(BudgetRule budget) {
budgetRules.add(budget);
}
@Override
public void clearAllBudgetRules() {
budgetRules.clear();
}
protected FilterReply onMatch = FilterReply.NEUTRAL;
protected FilterReply onMismatch = FilterReply.DENY;
public final void setOnMatch(FilterReply reply) {
this.onMatch = reply;
}
public final void setOnMismatch(FilterReply reply) {
this.onMismatch = reply;
}
public final FilterReply getOnMatch() {
return onMatch;
}
public final FilterReply getOnMismatch() {
return onMismatch;
}
}
================================================
FILE: logback-budget/src/test/java/com.tersesystems.logback.budget/BudgetEvaluatorTest.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.budget;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import java.net.URL;
import org.junit.Test;
public class BudgetEvaluatorTest {
@Test
public void testBudget() throws JoranException, InterruptedException {
LoggerContext context = new LoggerContext();
URL resource = getClass().getResource("/logback-budget.xml");
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(context);
configurator.doConfigure(resource);
Logger logger = context.getLogger("some.random.Logger");
for (int i = 0; i < 10; i++) {
logger.info("Hello world");
}
Thread.sleep(1000);
logger.info("Hello world");
}
}
================================================
FILE: logback-budget/src/test/java/com.tersesystems.logback.budget/BudgetTurboFilterTest.java
================================================
package com.tersesystems.logback.budget;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import java.net.URL;
import org.junit.Test;
public class BudgetTurboFilterTest {
@Test
public void testBudget() throws JoranException, InterruptedException {
LoggerContext context = new LoggerContext();
URL resource = getClass().getResource("/logback-turbofilter.xml");
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(context);
configurator.doConfigure(resource);
Logger logger = context.getLogger("some.random.Logger");
for (int i = 0; i < 10; i++) {
logger.info("Hello world");
}
Thread.sleep(1000);
logger.info("Hello world");
}
}
================================================
FILE: logback-budget/src/test/resources/logback-budget.xml
================================================
<!--
~ SPDX-License-Identifier: CC0-1.0
~
~ Copyright 2018-2020 Will Sargent.
~
~ Licensed under the CC0 Public Domain Dedication;
~ You may obtain a copy of the License at
~
~ http://creativecommons.org/publicdomain/zero/1.0/
-->
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator class="com.tersesystems.logback.budget.BudgetEvaluator">
<budgetRule>
<name>INFO</name>
<threshold>5</threshold>
<interval>1</interval>
<timeUnit>second</timeUnit>
</budgetRule>
</evaluator>
<OnMismatch>DENY</OnMismatch>
<OnMatch>NEUTRAL</OnMatch>
</filter>
<encoder>
<pattern>%-5relative %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="TRACE">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
================================================
FILE: logback-budget/src/test/resources/logback-turbofilter.xml
================================================
<!--
~ SPDX-License-Identifier: CC0-1.0
~
~ Copyright 2018-2020 Will Sargent.
~
~ Licensed under the CC0 Public Domain Dedication;
~ You may obtain a copy of the License at
~
~ http://creativecommons.org/publicdomain/zero/1.0/
-->
<configuration>
<turboFilter class="com.tersesystems.logback.budget.BudgetTurboFilter">
<budgetRule>
<name>INFO</name>
<threshold>5</threshold>
<interval>1</interval>
<timeUnit>second</timeUnit>
</budgetRule>
<OnMismatch>DENY</OnMismatch>
<OnMatch>NEUTRAL</OnMatch>
</turboFilter>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-5relative %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="TRACE">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
================================================
FILE: logback-bytebuddy/gradle.properties
================================================
#
# SPDX-License-Identifier: CC0-1.0
#
# Copyright 2018-2020 Will Sargent.
#
# Licensed under the CC0 Public Domain Dedication;
# You may obtain a copy of the License at
#
# http://creativecommons.org/publicdomain/zero/1.0/
#
project_description = Logback ByteBuddy Instrumentation
================================================
FILE: logback-bytebuddy/logback-bytebuddy.gradle
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
plugins {
id 'com.github.johnrengelman.shadow' version '5.1.0'
}
dependencies {
implementation "net.bytebuddy:byte-buddy:$bytebuddyVersion"
implementation "com.typesafe:config:${configVersion}"
// We cannot allow dependencies from logback-tracing in here.
shadow project(":logback-tracing")
shadow "org.slf4j:slf4j-api:$slf4jVersion"
shadow "ch.qos.logback:logback-classic:$logbackVersion"
shadow "ch.qos.logback:logback-core:$logbackVersion"
shadow "net.logstash.logback:logstash-logback-encoder:$logstashVersion"
shadow "com.google.auto.value:auto-value-annotations:1.6.2"
shadow group: 'com.fasterxml.uuid', name: 'java-uuid-generator', version: '3.2.0'
// You'll need this, but better to use the version already defined in project
testImplementation "net.bytebuddy:byte-buddy-agent:$bytebuddyVersion"
testImplementation "ch.qos.logback:logback-classic:$logbackVersion"
}
jar {
manifest {
attributes "Premain-Class": "com.tersesystems.logback.bytebuddy.LogbackInstrumentationAgent"
attributes "Agent-Class": "com.tersesystems.logback.bytebuddy.LogbackInstrumentationAgent"
attributes "Can-Redefine-Classes": "true"
attributes "Can-Retransform-Classes": "true"
}
}
shadowJar {
archiveFileName = "$archiveBaseName-$archiveVersion.$archiveExtension"
// Shade typesafe config and bytebuddy itself since this is used in bootstrap classloader
// and so would be visible to everything downstream
relocate 'com.typesafe', 'com.tersesystems.logback.shadow.typesafe'
relocate 'net.bytebuddy', 'com.tersesystems.logback.shadow.bytebuddy'
exclude '**/module-info.class'
dependencies {
exclude(dependency("com.fasterxml.jackson.core:"))
exclude(dependency("com.fasterxml.jackson.annotation:"))
exclude(dependency("com.fasterxml.jackson.databind:"))
exclude(dependency("net.logstash.logback:logstash-logback-encoder:$logstashVersion"))
exclude(dependency("org.slf4j:slf4j-api:$slf4jVersion"))
}
}
artifacts {
archives shadowJar
}
================================================
FILE: logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/AdviceConfig.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.bytebuddy;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.*;
import java.util.*;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
public class AdviceConfig {
private final ClassLoader classLoader;
private final Collection<TraceConfig> traceConfigCollection;
private final String serviceName;
private TraceConfig traceConfig;
public AdviceConfig(ClassLoader classLoader, String serviceName) {
this.classLoader = classLoader;
this.serviceName = serviceName;
traceConfigCollection = new HashSet<>();
}
public String getServiceName() {
return serviceName;
}
public void addTrace(String className, List<String> methodNames) throws Exception {
TraceConfig traceConfig = TraceConfig.create(classLoader, className, methodNames);
traceConfigCollection.add(traceConfig);
}
public void addTrace(String className) throws Exception {
TraceConfig traceConfig = TraceConfig.create(classLoader, className);
traceConfigCollection.add(traceConfig);
}
public List<String> classNames() {
return getTraceConfig().classNames;
}
public ElementMatcher<? super MethodDescription> methods() {
return getTraceConfig().methods();
}
public ElementMatcher<? super TypeDescription> types() {
return getTraceConfig().types();
}
private TraceConfig getTraceConfig() {
if (traceConfig == null) {
traceConfig = traceConfigCollection.stream().reduce(TraceConfig::join).get();
}
return traceConfig;
}
static final class TraceConfig {
private final List<String> classNames;
private final ElementMatcher.Junction<? super TypeDescription> typeMatcher;
private final ElementMatcher.Junction<? super MethodDescription> methodMatcher;
private TraceConfig(
List<String> classNames,
ElementMatcher.Junction<? super TypeDescription> typeMatcher,
ElementMatcher.Junction<? super MethodDescription> methodMatcher) {
this.typeMatcher = Objects.requireNonNull(typeMatcher);
this.methodMatcher = Objects.requireNonNull(methodMatcher);
this.classNames = classNames;
}
static TraceConfig create(ClassLoader classLoader, String className, List<String> methodNames)
throws Exception {
TypeDescription aClass = createTypeDescription(classLoader, className);
return new TraceConfig(
singletonList(className),
is(aClass),
methodNames.stream()
.map(m -> named(m).and(isDeclaredBy(aClass)).and(not(isNative().or(isConstructor()))))
.reduce(none(), ElementMatcher.Junction::or));
}
static TraceConfig create(ClassLoader classLoader, String className) throws Exception {
TypeDescription aClass = createTypeDescription(classLoader, className);
// Get a list of all non-native methods from the class, with no constructor.
MethodList<MethodDescription.InDefinedShape> methods =
aClass.getDeclaredMethods().filter(not(isNative().or(isConstructor())));
return new TraceConfig(singletonList(className), is(aClass), anyOf(methods));
}
public List<String> classNames() {
return this.classNames;
}
public ElementMatcher.Junction<? super MethodDescription> methods() {
return methodMatcher;
}
public ElementMatcher.Junction<? super TypeDescription> types() {
return typeMatcher;
}
public TraceConfig join(TraceConfig other) {
final ElementMatcher.Junction<? super MethodDescription> methodMatcher =
methods().or(other.methods());
final ElementMatcher.Junction<? super TypeDescription> typeMatcher =
types().or(other.types());
List<String> classNames = new ArrayList<>(classNames());
classNames.addAll(other.classNames());
return new TraceConfig(classNames, typeMatcher, methodMatcher);
}
private static TypeDescription createTypeDescription(ClassLoader classLoader, String className)
throws Exception {
return new TypeDescription.ForLoadedType(classLoader.loadClass(className));
}
}
@Override
public String toString() {
return "AdviceConfig{service-name = "
+ getServiceName()
+ ", methods="
+ methods()
+ ", types='"
+ types()
+ '\''
+ '}';
}
}
================================================
FILE: logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/LogbackInstrumentationAgent.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.bytebuddy;
import static java.util.Collections.singletonMap;
import static net.bytebuddy.dynamic.ClassFileLocator.ForClassLoader.read;
import static net.bytebuddy.dynamic.loading.ClassInjector.UsingInstrumentation.Target.BOOTSTRAP;
import java.io.File;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.nio.file.Files;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.loading.ClassInjector;
/**
* The agent class. This has the magic "premain" and "agentmain" methods for the Java
* Instrumentation API.
*/
public class LogbackInstrumentationAgent {
private static final Class<?> INSTRUMENTATION_ADVICE_CLASS = LoggingInstrumentationAdvice.class;
public static void premain(String arg, Instrumentation instrumentation) throws Exception {
injectBootstrapClasses(instrumentation);
LoggingInstrumentationAdvice logbackInst =
(LoggingInstrumentationAdvice) INSTRUMENTATION_ADVICE_CLASS.newInstance();
boolean debug = parseDebug(arg);
logbackInst.initialize(instrumentation, debug);
}
private static boolean parseDebug(String arg) {
return "debug".equalsIgnoreCase(arg);
}
public static void agentmain(String arg, Instrumentation instrumentation) throws Exception {
injectBootstrapClasses(instrumentation);
boolean debug = parseDebug(arg);
LoggingInstrumentationAdvice logbackInst =
(LoggingInstrumentationAdvice) INSTRUMENTATION_ADVICE_CLASS.newInstance();
logbackInst.initialize(instrumentation, debug);
}
/**
* Loads the advice class into the bootstrap target of instrumentation.
*
* @param instrumentation
* @throws IOException
*/
private static void injectBootstrapClasses(Instrumentation instrumentation) throws IOException {
File tempDir = Files.createTempDirectory("logback-bytebuddy").toFile();
tempDir.deleteOnExit();
// Inject the instrumentation advice class directly
byte[] classData = read(INSTRUMENTATION_ADVICE_CLASS);
TypeDescription typeDescription =
new TypeDescription.ForLoadedType(INSTRUMENTATION_ADVICE_CLASS);
ClassInjector classInjector =
ClassInjector.UsingInstrumentation.of(tempDir, BOOTSTRAP, instrumentation);
classInjector.inject(singletonMap(typeDescription, classData));
}
}
================================================
FILE: logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/LoggingInstrumentationAdvice.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.bytebuddy;
import com.tersesystems.logback.bytebuddy.impl.SystemFlow;
import com.typesafe.config.*;
import java.io.File;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.matcher.StringMatcher;
/** The code to be added on entry / exit to the methods under instrumentation. */
public class LoggingInstrumentationAdvice {
private static final String LOGBACK = "logback";
private static final String LOGBACK_TEST = "logback-test";
private static final String LOGBACK_REFERENCE_CONF = "logback-reference.conf";
private static final String CONFIG_FILE_PROPERTY = "terse.logback.configurationFile";
private static final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// We need to load the implementation of enter / exit methods from the system classloader,
// so that we don't end up hauling SLF4J impl factory into bootstrap classloader, which
// will hopelessly confuse the JVM.
public static Method enterMethod;
static {
try {
String className = "com.tersesystems.logback.bytebuddy.impl.Enter";
Class<?> enterClass = systemClassLoader.loadClass(className);
enterMethod = enterClass.getMethod("apply", String.class, Object[].class);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Method exitMethod;
static {
try {
String className = "com.tersesystems.logback.bytebuddy.impl.Exit";
Class<?> exitClass = systemClassLoader.loadClass(className);
exitMethod =
exitClass.getMethod("apply", String.class, Object[].class, Throwable.class, Object.class);
} catch (Exception e) {
e.printStackTrace();
}
}
void initialize(Instrumentation instrumentation, boolean debug) {
try {
Config config = generateConfig(systemClassLoader, debug);
AdviceConfig adviceConfig = generateAdviceConfig(systemClassLoader, config, debug);
if (debug) {
System.out.println("Generated Advice Config = " + adviceConfig);
}
SystemFlow.setServiceName(adviceConfig.getServiceName());
AgentBuilder agentBuilder =
new LoggingInstrumentationByteBuddyBuilder()
.builderFromConfigWithRetransformation(adviceConfig);
// The debugging listener shows what classes are being picked up by the instrumentation
if (debug) {
AgentBuilder.Listener debugListener = createDebugListener(adviceConfig.classNames());
agentBuilder = agentBuilder.with(debugListener);
}
agentBuilder.installOn(instrumentation);
} catch (Exception e) {
e.printStackTrace();
}
}
// The code here recapitulates the logback-config code, but in a bootstrap classloader.
// This does mean that typesafe-config classes are pulled from bootstrap thereafter, but
// this is pretty safe.
public static Config generateConfig(ClassLoader classLoader, boolean debug) {
// Look for logback.json, logback.conf, logback.properties
Config systemProperties = ConfigFactory.systemProperties();
String fileName = System.getProperty(CONFIG_FILE_PROPERTY);
Config file = ConfigFactory.empty();
if (fileName != null) {
file = ConfigFactory.parseFile(new File(fileName));
}
Config testResources = ConfigFactory.parseResourcesAnySyntax(classLoader, LOGBACK_TEST);
Config resources = ConfigFactory.parseResourcesAnySyntax(classLoader, LOGBACK);
Config reference = ConfigFactory.parseResources(classLoader, LOGBACK_REFERENCE_CONF);
Config config =
systemProperties // Look for a property from system properties first...
.withFallback(file) // if we don't find it, then look in an explicitly defined file...
.withFallback(
testResources) // if not, then if logback-test.conf exists, look for it there...
.withFallback(resources) // then look in logback.conf...
.withFallback(reference) // and then finally in logback-reference.conf.
.resolve(); // Tell config that we want to use ${?ENV_VAR} type stuff.
// Add a check to show the config value if nothing is working...
if (debug) {
String configString = config.root().render(ConfigRenderOptions.defaults());
System.out.println(configString);
}
return config;
}
public static AdviceConfig generateAdviceConfig(
ClassLoader classLoader, Config config, boolean debug) throws Exception {
List<AdviceConfig> configs = new ArrayList<>();
String serviceName = config.getString("logback.bytebuddy.service-name");
AdviceConfig adviceConfig = new AdviceConfig(classLoader, serviceName);
Set<Map.Entry<String, ConfigValue>> entries =
config.getConfig("logback.bytebuddy.tracing").entrySet();
for (Map.Entry<String, ConfigValue> entry : entries) {
String className = clean(entry.getKey());
ConfigValue value = entry.getValue();
if (value.valueType() == ConfigValueType.LIST) {
List<String> methodNames =
((List<String>) value.unwrapped())
.stream().map(LoggingInstrumentationAdvice::clean).collect(Collectors.toList());
if (methodNames.size() == 1 && Objects.equals(methodNames.get(0), "*")) {
if (debug) {
System.out.println("Using wildcard matching for class " + className);
}
adviceConfig.addTrace(className);
} else {
adviceConfig.addTrace(className, methodNames);
}
} else {
throw new IllegalStateException("unknown config!");
}
}
return adviceConfig;
}
private static String clean(String key) {
return key.replaceAll("\"", "").trim();
}
private static AgentBuilder.Listener createDebugListener(List<String> classNames) {
return new AgentBuilder.Listener.Filtering(
stringMatcher(classNames), AgentBuilder.Listener.StreamWriting.toSystemOut());
}
public static ElementMatcher.Junction<? super String> stringMatcher(
Collection<String> typeNames) {
boolean seen = false;
ElementMatcher.Junction<? super String> acc = ElementMatchers.none();
for (String typeName : typeNames) {
StringMatcher stringMatcher = new StringMatcher(typeName, StringMatcher.Mode.EQUALS_FULLY);
if (!seen) {
seen = true;
acc = stringMatcher;
} else {
acc = acc.or(stringMatcher);
}
}
return acc;
}
@Advice.OnMethodEnter
public static void enter(
@Advice.Origin("#t|#m|#d|#s") String origin, @Advice.AllArguments Object[] allArguments)
throws Exception {
enterMethod.invoke(null, origin, allArguments);
}
@Advice.OnMethodExit(onThrowable = Throwable.class)
public static void exit(
@Advice.Origin("#t|#m|#d|#s|#r") String origin,
@Advice.AllArguments Object[] allArguments,
@Advice.Thrown Throwable thrown,
@Advice.Return(typing = Assigner.Typing.DYNAMIC) Object returnValue)
throws Exception {
exitMethod.invoke(null, origin, allArguments, thrown, returnValue);
}
}
================================================
FILE: logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/LoggingInstrumentationByteBuddyBuilder.java
================================================
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright 2018-2020 Will Sargent.
*
* Licensed under the CC0 Public Domain Dedication;
* You may obtain a copy of the License at
*
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package com.tersesystems.logback.bytebuddy;
import static net.bytebuddy.matcher.ElementMatchers.*;
import static net.bytebuddy.matcher.ElementMatchers.any;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.field.FieldList;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.jar.asm.ClassVisitor;
import net.bytebuddy.jar.asm.Label;
import net.bytebuddy.jar.asm.MethodVisitor;
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.pool.TypePool;
/** Creates byte buddy agent builders with the LoggingInstrumentation advice. */
public class LoggingInstrumentationByteBuddyBuilder {
private static final MethodInfoLookup METHOD_INFO_LOOKUP = MethodInfoLookup.getInstance();
private static final Class<?> INSTRUMENTATION_ADVICE_CLASS = LoggingInstrumentationAdvice.class;
// Is there a better way to handle upgrades here?
// As far as I can tell, we just want the highest number possible.
// https://stackoverflow.com/questions/63399682/how-do-i-map-asms-api-version-in-opcodes-to-java-version
private static final int ASM_API = Opcodes.ASM9;
/**
* Creates a builder from the element matchers.
*
* @param typesMatcher an element matcher for types we should instrument.
* @param methodsMatcher an element matcher for the methods in the types that should be
* instrumented.
* @return the agent builder
*/
public AgentBuilder builderFromConfig(
ElementMatcher<? super TypeDescription> typesMatcher,
ElementMatcher<? super MethodDescription> methodsMatcher) {
return new AgentBuilder.Default()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
.with(AgentBuilder.TypeStrategy.Default.REDEFINE)
.disableClassFormatChanges() // frozen instrumented types
.type(typesMatcher) // for these classes...
.transform(
(builder, type, classLoader, module) -> {
// ...apply this advice to these methods.
Advice to = Advice.to(INSTRUMENTATION_ADVICE_CLASS);
AsmVisitorWrapper on = to.on(methodsMatcher);
AsmVisitorWrapper lineWrapper = new LineWrapper();
return builder.visit(lineWrapper).visit(on);
});
}
static class LineWrapper extends AsmVisitorWrapper.AbstractBase {
@Override
public ClassVisitor wrap(
TypeDescription instrumentedType,
ClassVisitor classVisitor,
Implementation.Context implementationContext,
TypePool typePool,
FieldList<FieldDescription.InDefinedShape> fields,
MethodList<?> methods,
int writerFlags,
int readerFlags) {
return new ClassVisitor(ASM_API, classVisitor) {
private String className;
private String source;
@Override
public void visitSource(String source, String debug) {
this.source = source;
super.visitSource(source, debug);
}
@Override
public void visit(
int version,
int access,
String name,
String signature,
String superName,
String[] interfaces) {
this.className = name != null ? name.replace('/', '.') : null;
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public MethodVisitor visitMethod(int access, String n, String d, String s, String[] e) {
MethodVisitor methodVisitor = super.visitMethod(access, n, d, s, e);
return new MethodVisitor(Opcodes.ASM5, methodVisitor) {
int line;
final MethodInfo methodInfo = new MethodInfo(n, d, source);
boolean isStart = false;
@Override
public void visitCode() {
isStart = true;
super.visitCode();
}
@Override
public void visitLineNumber(int line, Label start) {
if (isStart) {
methodInfo.setStartLine(line);
isStart = false;
}
this.line = line;
super.visitLineNumber(line, start);
}
@Override
public void visitEnd() {
methodInfo.setEndLine(line);
METHOD_INFO_LOOKUP.add(className, methodInfo);
super.
gitextract_k2vdfda5/ ├── .github/ │ └── ISSUE_TEMPLATE/ │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .java-version ├── LICENSE ├── README.md ├── RELEASING.md ├── build.gradle ├── docs/ │ ├── guide/ │ │ ├── audio.md │ │ ├── budget.md │ │ ├── censor.md │ │ ├── composite.md │ │ ├── compression.md │ │ ├── correlationid.md │ │ ├── exception-mapping.md │ │ ├── instrumentation.md │ │ ├── jdbc.md │ │ ├── relativens.md │ │ ├── select.md │ │ ├── slf4jbridge.md │ │ ├── tracing.md │ │ ├── turbomarker.md │ │ ├── typesafeconfig.md │ │ └── uniqueid.md │ ├── index.md │ └── reading/ │ └── reading.md ├── gradle/ │ ├── LICENSE_HEADER │ ├── java-publication.gradle │ ├── release.gradle │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── logback-audio/ │ ├── gradle.properties │ ├── logback-audio.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── audio/ │ │ ├── AudioAppender.java │ │ ├── AudioMarker.java │ │ ├── AudioMarkerAppender.java │ │ ├── FilePlayer.java │ │ ├── PlayMethods.java │ │ ├── Player.java │ │ ├── PlayerAction.java │ │ ├── PlayerAttachable.java │ │ ├── PlayerConverter.java │ │ ├── PlayerException.java │ │ ├── ResourcePlayer.java │ │ ├── SimplePlayer.java │ │ ├── SystemPlayer.java │ │ └── URLPlayer.java │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── audio/ │ │ ├── TestAudio.java │ │ └── TestNested.java │ └── resources/ │ ├── bark.ogg │ ├── drip.ogg │ ├── glass.ogg │ ├── logback-with-converter.xml │ ├── logback-with-marker-appender.xml │ ├── logback-with-nested-appender.xml │ ├── message.ogg │ └── sample.ogg ├── logback-budget/ │ ├── gradle.properties │ ├── logback-budget.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── budget/ │ │ ├── BudgetEvaluator.java │ │ ├── BudgetRule.java │ │ ├── BudgetRuleAction.java │ │ ├── BudgetRuleAttachable.java │ │ └── BudgetTurboFilter.java │ └── test/ │ ├── java/ │ │ └── com.tersesystems.logback.budget/ │ │ ├── BudgetEvaluatorTest.java │ │ └── BudgetTurboFilterTest.java │ └── resources/ │ ├── logback-budget.xml │ └── logback-turbofilter.xml ├── logback-bytebuddy/ │ ├── gradle.properties │ ├── logback-bytebuddy.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── bytebuddy/ │ │ ├── AdviceConfig.java │ │ ├── LogbackInstrumentationAgent.java │ │ ├── LoggingInstrumentationAdvice.java │ │ ├── LoggingInstrumentationByteBuddyBuilder.java │ │ ├── MethodInfo.java │ │ ├── MethodInfoLookup.java │ │ └── impl/ │ │ ├── DeclaringTypeLoggerResolver.java │ │ ├── Enter.java │ │ ├── Exit.java │ │ ├── FixedLoggerResolver.java │ │ ├── LoggerResolver.java │ │ ├── SafeArguments.java │ │ └── SystemFlow.java │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── bytebuddy/ │ │ ├── AdviceConfigTest.java │ │ ├── ClassCalledByAgent.java │ │ ├── InProcessInstrumentationExample.java │ │ └── PreloadedInstrumentationExample.java │ └── resources/ │ ├── logback-test.xml │ └── logback.conf ├── logback-censor/ │ ├── gradle.properties │ ├── logback-censor.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── censor/ │ │ ├── Censor.java │ │ ├── CensorAction.java │ │ ├── CensorAttachable.java │ │ ├── CensorConstants.java │ │ ├── CensorContextAware.java │ │ ├── CensorConverter.java │ │ ├── CensorRefAction.java │ │ ├── CensoringJsonGeneratorDecorator.java │ │ ├── CensoringPrettyPrintingJsonGeneratorDecorator.java │ │ └── RegexCensor.java │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── censor/ │ │ ├── CensorActionTest.java │ │ ├── CensoringJsonGeneratorDecoratorTest.java │ │ ├── RegexCensorTest.java │ │ └── TestAppender.java │ └── resources/ │ ├── test1.xml │ ├── test2.xml │ ├── test3.xml │ └── test4.xml ├── logback-classic/ │ ├── gradle.properties │ ├── logback-classic.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── classic/ │ │ ├── ChangeLogLevel.java │ │ ├── ContainerEventAppender.java │ │ ├── ContainerProxyLoggingEvent.java │ │ ├── ContextAwareBasicMarker.java │ │ ├── ExceptionMessageConverter.java │ │ ├── FormatParamsDecider.java │ │ ├── IContainerLoggingEvent.java │ │ ├── ILoggingEventFactory.java │ │ ├── LoggerDecider.java │ │ ├── LoggingEventFactory.java │ │ ├── MarkerLoggerDecider.java │ │ ├── NanoTime.java │ │ ├── NanoTimeComponentAppender.java │ │ ├── NanoTimeConverter.java │ │ ├── NanoTimeMarker.java │ │ ├── NanoTimeSupplier.java │ │ ├── ProxyLoggingEvent.java │ │ ├── SLF4JBridgeHandlerAction.java │ │ ├── SetLoggerLevelsAction.java │ │ ├── StartTime.java │ │ ├── StartTimeConverter.java │ │ ├── StartTimeMarker.java │ │ ├── StartTimeSupplier.java │ │ ├── TapFilter.java │ │ ├── TerseBasicMarker.java │ │ ├── TerseHighlightConverter.java │ │ ├── TimeSinceEpochConverter.java │ │ ├── TurboFilterDecider.java │ │ ├── Utils.java │ │ ├── encoder/ │ │ │ └── PatternLayoutEncoder.java │ │ ├── functional/ │ │ │ ├── GetAppenderFunction.java │ │ │ ├── GetSiftedAppenderFunction.java │ │ │ └── RootLoggerSupplier.java │ │ └── sift/ │ │ ├── DiscriminatingMarker.java │ │ ├── DiscriminatingMarkerFactory.java │ │ ├── DiscriminatingValue.java │ │ └── MarkerBasedDiscriminator.java │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── classic/ │ │ ├── ChangeLogLevelTest.java │ │ ├── CorrelationIdMarker.java │ │ ├── CorrelationIdTurboFilter.java │ │ ├── EnabledFilterTest.java │ │ ├── ExceptionMessageConverterTest.java │ │ ├── SetLoggerLevelsActionTest.java │ │ ├── TapFilterTest.java │ │ ├── TerseHighlightConverterTest.java │ │ └── UtilsTest.java │ └── resources/ │ ├── logback-tapfilter-correlation.xml │ └── logback-tapfilter.xml ├── logback-compress-encoder/ │ ├── gradle.properties │ ├── logback-compress-encoder.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com.tersesystems.logback.compress/ │ │ ├── CompressingEncoder.java │ │ └── CompressingFileAppender.java │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── compress/ │ │ └── Utils.java │ └── resources/ │ └── logback-with-zstd-encoder.xml ├── logback-core/ │ ├── gradle.properties │ ├── logback-core.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── core/ │ │ ├── AbstractAppender.java │ │ ├── Component.java │ │ ├── ComponentContainer.java │ │ ├── CompositeAppender.java │ │ ├── DecoratingAppender.java │ │ ├── DefaultAppenderAttachable.java │ │ ├── EnabledFilter.java │ │ ├── SelectAppender.java │ │ ├── encoder/ │ │ │ └── LayoutWrappingEncoder.java │ │ └── pattern/ │ │ └── PatternLayoutEncoderBase.java │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── core/ │ │ ├── CompositeAppenderTest.java │ │ ├── SelectAppenderTest.java │ │ └── TestAppender.java │ └── resources/ │ ├── logback-with-composite-appender.xml │ └── logback-with-select-appender.xml ├── logback-correlationid/ │ ├── gradle.properties │ ├── logback-correlationid.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── correlationid/ │ │ ├── CorrelationIdDecider.java │ │ ├── CorrelationIdFilter.java │ │ ├── CorrelationIdMarker.java │ │ ├── CorrelationIdProvider.java │ │ ├── CorrelationIdTapFilter.java │ │ └── CorrelationIdUtils.java │ └── test/ │ ├── java/ │ │ └── com.tersesystems.logback.correlationid/ │ │ ├── CorrelationIdFilterTest.java │ │ └── CorrelationIdTapFilterTest.java │ └── resources/ │ ├── logback-correlationid-jdbc.xml │ ├── logback-correlationid-tapfilter.xml │ ├── logback-correlationid.xml │ └── spy.properties ├── logback-exception-mapping/ │ ├── gradle.properties │ ├── logback-exception-mapping.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── exceptionmapping/ │ │ ├── BeanExceptionMapping.java │ │ ├── Constants.java │ │ ├── DefaultExceptionMappingRegistry.java │ │ ├── ExceptionCauseIterator.java │ │ ├── ExceptionHierarchyIterator.java │ │ ├── ExceptionMapping.java │ │ ├── ExceptionMappingAction.java │ │ ├── ExceptionMappingRegistry.java │ │ ├── ExceptionMappingRegistryAction.java │ │ ├── ExceptionMessageWithMappingsConverter.java │ │ ├── ExceptionProperty.java │ │ ├── FunctionExceptionMapping.java │ │ └── KeyValueExceptionProperty.java │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── exceptionmapping/ │ │ ├── ExceptionMappingTest.java │ │ ├── MyCustomException.java │ │ └── Thrower.java │ └── resources/ │ └── logback-test.xml ├── logback-exception-mapping-providers/ │ ├── gradle.properties │ ├── logback-exception-mapping-providers.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── exceptionmapping/ │ │ ├── config/ │ │ │ └── TypesafeConfigMappingsAction.java │ │ └── json/ │ │ └── ExceptionArgumentsProvider.java │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── exceptionmapping/ │ │ └── json/ │ │ ├── ExceptionArgumentsProviderTest.java │ │ ├── MySpecialException.java │ │ └── TypesafeConfigMappingsActionTest.java │ └── resources/ │ ├── logback-with-exception-mapping.xml │ └── logback.conf ├── logback-honeycomb-appender/ │ ├── gradle.properties │ ├── logback-honeycomb-appender.gradle │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── tersesystems/ │ └── logback/ │ └── honeycomb/ │ └── HoneycombAppender.java ├── logback-honeycomb-client/ │ ├── gradle.properties │ ├── logback-honeycomb-client.gradle │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── tersesystems/ │ └── logback/ │ └── honeycomb/ │ └── client/ │ ├── HoneycombClient.java │ ├── HoneycombClientService.java │ ├── HoneycombHeaders.java │ ├── HoneycombRequest.java │ └── HoneycombResponse.java ├── logback-honeycomb-okhttp/ │ ├── gradle.properties │ ├── logback-honeycomb-okhttp.gradle │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── honeycomb/ │ │ └── okhttp/ │ │ ├── HoneycombOkHTTPClient.java │ │ └── HoneycombOkHTTPClientService.java │ └── resources/ │ └── META-INF/ │ └── services/ │ └── com.tersesystems.logback.honeycomb.client.HoneycombClientService ├── logback-jdbc-appender/ │ ├── gradle.properties │ ├── logback-jdbc-appender.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── jdbc/ │ │ └── JDBCAppender.java │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── jdbc/ │ │ └── JDBCAppenderTest.java │ └── resources/ │ ├── logback-reference.conf │ └── logback-test.xml ├── logback-postgresjson-appender/ │ ├── gradle.properties │ ├── logback-postgresjson-appender.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── postgresjson/ │ │ └── PostgresJsonAppender.java │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── postgresjson/ │ │ └── PostgresJsonAppenderTest.java │ └── resources/ │ ├── db/ │ │ └── migration/ │ │ └── V1__logging_table.sql │ └── logback-postgres-json.xml ├── logback-tracing/ │ ├── gradle.properties │ ├── logback-tracing.gradle │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── tersesystems/ │ └── logback/ │ └── tracing/ │ ├── EventInfo.java │ ├── EventMarkerFactory.java │ ├── LinkInfo.java │ ├── LinkMarkerFactory.java │ ├── Nullable.java │ ├── SpanInfo.java │ ├── SpanMarkerFactory.java │ └── Tracer.java ├── logback-turbomarker/ │ ├── gradle.properties │ ├── logback-turbomarker.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── turbomarker/ │ │ ├── ContextAwareTurboFilterDecider.java │ │ ├── ContextAwareTurboMarker.java │ │ ├── ContextDecider.java │ │ ├── LoggerContextDecider.java │ │ ├── MarkerContextDecider.java │ │ ├── TurboMarker.java │ │ └── TurboMarkerTurboFilter.java │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── turbomarker/ │ │ ├── ApplicationContext.java │ │ ├── DiagnosticLoggingExample.java │ │ ├── LDMarkerFactory.java │ │ ├── LDMarkerTest.java │ │ ├── UserMarker.java │ │ ├── UserMarkerFactory.java │ │ └── UserMarkerTest.java │ └── resources/ │ └── logback-test.xml ├── logback-typesafe-config/ │ ├── gradle.properties │ ├── logback-typesafe-config.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── typesafeconfig/ │ │ ├── ConfigConstants.java │ │ ├── ConfigConversion.java │ │ ├── ConfigListConverter.java │ │ └── TypesafeConfigAction.java │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── typesafeconfig/ │ │ ├── ConfigListConverterTest.java │ │ └── TypesafeConfigActionTest.java │ └── resources/ │ ├── logback-test.conf │ └── typesafeconfig/ │ ├── config-with-context.xml │ ├── config-with-default.xml │ └── config-with-local.xml ├── logback-uniqueid-appender/ │ ├── gradle.properties │ ├── logback-uniqueid-appender.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── uniqueid/ │ │ ├── FlakeIdGenerator.java │ │ ├── IdGenerator.java │ │ ├── KsuidSubsecondIdGenerator.java │ │ ├── RandomUUIDIdGenerator.java │ │ ├── TsidIdgenerator.java │ │ ├── UlidIdGenerator.java │ │ ├── UniqueIdComponentAppender.java │ │ ├── UniqueIdConverter.java │ │ └── UniqueIdProvider.java │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── tersesystems/ │ │ └── logback/ │ │ └── uniqueid/ │ │ └── UniqueIdAppenderTest.java │ └── resources/ │ └── logback-with-uniqueid-appender.xml ├── mkdocs.yml ├── settings.gradle └── version.properties
SYMBOL INDEX (1119 symbols across 195 files)
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/AudioAppender.java
class AudioAppender (line 16) | public class AudioAppender extends AppenderBase<ILoggingEvent> implement...
method append (line 20) | @Override
method addPlayer (line 25) | @Override
method clearAllPlayers (line 30) | @Override
method start (line 35) | @Override
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/AudioMarker.java
class AudioMarker (line 18) | public class AudioMarker extends TerseBasicMarker implements Player {
method AudioMarker (line 24) | public AudioMarker(URL url) {
method AudioMarker (line 29) | public AudioMarker(Path path) {
method AudioMarker (line 34) | public AudioMarker(InputStream inputStream) {
method AudioMarker (line 39) | public AudioMarker(Player player) {
method play (line 44) | public void play() {
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/AudioMarkerAppender.java
class AudioMarkerAppender (line 18) | public class AudioMarkerAppender extends AppenderBase<ILoggingEvent> {
method append (line 20) | @Override
method writePlayerMarkerIfNecessary (line 25) | private void writePlayerMarkerIfNecessary(Marker marker) {
method isPlayerMarker (line 39) | private static boolean isPlayerMarker(Marker marker) {
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/FilePlayer.java
class FilePlayer (line 19) | public class FilePlayer extends ContextAwareBase implements Player, Life...
method FilePlayer (line 25) | public FilePlayer() {}
method setFile (line 27) | public void setFile(String file) {
method play (line 31) | @Override
method start (line 36) | @Override
method stop (line 47) | @Override
method isStarted (line 53) | @Override
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/PlayMethods.java
type PlayMethods (line 17) | public interface PlayMethods {
method play (line 19) | default void play(Supplier<AudioInputStream> supplier) {
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/Player.java
type Player (line 14) | public interface Player {
method play (line 15) | void play();
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/PlayerAction.java
class PlayerAction (line 22) | public class PlayerAction extends Action {
method begin (line 26) | @Override
method end (line 69) | @Override
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/PlayerAttachable.java
type PlayerAttachable (line 13) | public interface PlayerAttachable {
method addPlayer (line 14) | void addPlayer(Player player);
method clearAllPlayers (line 16) | void clearAllPlayers();
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/PlayerConverter.java
class PlayerConverter (line 18) | public class PlayerConverter extends ClassicConverter {
method convert (line 20) | @Override
method writePlayerMarkerIfNecessary (line 26) | private void writePlayerMarkerIfNecessary(Marker marker) {
method isPlayerMarker (line 40) | private static boolean isPlayerMarker(Marker marker) {
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/PlayerException.java
class PlayerException (line 13) | public class PlayerException extends RuntimeException {
method PlayerException (line 14) | public PlayerException() {}
method PlayerException (line 16) | public PlayerException(String s) {
method PlayerException (line 20) | public PlayerException(String s, Throwable throwable) {
method PlayerException (line 24) | public PlayerException(Throwable throwable) {
method PlayerException (line 28) | public PlayerException(String s, Throwable throwable, boolean b, boole...
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/ResourcePlayer.java
class ResourcePlayer (line 17) | public class ResourcePlayer extends ContextAwareBase implements Player, ...
method ResourcePlayer (line 23) | public ResourcePlayer() {}
method setResource (line 25) | public void setResource(String resource) {
method play (line 29) | @Override
method start (line 38) | @Override
method stop (line 49) | @Override
method isStarted (line 55) | @Override
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/SimplePlayer.java
class SimplePlayer (line 23) | public class SimplePlayer implements PlayMethods, Player {
method SimplePlayer (line 27) | protected SimplePlayer(Supplier<AudioInputStream> supplier) {
method fromURL (line 31) | public static Player fromURL(URL url) {
method fromPath (line 42) | public static Player fromPath(Path path) {
method fromInputStream (line 53) | public static Player fromInputStream(InputStream inputStream) {
method play (line 64) | @Override
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/SystemPlayer.java
class SystemPlayer (line 15) | public class SystemPlayer implements Player {
method play (line 16) | @Override
FILE: logback-audio/src/main/java/com/tersesystems/logback/audio/URLPlayer.java
class URLPlayer (line 16) | public class URLPlayer extends ContextAwareBase implements Player {
method URLPlayer (line 20) | public URLPlayer() {}
method URLPlayer (line 22) | public void URLPlayer(URL url) {
method play (line 26) | @Override
FILE: logback-audio/src/test/java/com/tersesystems/logback/audio/TestAudio.java
class TestAudio (line 23) | public class TestAudio {
method testMarkerWithURL (line 25) | @Test
method testMarkerWithURLWithConverter (line 43) | @Test
method testMarkerWithPath (line 62) | public void testMarkerWithPath() throws JoranException, InterruptedExc...
FILE: logback-audio/src/test/java/com/tersesystems/logback/audio/TestNested.java
class TestNested (line 20) | public class TestNested {
method testLogger (line 22) | @Test
FILE: logback-budget/src/main/java/com/tersesystems/logback/budget/BudgetEvaluator.java
class BudgetEvaluator (line 25) | public class BudgetEvaluator extends EventEvaluatorBase<ILoggingEvent>
method start (line 31) | @Override
method createCircuitBreaker (line 40) | private CircuitBreaker<Integer> createCircuitBreaker(BudgetRule budget...
method evaluate (line 68) | @Override
method addBudgetRule (line 87) | @Override
method clearAllBudgetRules (line 92) | @Override
FILE: logback-budget/src/main/java/com/tersesystems/logback/budget/BudgetRule.java
class BudgetRule (line 15) | public class BudgetRule {
method BudgetRule (line 22) | public BudgetRule() {}
method getName (line 24) | public String getName() {
method setName (line 28) | public void setName(String name) {
method getThreshold (line 32) | public int getThreshold() {
method setThreshold (line 36) | public void setThreshold(int threshold) {
method getInterval (line 40) | public long getInterval() {
method setInterval (line 44) | public void setInterval(long interval) {
method getTimeUnit (line 48) | public String getTimeUnit() {
method setTimeUnit (line 52) | public void setTimeUnit(String timeUnit) {
method toString (line 56) | @Override
FILE: logback-budget/src/main/java/com/tersesystems/logback/budget/BudgetRuleAction.java
class BudgetRuleAction (line 19) | public class BudgetRuleAction extends Action {
method begin (line 24) | @Override
method end (line 70) | @Override
FILE: logback-budget/src/main/java/com/tersesystems/logback/budget/BudgetRuleAttachable.java
type BudgetRuleAttachable (line 13) | public interface BudgetRuleAttachable {
method addBudgetRule (line 15) | void addBudgetRule(BudgetRule budget);
method clearAllBudgetRules (line 17) | void clearAllBudgetRules();
FILE: logback-budget/src/main/java/com/tersesystems/logback/budget/BudgetTurboFilter.java
class BudgetTurboFilter (line 16) | public class BudgetTurboFilter extends TurboFilter implements BudgetRule...
method start (line 21) | public void start() {
method createCircuitBreaker (line 29) | private CircuitBreaker<Integer> createCircuitBreaker(BudgetRule budget...
method decide (line 57) | @Override
method addBudgetRule (line 76) | @Override
method clearAllBudgetRules (line 81) | @Override
method setOnMatch (line 89) | public final void setOnMatch(FilterReply reply) {
method setOnMismatch (line 93) | public final void setOnMismatch(FilterReply reply) {
method getOnMatch (line 97) | public final FilterReply getOnMatch() {
method getOnMismatch (line 101) | public final FilterReply getOnMismatch() {
FILE: logback-budget/src/test/java/com.tersesystems.logback.budget/BudgetEvaluatorTest.java
class BudgetEvaluatorTest (line 20) | public class BudgetEvaluatorTest {
method testBudget (line 22) | @Test
FILE: logback-budget/src/test/java/com.tersesystems.logback.budget/BudgetTurboFilterTest.java
class BudgetTurboFilterTest (line 10) | public class BudgetTurboFilterTest {
method testBudget (line 12) | @Test
FILE: logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/AdviceConfig.java
class AdviceConfig (line 22) | public class AdviceConfig {
method AdviceConfig (line 31) | public AdviceConfig(ClassLoader classLoader, String serviceName) {
method getServiceName (line 37) | public String getServiceName() {
method addTrace (line 41) | public void addTrace(String className, List<String> methodNames) throw...
method addTrace (line 46) | public void addTrace(String className) throws Exception {
method classNames (line 51) | public List<String> classNames() {
method methods (line 55) | public ElementMatcher<? super MethodDescription> methods() {
method types (line 59) | public ElementMatcher<? super TypeDescription> types() {
method getTraceConfig (line 63) | private TraceConfig getTraceConfig() {
class TraceConfig (line 70) | static final class TraceConfig {
method TraceConfig (line 76) | private TraceConfig(
method create (line 85) | static TraceConfig create(ClassLoader classLoader, String className,...
method create (line 97) | static TraceConfig create(ClassLoader classLoader, String className)...
method classNames (line 106) | public List<String> classNames() {
method methods (line 110) | public ElementMatcher.Junction<? super MethodDescription> methods() {
method types (line 114) | public ElementMatcher.Junction<? super TypeDescription> types() {
method join (line 118) | public TraceConfig join(TraceConfig other) {
method createTypeDescription (line 128) | private static TypeDescription createTypeDescription(ClassLoader cla...
method toString (line 134) | @Override
FILE: logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/LogbackInstrumentationAgent.java
class LogbackInstrumentationAgent (line 28) | public class LogbackInstrumentationAgent {
method premain (line 32) | public static void premain(String arg, Instrumentation instrumentation...
method parseDebug (line 41) | private static boolean parseDebug(String arg) {
method agentmain (line 45) | public static void agentmain(String arg, Instrumentation instrumentati...
method injectBootstrapClasses (line 60) | private static void injectBootstrapClasses(Instrumentation instrumenta...
FILE: logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/LoggingInstrumentationAdvice.java
class LoggingInstrumentationAdvice (line 28) | public class LoggingInstrumentationAdvice {
method initialize (line 68) | void initialize(Instrumentation instrumentation, boolean debug) {
method generateConfig (line 96) | public static Config generateConfig(ClassLoader classLoader, boolean d...
method generateAdviceConfig (line 126) | public static AdviceConfig generateAdviceConfig(
method clean (line 157) | private static String clean(String key) {
method createDebugListener (line 161) | private static AgentBuilder.Listener createDebugListener(List<String> ...
method stringMatcher (line 166) | public static ElementMatcher.Junction<? super String> stringMatcher(
method enter (line 182) | @Advice.OnMethodEnter
method exit (line 189) | @Advice.OnMethodExit(onThrowable = Throwable.class)
FILE: logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/LoggingInstrumentationByteBuddyBuilder.java
class LoggingInstrumentationByteBuddyBuilder (line 33) | public class LoggingInstrumentationByteBuddyBuilder {
method builderFromConfig (line 52) | public AgentBuilder builderFromConfig(
class LineWrapper (line 71) | static class LineWrapper extends AsmVisitorWrapper.AbstractBase {
method wrap (line 72) | @Override
method builderFromConfigWithRetransformation (line 148) | public AgentBuilder builderFromConfigWithRetransformation(
method withSystemClassLoaderMatching (line 154) | protected AgentBuilder withSystemClassLoaderMatching(AgentBuilder buil...
method ignoreMatchers (line 162) | public AgentBuilder.RawMatcher.ForElementMatchers ignoreMatchers() {
method builderFromConfig (line 172) | public AgentBuilder builderFromConfig(
method builderFromConfig (line 179) | public AgentBuilder builderFromConfig(AdviceConfig c) {
method builderFromConfigWithRetransformation (line 183) | public AgentBuilder builderFromConfigWithRetransformation(AdviceConfig...
FILE: logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/MethodInfo.java
class MethodInfo (line 18) | public class MethodInfo {
method MethodInfo (line 25) | MethodInfo(String methodName, String descriptor, String source) {
method setStartLine (line 31) | public void setStartLine(int line) {
method setEndLine (line 35) | public void setEndLine(int line) {
method getStartLine (line 39) | public int getStartLine() {
method getEndLine (line 43) | public int getEndLine() {
method toString (line 47) | @Override
FILE: logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/MethodInfoLookup.java
class MethodInfoLookup (line 21) | public class MethodInfoLookup {
method getInstance (line 26) | public static MethodInfoLookup getInstance() {
class SingletonHolder (line 30) | static class SingletonHolder {
method add (line 34) | public void add(String className, MethodInfo methodInfo) {
method find (line 39) | public Optional<MethodInfo> find(String className, String methodName, ...
method matchingInfo (line 44) | private Predicate<MethodInfo> matchingInfo(String methodName, String d...
FILE: logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/impl/DeclaringTypeLoggerResolver.java
class DeclaringTypeLoggerResolver (line 19) | public class DeclaringTypeLoggerResolver implements LoggerResolver {
method DeclaringTypeLoggerResolver (line 23) | public DeclaringTypeLoggerResolver(Supplier<ILoggerFactory> loggerFact...
method resolve (line 27) | @Override
FILE: logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/impl/Enter.java
class Enter (line 25) | public class Enter {
method apply (line 30) | public static void apply(String origin, Object[] allArguments) {
FILE: logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/impl/Exit.java
class Exit (line 26) | public class Exit {
method apply (line 32) | public static void apply(
FILE: logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/impl/FixedLoggerResolver.java
class FixedLoggerResolver (line 18) | public class FixedLoggerResolver implements LoggerResolver {
method FixedLoggerResolver (line 21) | public FixedLoggerResolver(Logger logger) {
method resolve (line 25) | @Override
FILE: logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/impl/LoggerResolver.java
type LoggerResolver (line 16) | public interface LoggerResolver {
method resolve (line 21) | Logger resolve(String origin);
FILE: logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/impl/SafeArguments.java
class SafeArguments (line 18) | public class SafeArguments {
method apply (line 19) | public List<String> apply(Object[] allArguments) {
method apply (line 23) | public String apply(Object returnValue) {
method parseCertificate (line 43) | private String parseCertificate(X509Certificate cert) {
method parseArray (line 49) | private String parseArray(Object[] returnValue) {
method parseCollection (line 53) | private String parseCollection(Collection<Object> coll) {
method parseStream (line 57) | private String parseStream(Stream<Object> stream) {
FILE: logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/impl/SystemFlow.java
class SystemFlow (line 30) | public final class SystemFlow {
method getLoggerResolver (line 61) | public static LoggerResolver getLoggerResolver() {
method setLoggerResolver (line 65) | public static void setLoggerResolver(LoggerResolver loggerResolver) {
method getLogger (line 69) | public static Logger getLogger(String origin) {
method setServiceName (line 73) | public static void setServiceName(String serviceName) {
method setIdGenerator (line 77) | public static void setIdGenerator(Supplier<String> idGenerator) {
method getSafeArguments (line 81) | public static SafeArguments getSafeArguments() {
method setSafeArguments (line 85) | public static void setSafeArguments(SafeArguments safeArguments) {
method createMarker (line 89) | public static LogstashMarker createMarker(SpanInfo span) {
method pushSpan (line 93) | public static void pushSpan(String name) {
method popSpan (line 97) | public static Optional<SpanInfo> popSpan() {
method safeReturnValue (line 101) | static StructuredArgument safeReturnValue(Object returnValue) {
method safeArguments (line 106) | static StructuredArgument safeArguments(Object[] allArguments) {
method createName (line 111) | static String createName(String className, String method, String signa...
method baseMarkers (line 115) | static LogstashMarker baseMarkers() {
FILE: logback-bytebuddy/src/test/java/com/tersesystems/logback/bytebuddy/AdviceConfigTest.java
class AdviceConfigTest (line 18) | public class AdviceConfigTest {
method testConfig (line 20) | @Test
FILE: logback-bytebuddy/src/test/java/com/tersesystems/logback/bytebuddy/ClassCalledByAgent.java
class ClassCalledByAgent (line 14) | public class ClassCalledByAgent {
method printStatement (line 15) | public void printStatement() {
method printArgument (line 19) | public void printArgument(String arg) {
method throwException (line 23) | public void throwException(String arg) {
FILE: logback-bytebuddy/src/test/java/com/tersesystems/logback/bytebuddy/InProcessInstrumentationExample.java
class InProcessInstrumentationExample (line 34) | public class InProcessInstrumentationExample {
method createDebugListener (line 36) | public static AgentBuilder.Listener createDebugListener(List<String> c...
method main (line 42) | public static void main(String[] args) throws Exception {
FILE: logback-bytebuddy/src/test/java/com/tersesystems/logback/bytebuddy/PreloadedInstrumentationExample.java
class PreloadedInstrumentationExample (line 21) | public class PreloadedInstrumentationExample {
method main (line 22) | public static void main(String[] args) throws Exception {
FILE: logback-censor/src/main/java/com/tersesystems/logback/censor/Censor.java
type Censor (line 16) | public interface Censor extends Component {
method censorText (line 17) | CharSequence censorText(CharSequence input);
FILE: logback-censor/src/main/java/com/tersesystems/logback/censor/CensorAction.java
class CensorAction (line 25) | public class CensorAction extends Action {
method begin (line 29) | @SuppressWarnings("unchecked")
method addConverter (line 89) | private void addConverter() {
method end (line 103) | public void end(InterpretationContext ec, String name) {
FILE: logback-censor/src/main/java/com/tersesystems/logback/censor/CensorAttachable.java
type CensorAttachable (line 13) | public interface CensorAttachable {
method addCensor (line 14) | void addCensor(CensorContextAware censor);
FILE: logback-censor/src/main/java/com/tersesystems/logback/censor/CensorConstants.java
class CensorConstants (line 13) | public class CensorConstants {
FILE: logback-censor/src/main/java/com/tersesystems/logback/censor/CensorContextAware.java
type CensorContextAware (line 16) | public interface CensorContextAware extends Censor, ContextAware, LifeCy...
method getName (line 19) | String getName();
method setName (line 24) | void setName(String name);
FILE: logback-censor/src/main/java/com/tersesystems/logback/censor/CensorConverter.java
class CensorConverter (line 33) | public class CensorConverter extends CompositeConverter<ILoggingEvent> {
method start (line 37) | @Override
method transform (line 73) | @SuppressWarnings("unchecked")
FILE: logback-censor/src/main/java/com/tersesystems/logback/censor/CensorRefAction.java
class CensorRefAction (line 19) | public class CensorRefAction extends Action {
method begin (line 22) | @SuppressWarnings("unchecked")
method end (line 78) | public void end(InterpretationContext ec, String n) {}
FILE: logback-censor/src/main/java/com/tersesystems/logback/censor/CensoringJsonGeneratorDecorator.java
class CensoringJsonGeneratorDecorator (line 27) | public class CensoringJsonGeneratorDecorator extends ContextAwareBase
method decorate (line 33) | @Override
method getCensors (line 41) | public List<CensorContextAware> getCensors() {
method addCensor (line 45) | public void addCensor(CensorContextAware censor) {
method start (line 49) | @Override
method stop (line 54) | @Override
method isStarted (line 61) | @Override
method addFilterKey (line 68) | public void addFilterKey(String filterKey) {
class CensoringTokenFilter (line 73) | private class CensoringTokenFilter extends TokenFilter {
method includeElement (line 74) | @Override
method includeProperty (line 79) | @Override
method shouldFilter (line 87) | private boolean shouldFilter(String name) {
method _includeScalar (line 91) | @Override
class CensoringJsonGeneratorDelegate (line 98) | private class CensoringJsonGeneratorDelegate extends JsonGeneratorDele...
method CensoringJsonGeneratorDelegate (line 99) | public CensoringJsonGeneratorDelegate(JsonGenerator d) {
method censorSensitiveMessage (line 103) | private String censorSensitiveMessage(String original) {
method writeString (line 113) | @Override
method writeString (line 119) | @Override
method writeString (line 126) | @Override
method writeRawUTF8String (line 133) | @Override
method writeUTF8String (line 140) | @Override
FILE: logback-censor/src/main/java/com/tersesystems/logback/censor/CensoringPrettyPrintingJsonGeneratorDecorator.java
class CensoringPrettyPrintingJsonGeneratorDecorator (line 15) | public class CensoringPrettyPrintingJsonGeneratorDecorator extends Censo...
method decorate (line 17) | @Override
FILE: logback-censor/src/main/java/com/tersesystems/logback/censor/RegexCensor.java
class RegexCensor (line 17) | public class RegexCensor extends ContextAwareBase implements CensorConte...
method getName (line 28) | public String getName() {
method setName (line 32) | public void setName(String name) {
method getReplacementText (line 36) | public String getReplacementText() {
method setReplacementText (line 40) | public void setReplacementText(String replacementText) {
method setRegex (line 44) | public void setRegex(String regex) {
method isStarted (line 48) | @Override
method start (line 53) | @Override
method stop (line 69) | @Override
method censorText (line 77) | @Override
FILE: logback-censor/src/test/java/com/tersesystems/logback/censor/CensorActionTest.java
class CensorActionTest (line 25) | public class CensorActionTest {
method setUp (line 30) | @Before
method testFirstTest1 (line 35) | @Test
method testSecondTest1 (line 46) | @Test
method testJsonTest3 (line 57) | @Test
method testJsonTest4 (line 69) | @Test
method createLoggingEvent (line 80) | private LoggingEvent createLoggingEvent(ch.qos.logback.classic.Logger ...
FILE: logback-censor/src/test/java/com/tersesystems/logback/censor/CensoringJsonGeneratorDecoratorTest.java
class CensoringJsonGeneratorDecoratorTest (line 22) | public class CensoringJsonGeneratorDecoratorTest {
method basicCensor (line 24) | @Test
method filterKey (line 57) | @Test
method prettyPrintCensor (line 75) | @Test
FILE: logback-censor/src/test/java/com/tersesystems/logback/censor/RegexCensorTest.java
class RegexCensorTest (line 17) | public class RegexCensorTest {
method testCensor (line 19) | @Test
method testCensorWithNoMatch (line 31) | @Test
FILE: logback-censor/src/test/java/com/tersesystems/logback/censor/TestAppender.java
class TestAppender (line 19) | public class TestAppender extends AppenderBase<ILoggingEvent> {
method getEncoder (line 25) | public Encoder<ILoggingEvent> getEncoder() {
method setEncoder (line 29) | public void setEncoder(Encoder<ILoggingEvent> encoder) {
method append (line 33) | @Override
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/ChangeLogLevel.java
class ChangeLogLevel (line 19) | public class ChangeLogLevel {
method ChangeLogLevel (line 23) | public ChangeLogLevel() {
method ChangeLogLevel (line 27) | public ChangeLogLevel(ILoggerFactory loggerFactory) {
method changeLogLevel (line 31) | public void changeLogLevel(String loggerName, String levelName) {
method changeLogLevel (line 35) | public final void changeLogLevel(String loggerName, int levelNumber) {
method changeLogLevel (line 39) | public final void changeLogLevel(org.slf4j.Logger logger, String level...
method changeLogLevel (line 45) | public final void changeLogLevel(org.slf4j.Logger logger, int levelNum...
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/ContainerEventAppender.java
class ContainerEventAppender (line 21) | public class ContainerEventAppender
method decorateEvent (line 23) | @Override
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/ContainerProxyLoggingEvent.java
class ContainerProxyLoggingEvent (line 20) | public class ContainerProxyLoggingEvent extends ProxyLoggingEvent
method ContainerProxyLoggingEvent (line 24) | public ContainerProxyLoggingEvent(ILoggingEvent delegate) {
method putComponent (line 28) | public <T> void putComponent(Class<T> type, T instance) {
method getComponent (line 32) | public <T> T getComponent(Class<T> type) {
method hasComponent (line 36) | @Override
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/ContextAwareBasicMarker.java
class ContextAwareBasicMarker (line 18) | public class ContextAwareBasicMarker extends TerseBasicMarker implements...
method ContextAwareBasicMarker (line 22) | public ContextAwareBasicMarker(String name) {
method setContext (line 26) | public void setContext(Context context) {
method getContext (line 34) | public Context getContext() {
method getStatusManager (line 38) | public StatusManager getStatusManager() {
method getDeclaredOrigin (line 51) | protected Object getDeclaredOrigin() {
method addStatus (line 55) | public void addStatus(Status status) {
method addInfo (line 68) | public void addInfo(String msg) {
method addInfo (line 72) | public void addInfo(String msg, Throwable ex) {
method addWarn (line 76) | public void addWarn(String msg) {
method addWarn (line 80) | public void addWarn(String msg, Throwable ex) {
method addError (line 84) | public void addError(String msg) {
method addError (line 88) | public void addError(String msg, Throwable ex) {
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/ExceptionMessageConverter.java
class ExceptionMessageConverter (line 31) | public class ExceptionMessageConverter extends ThrowableHandlingConverter {
method convert (line 33) | @Override
method getLeadingWhitespace (line 56) | private Integer getLeadingWhitespace() {
method getDepth (line 60) | protected Integer getDepth() {
method getPrefix (line 64) | protected String getPrefix() {
method getSeparator (line 68) | protected String getSeparator() {
method getSuffix (line 72) | protected String getSuffix() {
method getOption (line 76) | protected Optional<String> getOption(int index) {
method processException (line 84) | protected String processException(
method constructMessage (line 105) | protected String constructMessage(IThrowableProxy ex) {
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/FormatParamsDecider.java
type FormatParamsDecider (line 19) | @FunctionalInterface
method decide (line 22) | @Override
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/IContainerLoggingEvent.java
type IContainerLoggingEvent (line 18) | public interface IContainerLoggingEvent extends ILoggingEvent, Component...
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/ILoggingEventFactory.java
type ILoggingEventFactory (line 18) | public interface ILoggingEventFactory<E extends ILoggingEvent> {
method create (line 19) | E create(Marker marker, Logger logger, Level level, String msg, Object...
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/LoggerDecider.java
type LoggerDecider (line 18) | @FunctionalInterface
method decide (line 20) | default FilterReply decide(
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/LoggingEventFactory.java
class LoggingEventFactory (line 21) | public class LoggingEventFactory implements ILoggingEventFactory<ILoggin...
method create (line 22) | public ILoggingEvent create(
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/MarkerLoggerDecider.java
type MarkerLoggerDecider (line 18) | @FunctionalInterface
method decide (line 21) | default FilterReply decide(
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/NanoTime.java
class NanoTime (line 22) | public final class NanoTime {
method fromOptional (line 25) | public static Optional<Long> fromOptional(Context context, ILoggingEve...
method fromMarker (line 36) | static Optional<Long> fromMarker(Context context, Marker m) {
method fromContainer (line 59) | public static Optional<Long> fromContainer(ComponentContainer containe...
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/NanoTimeComponentAppender.java
class NanoTimeComponentAppender (line 18) | public class NanoTimeComponentAppender
method decorateEvent (line 20) | @Override
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/NanoTimeConverter.java
class NanoTimeConverter (line 18) | public class NanoTimeConverter extends ClassicConverter {
method convert (line 19) | @Override
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/NanoTimeMarker.java
class NanoTimeMarker (line 14) | public class NanoTimeMarker extends TerseBasicMarker implements NanoTime...
method NanoTimeMarker (line 18) | public NanoTimeMarker() {
method getNanoTime (line 23) | public long getNanoTime() {
method create (line 27) | public static NanoTimeMarker create() {
method toString (line 31) | @Override
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/NanoTimeSupplier.java
type NanoTimeSupplier (line 16) | public interface NanoTimeSupplier extends Component {
method getNanoTime (line 17) | long getNanoTime();
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/ProxyLoggingEvent.java
class ProxyLoggingEvent (line 26) | public class ProxyLoggingEvent implements ILoggingEvent {
method ProxyLoggingEvent (line 29) | public ProxyLoggingEvent(ILoggingEvent delegate) {
method getDelegate (line 33) | public ILoggingEvent getDelegate() {
method getThreadName (line 37) | @Override
method getLevel (line 42) | @Override
method getMessage (line 47) | @Override
method getArgumentArray (line 52) | @Override
method getFormattedMessage (line 57) | @Override
method getLoggerName (line 62) | @Override
method getLoggerContextVO (line 67) | @Override
method getThrowableProxy (line 72) | @Override
method getCallerData (line 77) | @Override
method hasCallerData (line 82) | @Override
method getMarker (line 87) | @Override
method getMDCPropertyMap (line 92) | @Override
method getMdc (line 97) | @Override
method getTimeStamp (line 102) | @Override
method prepareForDeferredProcessing (line 107) | @Override
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/SLF4JBridgeHandlerAction.java
class SLF4JBridgeHandlerAction (line 26) | public class SLF4JBridgeHandlerAction extends Action {
method begin (line 28) | @Override
method end (line 35) | @Override
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/SetLoggerLevelsAction.java
class SetLoggerLevelsAction (line 22) | public class SetLoggerLevelsAction extends Action {
method getLevelsKey (line 27) | public String getLevelsKey() {
method setLevelsKey (line 31) | public void setLevelsKey(String levelsKey) {
method begin (line 35) | @Override
method end (line 41) | @Override
method doConfigure (line 44) | @SuppressWarnings("unchecked")
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/StartTime.java
class StartTime (line 23) | public final class StartTime {
method from (line 33) | public static Instant from(Context context, ILoggingEvent event) {
method fromOptional (line 45) | public static Optional<Instant> fromOptional(Context context, ILogging...
method fromMarker (line 62) | public static Optional<Instant> fromMarker(Context context, Marker m) {
method fromContainer (line 85) | public static Optional<Instant> fromContainer(ComponentContainer conta...
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/StartTimeConverter.java
class StartTimeConverter (line 18) | public class StartTimeConverter extends ClassicConverter {
method convert (line 19) | @Override
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/StartTimeMarker.java
class StartTimeMarker (line 16) | public class StartTimeMarker extends TerseBasicMarker implements StartTi...
method StartTimeMarker (line 20) | public StartTimeMarker(Instant start) {
method getStartTime (line 25) | @Override
method toString (line 30) | @Override
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/StartTimeSupplier.java
type StartTimeSupplier (line 15) | public interface StartTimeSupplier {
method getStartTime (line 16) | Instant getStartTime();
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/TapFilter.java
class TapFilter (line 37) | public class TapFilter extends TurboFilter
method addTurboFilter (line 46) | public void addTurboFilter(TurboFilter turboFilter) {
method getTurboFilters (line 50) | public TurboFilterList getTurboFilters() {
method getTurboFilters (line 54) | public void getTurboFilters(TurboFilterList tapEvaluator) {
method appenderAttachableImpl (line 58) | @Override
method getLoggingEventFactory (line 63) | public ILoggingEventFactory<ILoggingEvent> getLoggingEventFactory() {
method setLoggingEventFactory (line 67) | public void setLoggingEventFactory(ILoggingEventFactory<ILoggingEvent>...
method start (line 71) | @Override
method decide (line 97) | @Override
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/TerseBasicMarker.java
class TerseBasicMarker (line 23) | public class TerseBasicMarker implements Marker {
method TerseBasicMarker (line 28) | public TerseBasicMarker(String name) {
method getName (line 33) | public String getName() {
method add (line 37) | public synchronized void add(Marker reference) {
method hasReferences (line 48) | public synchronized boolean hasReferences() {
method hasChildren (line 55) | @Deprecated
method iterator (line 60) | public synchronized Iterator<Marker> iterator() {
method remove (line 64) | public synchronized boolean remove(Marker referenceToRemove) {
method contains (line 72) | public boolean contains(Marker other) {
method contains (line 84) | public boolean contains(String name) {
method equals (line 100) | public boolean equals(Object obj) {
method hashCode (line 109) | public int hashCode() {
method toString (line 113) | public String toString() {
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/TerseHighlightConverter.java
class TerseHighlightConverter (line 30) | public class TerseHighlightConverter extends ForegroundCompositeConverte...
type Color (line 34) | enum Color {
method Color (line 46) | Color(String code) {
method getForegroundColorCode (line 51) | @Override
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/TimeSinceEpochConverter.java
class TimeSinceEpochConverter (line 16) | public class TimeSinceEpochConverter extends ClassicConverter {
method convert (line 17) | @Override
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/TurboFilterDecider.java
type TurboFilterDecider (line 26) | public interface TurboFilterDecider {
method decide (line 27) | FilterReply decide(
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/Utils.java
class Utils (line 38) | public class Utils {
method Utils (line 41) | Utils(LoggerContext loggerContext) {
method defaultContext (line 45) | public static LoggerContext defaultContext() {
method contextFromResource (line 55) | public static LoggerContext contextFromResource(String resourcePath) t...
method create (line 64) | public static Utils create(LoggerContext loggerContext) {
method create (line 68) | public static Utils create(String resourcePath) throws JoranException {
method create (line 72) | public static Utils create() {
method getStatusList (line 76) | public List<Status> getStatusList() {
method getLoggerContext (line 81) | public LoggerContext getLoggerContext() {
method getRootLogger (line 85) | public Logger getRootLogger() {
method getLogger (line 89) | public Logger getLogger(String loggerName) {
method getLogger (line 93) | public Logger getLogger(Class<?> clazz) {
method getObject (line 97) | public <E> Optional<E> getObject(Class<E> classType, String name) {
method getTurboFilter (line 103) | public <E extends TurboFilter> Optional<E> getTurboFilter(
method getAppender (line 112) | public <E extends Appender<ILoggingEvent>> Optional<E> getAppender(Str...
method getMDCPropertyMap (line 116) | public Map<String, String> getMDCPropertyMap() {
method getLoggingEventFactory (line 130) | public LoggingEventFactory getLoggingEventFactory() {
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/encoder/PatternLayoutEncoder.java
class PatternLayoutEncoder (line 23) | public class PatternLayoutEncoder extends PatternLayoutEncoderBase<ILogg...
method start (line 25) | @Override
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/functional/GetAppenderFunction.java
class GetAppenderFunction (line 23) | public class GetAppenderFunction<A extends Appender<ILoggingEvent>>
method GetAppenderFunction (line 28) | public GetAppenderFunction(Logger rootLogger) {
method apply (line 32) | @SuppressWarnings("unchecked")
method create (line 43) | public static <AT extends Appender<ILoggingEvent>> GetAppenderFunction...
method create (line 47) | public static <AT extends Appender<ILoggingEvent>> GetAppenderFunction...
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/functional/GetSiftedAppenderFunction.java
class GetSiftedAppenderFunction (line 20) | public class GetSiftedAppenderFunction<A> implements Function<SiftingApp...
method GetSiftedAppenderFunction (line 24) | public GetSiftedAppenderFunction(String key) {
method apply (line 28) | @SuppressWarnings("unchecked")
method create (line 39) | public static <AT extends Appender<ILoggingEvent>> GetSiftedAppenderFu...
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/functional/RootLoggerSupplier.java
class RootLoggerSupplier (line 18) | public class RootLoggerSupplier implements Supplier<Logger> {
method RootLoggerSupplier (line 22) | public RootLoggerSupplier(LoggerContext loggerContext) {
method get (line 26) | public Logger get() {
method create (line 30) | public static RootLoggerSupplier create(LoggerContext loggerContext) {
method create (line 34) | public static RootLoggerSupplier create() {
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/sift/DiscriminatingMarker.java
class DiscriminatingMarker (line 17) | public class DiscriminatingMarker extends TerseBasicMarker implements Di...
method DiscriminatingMarker (line 22) | public DiscriminatingMarker(Function<ILoggingEvent, String> discrimina...
method getDiscriminatingValue (line 27) | @Override
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/sift/DiscriminatingMarkerFactory.java
class DiscriminatingMarkerFactory (line 16) | public class DiscriminatingMarkerFactory {
method DiscriminatingMarkerFactory (line 20) | public DiscriminatingMarkerFactory(Function<ILoggingEvent, String> dis...
method create (line 24) | public static DiscriminatingMarkerFactory create(
method createMarker (line 29) | public DiscriminatingMarker createMarker() {
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/sift/DiscriminatingValue.java
type DiscriminatingValue (line 15) | public interface DiscriminatingValue {
method getDiscriminatingValue (line 16) | String getDiscriminatingValue(ILoggingEvent loggingEvent);
FILE: logback-classic/src/main/java/com/tersesystems/logback/classic/sift/MarkerBasedDiscriminator.java
class MarkerBasedDiscriminator (line 25) | public class MarkerBasedDiscriminator<LoggingEventT extends ILoggingEvent>
method getDiscriminatingValue (line 31) | @Override
method getDiscriminatorMarker (line 37) | public Optional<DiscriminatingValue> getDiscriminatorMarker(ILoggingEv...
method fromMarker (line 41) | static Optional<DiscriminatingValue> fromMarker(Marker m) {
method getKey (line 59) | @Override
method setKey (line 64) | public void setKey(String key) {
method getDefaultValue (line 68) | public String getDefaultValue() {
method setDefaultValue (line 72) | public void setDefaultValue(String defaultValue) {
FILE: logback-classic/src/test/java/com/tersesystems/logback/classic/ChangeLogLevelTest.java
class ChangeLogLevelTest (line 15) | public class ChangeLogLevelTest {
method testChangeLogLevel (line 17) | @Test
FILE: logback-classic/src/test/java/com/tersesystems/logback/classic/CorrelationIdMarker.java
type CorrelationIdMarker (line 17) | public interface CorrelationIdMarker extends Marker {
method getCorrelationId (line 18) | String getCorrelationId();
method create (line 20) | static CorrelationIdMarker create(String value) {
class CorrelationIdBasicMarker (line 26) | class CorrelationIdBasicMarker extends TerseBasicMarker implements Corre...
method CorrelationIdBasicMarker (line 29) | public CorrelationIdBasicMarker(String value) {
method getCorrelationId (line 34) | public String getCorrelationId() {
FILE: logback-classic/src/test/java/com/tersesystems/logback/classic/CorrelationIdTurboFilter.java
class CorrelationIdTurboFilter (line 25) | public class CorrelationIdTurboFilter extends TurboFilter {
method getMdcKey (line 29) | public String getMdcKey() {
method setMdcKey (line 33) | public void setMdcKey(String mdcKey) {
method start (line 37) | @Override
method doMarker (line 43) | boolean doMarker(Marker m, Predicate<Marker> predicate) {
method decide (line 61) | @Override
FILE: logback-classic/src/test/java/com/tersesystems/logback/classic/EnabledFilterTest.java
class EnabledFilterTest (line 20) | public class EnabledFilterTest {
method testFilterFalse (line 21) | @Test
method testFilterTrue (line 32) | @Test
FILE: logback-classic/src/test/java/com/tersesystems/logback/classic/ExceptionMessageConverterTest.java
class ExceptionMessageConverterTest (line 21) | public class ExceptionMessageConverterTest {
method testNoException (line 23) | @Test
method testSingleMessage (line 37) | @Test
method testNestedMessages (line 53) | @Test
method testNestedMessagesWithCutOff (line 72) | @Test
method testNestedMessagesSeperator (line 92) | @Test
method testCustomPrefixSuffix (line 112) | @Test
FILE: logback-classic/src/test/java/com/tersesystems/logback/classic/SetLoggerLevelsActionTest.java
class SetLoggerLevelsActionTest (line 13) | public class SetLoggerLevelsActionTest {}
FILE: logback-classic/src/test/java/com/tersesystems/logback/classic/TapFilterTest.java
class TapFilterTest (line 32) | public class TapFilterTest {
method clearMDC (line 34) | @Before
method testSimple (line 40) | @Test
method testCorrelationWithNoMarker (line 58) | @Test
method testCorrelationWithMarker (line 76) | @Test
method testCorrelationWithMDC (line 96) | @Test
method createLoggerFactory (line 117) | LoggerContext createLoggerFactory(String resourceName) throws JoranExc...
method getFilterAppender (line 126) | Optional<Appender<ILoggingEvent>> getFilterAppender(TurboFilterList tu...
method getListAppender (line 133) | ListAppender<ILoggingEvent> getListAppender(LoggerContext context) {
FILE: logback-classic/src/test/java/com/tersesystems/logback/classic/TerseHighlightConverterTest.java
class TerseHighlightConverterTest (line 22) | public class TerseHighlightConverterTest {
method testHighlighter (line 24) | @Test
FILE: logback-classic/src/test/java/com/tersesystems/logback/classic/UtilsTest.java
class UtilsTest (line 24) | public class UtilsTest {
class FancyTurboFilter (line 26) | static class FancyTurboFilter extends TurboFilter {
method decide (line 27) | @Override
method testTurboFilterMatchingType (line 34) | @Test
method testTurboFilterNonMatchingType (line 46) | @Test
FILE: logback-compress-encoder/src/main/java/com.tersesystems.logback.compress/CompressingEncoder.java
class CompressingEncoder (line 22) | public class CompressingEncoder<E> extends EncoderBase<E> {
method CompressingEncoder (line 26) | public CompressingEncoder(
method headerBytes (line 33) | @Override
method encode (line 42) | @Override
method footerBytes (line 51) | @Override
class Accumulator (line 60) | static class Accumulator {
method Accumulator (line 66) | public Accumulator(String compressAlgo, CompressorStreamFactory fact...
method isFlushable (line 73) | boolean isFlushable() {
method apply (line 77) | byte[] apply(byte[] bytes) throws IOException {
method drain (line 92) | byte[] drain(byte[] inputBytes) throws IOException {
FILE: logback-compress-encoder/src/main/java/com.tersesystems.logback.compress/CompressingFileAppender.java
class CompressingFileAppender (line 20) | public class CompressingFileAppender<E> extends UnsynchronizedAppenderBa...
method getEncoder (line 36) | public Encoder<E> getEncoder() {
method setEncoder (line 40) | public void setEncoder(Encoder<E> encoder) {
method isPrudent (line 44) | public boolean isPrudent() {
method setPrudent (line 48) | public void setPrudent(boolean prudent) {
method setAppend (line 52) | public void setAppend(boolean append) {
method setFile (line 56) | public void setFile(String file) {
method isAppend (line 60) | public boolean isAppend() {
method getFile (line 64) | public String getFile() {
method getBufferSize (line 68) | public int getBufferSize() {
method setBufferSize (line 72) | public void setBufferSize(int bufferSize) {
method getCompressAlgo (line 76) | public String getCompressAlgo() {
method setCompressAlgo (line 80) | public void setCompressAlgo(String compressAlgo) {
method start (line 84) | @Override
method stop (line 101) | public void stop() {
method append (line 106) | @Override
method createCompressingEncoder (line 111) | protected CompressingEncoder<E> createCompressingEncoder(Encoder<E> e) {
FILE: logback-compress-encoder/src/test/java/com/tersesystems/logback/compress/Utils.java
class Utils (line 17) | public class Utils {
method readAllBytes (line 19) | public static byte[] readAllBytes(InputStream inputStream) throws IOEx...
FILE: logback-core/src/main/java/com/tersesystems/logback/core/AbstractAppender.java
class AbstractAppender (line 24) | public abstract class AbstractAppender<E> extends UnsynchronizedAppender...
method appendEvent (line 29) | protected abstract E appendEvent(E eventObject);
method append (line 31) | @Override
method postAppend (line 38) | protected void postAppend() {}
method preAppend (line 40) | protected void preAppend() {}
method addAppender (line 42) | public void addAppender(Appender<E> newAppender) {
method iteratorForAppenders (line 47) | public Iterator<Appender<E>> iteratorForAppenders() {
method stop (line 51) | public void stop() {
method getAppender (line 56) | public Appender<E> getAppender(String name) {
method isAttached (line 60) | public boolean isAttached(Appender<E> eAppender) {
method detachAndStopAllAppenders (line 64) | public void detachAndStopAllAppenders() {
method detachAppender (line 68) | public boolean detachAppender(Appender<E> eAppender) {
method detachAppender (line 72) | public boolean detachAppender(String name) {
FILE: logback-core/src/main/java/com/tersesystems/logback/core/Component.java
type Component (line 15) | public interface Component {}
FILE: logback-core/src/main/java/com/tersesystems/logback/core/ComponentContainer.java
type ComponentContainer (line 19) | public interface ComponentContainer {
method putComponent (line 20) | <T> void putComponent(Class<T> type, T instance);
method getComponent (line 22) | <T> T getComponent(Class<T> type);
method hasComponent (line 24) | <T> boolean hasComponent(Class<T> type);
FILE: logback-core/src/main/java/com/tersesystems/logback/core/CompositeAppender.java
class CompositeAppender (line 27) | public class CompositeAppender<E> extends UnsynchronizedAppenderBase<E>
method stop (line 32) | @Override
method append (line 40) | @Override
method addAppender (line 45) | public void addAppender(Appender<E> newAppender) {
method iteratorForAppenders (line 50) | public Iterator<Appender<E>> iteratorForAppenders() {
method getAppender (line 54) | public Appender<E> getAppender(String name) {
method isAttached (line 58) | public boolean isAttached(Appender<E> eAppender) {
method detachAndStopAllAppenders (line 62) | public void detachAndStopAllAppenders() {
method detachAppender (line 66) | public boolean detachAppender(Appender<E> eAppender) {
method detachAppender (line 70) | public boolean detachAppender(String name) {
FILE: logback-core/src/main/java/com/tersesystems/logback/core/DecoratingAppender.java
class DecoratingAppender (line 26) | public abstract class DecoratingAppender<E, EE extends E> extends Unsync...
method decorateEvent (line 31) | protected abstract EE decorateEvent(E eventObject);
method append (line 33) | @Override
method addAppender (line 38) | public void addAppender(Appender<EE> newAppender) {
method iteratorForAppenders (line 43) | public Iterator<Appender<EE>> iteratorForAppenders() {
method getAppender (line 47) | public Appender<EE> getAppender(String name) {
method isAttached (line 51) | public boolean isAttached(Appender<EE> eAppender) {
method detachAndStopAllAppenders (line 55) | public void detachAndStopAllAppenders() {
method detachAppender (line 59) | public boolean detachAppender(Appender<EE> eAppender) {
method detachAppender (line 63) | public boolean detachAppender(String name) {
method stop (line 67) | public void stop() {
FILE: logback-core/src/main/java/com/tersesystems/logback/core/DefaultAppenderAttachable.java
type DefaultAppenderAttachable (line 18) | public interface DefaultAppenderAttachable<E> extends AppenderAttachable...
method appenderAttachableImpl (line 20) | AppenderAttachableImpl<E> appenderAttachableImpl();
method addAppender (line 22) | default void addAppender(Appender<E> newAppender) {
method iteratorForAppenders (line 26) | default Iterator<Appender<E>> iteratorForAppenders() {
method getAppender (line 30) | default Appender<E> getAppender(String name) {
method isAttached (line 34) | default boolean isAttached(Appender<E> eAppender) {
method detachAndStopAllAppenders (line 38) | default void detachAndStopAllAppenders() {
method detachAppender (line 42) | default boolean detachAppender(Appender<E> eAppender) {
method detachAppender (line 46) | default boolean detachAppender(String name) {
FILE: logback-core/src/main/java/com/tersesystems/logback/core/EnabledFilter.java
class EnabledFilter (line 17) | public class EnabledFilter<E> extends Filter<E> {
method decide (line 21) | @Override
method isEnabled (line 30) | public boolean isEnabled() {
method setEnabled (line 34) | public void setEnabled(boolean enabled) {
FILE: logback-core/src/main/java/com/tersesystems/logback/core/SelectAppender.java
class SelectAppender (line 20) | public class SelectAppender<E> extends AppenderBase<E> implements Append...
method start (line 26) | @Override
method stop (line 35) | @Override
method isStarted (line 43) | @Override
method append (line 48) | @Override
method getAppenderKey (line 58) | public String getAppenderKey() {
method setAppenderKey (line 62) | public void setAppenderKey(String appenderKey) {
method addAppender (line 66) | @Override
method iteratorForAppenders (line 71) | @Override
method getAppender (line 76) | @Override
method isAttached (line 81) | @Override
method detachAndStopAllAppenders (line 86) | @Override
method detachAppender (line 91) | @Override
method detachAppender (line 96) | @Override
FILE: logback-core/src/main/java/com/tersesystems/logback/core/encoder/LayoutWrappingEncoder.java
class LayoutWrappingEncoder (line 37) | public class LayoutWrappingEncoder<E> extends EncoderBase<E> {
method getLayout (line 53) | public Layout<E> getLayout() {
method setLayout (line 57) | public void setLayout(Layout<E> layout) {
method getCharset (line 61) | public Charset getCharset() {
method setCharset (line 73) | public void setCharset(Charset charset) {
method setImmediateFlush (line 84) | public void setImmediateFlush(boolean immediateFlush) {
method headerBytes (line 91) | @Override
method footerBytes (line 107) | @Override
method convertToBytes (line 117) | private byte[] convertToBytes(String s) {
method encode (line 125) | public byte[] encode(E event) {
method isStarted (line 130) | public boolean isStarted() {
method start (line 134) | public void start() {
method stop (line 150) | public void stop() {
method appendIfNotNull (line 154) | private void appendIfNotNull(StringBuilder sb, String s) {
method setParent (line 162) | public void setParent(Object parent) {
FILE: logback-core/src/main/java/com/tersesystems/logback/core/pattern/PatternLayoutEncoderBase.java
class PatternLayoutEncoderBase (line 34) | public class PatternLayoutEncoderBase<E> extends LayoutWrappingEncoder<E> {
method getPattern (line 41) | public String getPattern() {
method setPattern (line 45) | public void setPattern(String pattern) {
method isOutputPatternAsHeader (line 49) | public boolean isOutputPatternAsHeader() {
method setOutputPatternAsHeader (line 59) | public void setOutputPatternAsHeader(boolean outputPatternAsHeader) {
method isOutputPatternAsPresentationHeader (line 63) | public boolean isOutputPatternAsPresentationHeader() {
method setOutputPatternAsPresentationHeader (line 70) | public void setOutputPatternAsPresentationHeader(boolean outputPattern...
method setLayout (line 76) | @Override
FILE: logback-core/src/test/java/com/tersesystems/logback/core/CompositeAppenderTest.java
class CompositeAppenderTest (line 24) | public class CompositeAppenderTest {
method testSimpleAppender (line 26) | @Test
FILE: logback-core/src/test/java/com/tersesystems/logback/core/SelectAppenderTest.java
class SelectAppenderTest (line 24) | public class SelectAppenderTest {
method testWithTestEnvironment (line 26) | @Test
method testWithDevelopmentEnvironment (line 46) | @Test
FILE: logback-core/src/test/java/com/tersesystems/logback/core/TestAppender.java
class TestAppender (line 19) | public class TestAppender extends AppenderBase<ILoggingEvent> {
method getEncoder (line 25) | public Encoder<ILoggingEvent> getEncoder() {
method setEncoder (line 29) | public void setEncoder(Encoder<ILoggingEvent> encoder) {
method append (line 33) | @Override
FILE: logback-correlationid/src/main/java/com/tersesystems/logback/correlationid/CorrelationIdDecider.java
class CorrelationIdDecider (line 21) | public class CorrelationIdDecider implements TurboFilterDecider {
method CorrelationIdDecider (line 24) | public CorrelationIdDecider(CorrelationIdUtils utils) {
method decide (line 28) | @Override
FILE: logback-correlationid/src/main/java/com/tersesystems/logback/correlationid/CorrelationIdFilter.java
class CorrelationIdFilter (line 19) | public class CorrelationIdFilter extends Filter<ILoggingEvent> {
method getMdcKey (line 22) | public String getMdcKey() {
method setMdcKey (line 26) | public void setMdcKey(String mdcKey) {
method start (line 32) | @Override
method decide (line 38) | @Override
FILE: logback-correlationid/src/main/java/com/tersesystems/logback/correlationid/CorrelationIdMarker.java
type CorrelationIdMarker (line 18) | public interface CorrelationIdMarker extends Marker, CorrelationIdProvid...
method create (line 19) | static CorrelationIdMarker create(String value) {
class CorrelationIdBasicMarker (line 25) | class CorrelationIdBasicMarker extends TerseBasicMarker implements Corre...
method CorrelationIdBasicMarker (line 28) | public CorrelationIdBasicMarker(String value) {
method getCorrelationId (line 33) | public String getCorrelationId() {
FILE: logback-correlationid/src/main/java/com/tersesystems/logback/correlationid/CorrelationIdProvider.java
type CorrelationIdProvider (line 17) | public interface CorrelationIdProvider extends Component {
method getCorrelationId (line 18) | String getCorrelationId();
FILE: logback-correlationid/src/main/java/com/tersesystems/logback/correlationid/CorrelationIdTapFilter.java
class CorrelationIdTapFilter (line 23) | public class CorrelationIdTapFilter extends TapFilter {
method getMdcKey (line 27) | public String getMdcKey() {
method setMdcKey (line 31) | public void setMdcKey(String mdcKey) {
method start (line 35) | @Override
method decide (line 41) | @Override
FILE: logback-correlationid/src/main/java/com/tersesystems/logback/correlationid/CorrelationIdUtils.java
class CorrelationIdUtils (line 23) | public class CorrelationIdUtils {
method CorrelationIdUtils (line 27) | public CorrelationIdUtils(String mdcKey) {
method get (line 31) | public Optional<String> get(Map<String, String> mdcPropertyMap, Marker...
method fromMarker (line 41) | public Optional<String> fromMarker(Marker m) {
method get (line 61) | public Optional<String> get(Map<String, String> mdcPropertyMap) {
method getMDCPropertyMap (line 72) | public Map<String, String> getMDCPropertyMap() {
FILE: logback-correlationid/src/test/java/com.tersesystems.logback.correlationid/CorrelationIdFilterTest.java
class CorrelationIdFilterTest (line 32) | public class CorrelationIdFilterTest {
method clearMDC (line 33) | @Before
method testFilter (line 39) | @Test
method createLoggerFactory (line 63) | LoggerContext createLoggerFactory(String resourceName) throws JoranExc...
method getListAppender (line 72) | ListAppender<ILoggingEvent> getListAppender(LoggerContext context) {
method getFirstAppender (line 82) | private Optional<Appender<ILoggingEvent>> getFirstAppender(Logger logg...
FILE: logback-correlationid/src/test/java/com.tersesystems.logback.correlationid/CorrelationIdTapFilterTest.java
class CorrelationIdTapFilterTest (line 33) | public class CorrelationIdTapFilterTest {
method clearMDC (line 35) | @Before
method testCorrelationWithNoMarker (line 41) | @Test
method testCorrelationWithMarker (line 60) | @Test
method testCorrelationWithMDC (line 81) | @Test
method createLoggerFactory (line 103) | LoggerContext createLoggerFactory(String resourceName) throws JoranExc...
method getFilterAppender (line 112) | Optional<Appender<ILoggingEvent>> getFilterAppender(TurboFilterList tu...
method getListAppender (line 119) | ListAppender<ILoggingEvent> getListAppender(LoggerContext context) {
FILE: logback-exception-mapping-providers/src/main/java/com/tersesystems/logback/exceptionmapping/config/TypesafeConfigMappingsAction.java
class TypesafeConfigMappingsAction (line 26) | public class TypesafeConfigMappingsAction extends Action {
method getRegistry (line 28) | @SuppressWarnings("unchecked")
method begin (line 43) | @Override
method getConfig (line 71) | private Config getConfig(InterpretationContext ic) {
method end (line 83) | @Override
method getMappingsFromConfig (line 86) | public Map<String, List<String>> getMappingsFromConfig(Config config, ...
FILE: logback-exception-mapping-providers/src/main/java/com/tersesystems/logback/exceptionmapping/json/ExceptionArgumentsProvider.java
class ExceptionArgumentsProvider (line 27) | public class ExceptionArgumentsProvider extends AbstractFieldJsonProvide...
method getRegistry (line 29) | @SuppressWarnings("unchecked")
method writeTo (line 48) | @Override
method writeExceptionIfNecessary (line 53) | private void writeExceptionIfNecessary(JsonGenerator generator, IThrow...
method renderException (line 82) | private void renderException(
FILE: logback-exception-mapping-providers/src/test/java/com/tersesystems/logback/exceptionmapping/json/ExceptionArgumentsProviderTest.java
class ExceptionArgumentsProviderTest (line 32) | public class ExceptionArgumentsProviderTest {
method testProvider (line 34) | @Test
method createExceptionMappingRegistry (line 62) | private void createExceptionMappingRegistry(LoggerContext context) {
method mkLoggingEvent (line 69) | private ILoggingEvent mkLoggingEvent(LoggerContext context) {
method mkJsonGenerator (line 74) | private JsonGenerator mkJsonGenerator(StringWriter writer) throws IOEx...
FILE: logback-exception-mapping-providers/src/test/java/com/tersesystems/logback/exceptionmapping/json/MySpecialException.java
class MySpecialException (line 15) | public class MySpecialException extends Exception {
method MySpecialException (line 19) | public MySpecialException(String message, Instant timestamp) {
method MySpecialException (line 24) | public MySpecialException(String message, Instant timestamp, Throwable...
method getTimestamp (line 29) | public Instant getTimestamp() {
FILE: logback-exception-mapping-providers/src/test/java/com/tersesystems/logback/exceptionmapping/json/TypesafeConfigMappingsActionTest.java
class TypesafeConfigMappingsActionTest (line 26) | public class TypesafeConfigMappingsActionTest {
method setUp (line 31) | @Before
method testConfig (line 36) | @Test
FILE: logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/BeanExceptionMapping.java
class BeanExceptionMapping (line 20) | public class BeanExceptionMapping implements ExceptionMapping {
method BeanExceptionMapping (line 25) | public BeanExceptionMapping(
method getName (line 32) | @Override
method apply (line 37) | @Override
method findMethod (line 44) | protected Stream<ExceptionProperty> findMethod(Throwable e, String met...
FILE: logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/Constants.java
class Constants (line 13) | public final class Constants {
FILE: logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/DefaultExceptionMappingRegistry.java
class DefaultExceptionMappingRegistry (line 22) | public class DefaultExceptionMappingRegistry implements ExceptionMapping...
method DefaultExceptionMappingRegistry (line 26) | public DefaultExceptionMappingRegistry(Consumer<Exception> exceptionHa...
method register (line 34) | @Override
method register (line 41) | @Override
method register (line 52) | @Override
method register (line 57) | @SuppressWarnings("unchecked")
method register (line 69) | @Override
method register (line 76) | @SuppressWarnings("unchecked")
method register (line 85) | @Override
method register (line 93) | @Override
method apply (line 101) | @Override
method iterator (line 119) | @Override
method get (line 124) | @Override
method contains (line 129) | @Override
method contains (line 134) | @Override
method remove (line 139) | @Override
method remove (line 144) | @Override
FILE: logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/ExceptionCauseIterator.java
class ExceptionCauseIterator (line 19) | public class ExceptionCauseIterator implements Iterator<Throwable> {
method ExceptionCauseIterator (line 22) | ExceptionCauseIterator(Throwable throwable) {
method hasNext (line 26) | @Override
method next (line 31) | @SuppressWarnings("unchecked")
method stream (line 41) | @SuppressWarnings("unchecked")
method create (line 47) | public static ExceptionCauseIterator create(Throwable throwable) {
FILE: logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/ExceptionHierarchyIterator.java
class ExceptionHierarchyIterator (line 19) | public class ExceptionHierarchyIterator implements Iterator<Class<?>> {
method ExceptionHierarchyIterator (line 22) | ExceptionHierarchyIterator(Class<?> clazz) {
method hasNext (line 26) | @Override
method next (line 31) | @SuppressWarnings("unchecked")
method stream (line 41) | @SuppressWarnings("unchecked")
method create (line 47) | public static ExceptionHierarchyIterator create(Class<?> clazz) {
FILE: logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/ExceptionMapping.java
type ExceptionMapping (line 16) | public interface ExceptionMapping extends Function<Throwable, List<Excep...
method getName (line 17) | String getName();
FILE: logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/ExceptionMappingAction.java
class ExceptionMappingAction (line 22) | public class ExceptionMappingAction extends Action {
method begin (line 27) | @SuppressWarnings("unchecked")
method end (line 75) | public void end(InterpretationContext ec, String n) {}
FILE: logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/ExceptionMappingRegistry.java
type ExceptionMappingRegistry (line 16) | public interface ExceptionMappingRegistry {
method register (line 18) | void register(Map<String, List<String>> mappers);
method register (line 20) | void register(ClassLoader classLoader, Map<String, List<String>> mappe...
method register (line 22) | void register(String className, String... methodNames);
method register (line 24) | void register(ClassLoader classLoader, String className, String... met...
method register (line 26) | <E extends Throwable> void register(Class<E> exceptionClass, String......
method register (line 28) | <E extends Throwable> void register(
method register (line 31) | void register(String className, Function<Throwable, List<ExceptionProp...
method register (line 33) | void register(ExceptionMapping mapper);
method apply (line 35) | List<ExceptionProperty> apply(Throwable e);
method iterator (line 37) | Iterator<ExceptionMapping> iterator();
method get (line 39) | ExceptionMapping get(String name);
method contains (line 41) | boolean contains(ExceptionMapping exceptionMapping);
method contains (line 43) | boolean contains(String name);
method remove (line 45) | boolean remove(ExceptionMapping exceptionMapping);
method remove (line 47) | boolean remove(String name);
FILE: logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/ExceptionMappingRegistryAction.java
class ExceptionMappingRegistryAction (line 32) | public class ExceptionMappingRegistryAction extends Action {
method begin (line 38) | @SuppressWarnings("unchecked")
method initializeRegistry (line 77) | protected void initializeRegistry(ExceptionMappingRegistry registry) {
method initialMappings (line 85) | protected List<ExceptionMapping> initialMappings() {
method complexMappings (line 138) | protected void complexMappings(ExceptionMappingRegistry mappings) {
method end (line 167) | public void end(InterpretationContext ec, String name) {
FILE: logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/ExceptionMessageWithMappingsConverter.java
class ExceptionMessageWithMappingsConverter (line 22) | public class ExceptionMessageWithMappingsConverter extends ExceptionMess...
method constructMessage (line 24) | @Override
method getMappingsKey (line 31) | private String getMappingsKey() {
method findArgumentMappings (line 35) | private String findArgumentMappings(IThrowableProxy ex) {
method format (line 48) | private String format(List<ExceptionProperty> args) {
method getRegistry (line 61) | @SuppressWarnings("unchecked")
class StringBufferExceptionPropertyWriter (line 79) | class StringBufferExceptionPropertyWriter {
method StringBufferExceptionPropertyWriter (line 82) | StringBufferExceptionPropertyWriter(StringBuilder sb) {
method write (line 86) | public void write(ExceptionProperty exceptionProperty) {
FILE: logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/ExceptionProperty.java
type ExceptionProperty (line 15) | public interface ExceptionProperty {
method create (line 17) | public static ExceptionProperty create(String name, String value) {
method create (line 21) | public static ExceptionProperty create(String name, Object value) {
method toString (line 25) | static String toString(Object value) {
FILE: logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/FunctionExceptionMapping.java
class FunctionExceptionMapping (line 16) | public class FunctionExceptionMapping implements ExceptionMapping {
method FunctionExceptionMapping (line 20) | public FunctionExceptionMapping(String name, Function<Throwable, List<...
method apply (line 25) | @Override
method getName (line 30) | @Override
FILE: logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/KeyValueExceptionProperty.java
class KeyValueExceptionProperty (line 15) | public class KeyValueExceptionProperty implements ExceptionProperty {
method KeyValueExceptionProperty (line 20) | KeyValueExceptionProperty(String key, String value) {
method getKey (line 25) | public String getKey() {
method getValue (line 29) | public String getValue() {
method equals (line 33) | @Override
method hashCode (line 41) | @Override
method toString (line 46) | @Override
FILE: logback-exception-mapping/src/test/java/com/tersesystems/logback/exceptionmapping/ExceptionMappingTest.java
class ExceptionMappingTest (line 28) | public class ExceptionMappingTest {
method testSimpleArgument (line 30) | @Test
method testComplexArgument (line 43) | @Test
method populate (line 64) | private void populate(DefaultExceptionMappingRegistry mappings) {
FILE: logback-exception-mapping/src/test/java/com/tersesystems/logback/exceptionmapping/MyCustomException.java
class MyCustomException (line 13) | public class MyCustomException extends RuntimeException {
method MyCustomException (line 18) | public MyCustomException(String message, String one, String two, Strin...
method getOne (line 25) | public String getOne() {
method getTwo (line 29) | public String getTwo() {
method getThree (line 33) | public String getThree() {
FILE: logback-exception-mapping/src/test/java/com/tersesystems/logback/exceptionmapping/Thrower.java
class Thrower (line 17) | public class Thrower {
method main (line 20) | public static void main(String[] progArgs) {
method doSomethingExceptional (line 28) | static void doSomethingExceptional() {
FILE: logback-honeycomb-appender/src/main/java/com/tersesystems/logback/honeycomb/HoneycombAppender.java
class HoneycombAppender (line 27) | public class HoneycombAppender extends UnsynchronizedAppenderBase<ILoggi...
method getEncoder (line 40) | public Encoder<ILoggingEvent> getEncoder() {
method setQueueSize (line 44) | public void setQueueSize(Integer queueSize) {
method setDataSet (line 48) | public void setDataSet(String dataSet) {
method setApiKey (line 52) | public void setApiKey(String apiKey) {
method setEncoder (line 56) | public void setEncoder(Encoder<ILoggingEvent> encoder) {
method setSampleRate (line 60) | public void setSampleRate(Integer sampleRate) {
method setBatch (line 64) | public void setBatch(boolean batch) {
method isIncludeCallerData (line 68) | public boolean isIncludeCallerData() {
method setIncludeCallerData (line 72) | public void setIncludeCallerData(boolean includeCallerData) {
method prepareForDeferredProcessing (line 76) | protected void prepareForDeferredProcessing(ILoggingEvent event) {
method start (line 83) | @Override
method stop (line 117) | @Override
method dumpQueue (line 134) | protected void dumpQueue() {
method append (line 148) | @Override
method postEvent (line 181) | private CompletionStage<Void> postEvent(HoneycombRequest<ILoggingEvent...
method postBatch (line 185) | private CompletionStage<Void> postBatch(
method serialize (line 190) | private byte[] serialize(HoneycombRequest<ILoggingEvent> honeycombRequ...
method clientService (line 194) | private HoneycombClientService clientService() {
method accept (line 204) | private void accept(HoneycombResponse response) {
method accept (line 218) | private void accept(List<HoneycombResponse> responses) {
FILE: logback-honeycomb-client/src/main/java/com/tersesystems/logback/honeycomb/client/HoneycombClient.java
type HoneycombClient (line 17) | public interface HoneycombClient<E> {
method post (line 19) | CompletionStage<HoneycombResponse> post(HoneycombRequest<E> request);
method post (line 21) | <F> CompletionStage<HoneycombResponse> post(
method postBatch (line 24) | CompletionStage<List<HoneycombResponse>> postBatch(Iterable<HoneycombR...
method postBatch (line 26) | <F> CompletionStage<List<HoneycombResponse>> postBatch(
method close (line 29) | CompletionStage<Void> close();
FILE: logback-honeycomb-client/src/main/java/com/tersesystems/logback/honeycomb/client/HoneycombClientService.java
type HoneycombClientService (line 16) | public interface HoneycombClientService {
method newClient (line 17) | <E> HoneycombClient<E> newClient(
FILE: logback-honeycomb-client/src/main/java/com/tersesystems/logback/honeycomb/client/HoneycombHeaders.java
class HoneycombHeaders (line 13) | public class HoneycombHeaders {
method teamHeader (line 14) | public static String teamHeader() {
method eventTimeHeader (line 18) | public static String eventTimeHeader() {
method sampleRateHeader (line 22) | public static String sampleRateHeader() {
FILE: logback-honeycomb-client/src/main/java/com/tersesystems/logback/honeycomb/client/HoneycombRequest.java
class HoneycombRequest (line 15) | public class HoneycombRequest<E> {
method HoneycombRequest (line 21) | public HoneycombRequest(Integer sampleRate, Instant timestamp, E event) {
method getEvent (line 27) | public E getEvent() {
method getSampleRate (line 31) | public Integer getSampleRate() {
method getTimestamp (line 35) | public Instant getTimestamp() {
FILE: logback-honeycomb-client/src/main/java/com/tersesystems/logback/honeycomb/client/HoneycombResponse.java
class HoneycombResponse (line 13) | public class HoneycombResponse {
method HoneycombResponse (line 18) | public HoneycombResponse(int status, String reason) {
method getStatus (line 23) | public int getStatus() {
method getReason (line 27) | public String getReason() {
method isSuccess (line 31) | public boolean isSuccess() {
method isInvalidKey (line 35) | public boolean isInvalidKey() {
method isMalformed (line 39) | public boolean isMalformed() {
method isTooLarge (line 43) | public boolean isTooLarge() {
method isRateLimited (line 47) | public boolean isRateLimited() {
method isBlacklisted (line 51) | public boolean isBlacklisted() {
method toString (line 55) | @Override
method is400 (line 60) | private boolean is400() {
method is429 (line 64) | private boolean is429() {
FILE: logback-honeycomb-okhttp/src/main/java/com/tersesystems/logback/honeycomb/okhttp/HoneycombOkHTTPClient.java
class HoneycombOkHTTPClient (line 30) | public class HoneycombOkHTTPClient<E> implements HoneycombClient<E> {
method HoneycombOkHTTPClient (line 39) | public HoneycombOkHTTPClient(
method post (line 53) | @Override
method post (line 58) | @Override
method postBatch (line 81) | @Override
method postBatch (line 87) | @Override
method eventURL (line 111) | private String eventURL(String dataset) {
method batchURL (line 116) | private String batchURL(String dataset) {
method close (line 121) | public CompletionStage<Void> close() {
method generateBatchJson (line 128) | private <F> byte[] generateBatchJson(
method isoTime (line 145) | private String isoTime(Instant eventTime) {
class HoneycombRequestFormatter (line 149) | class HoneycombRequestFormatter<F> {
method HoneycombRequestFormatter (line 152) | HoneycombRequestFormatter(JsonGenerator generator) {
method start (line 156) | void start() throws IOException {
method end (line 160) | void end() throws IOException {
method format (line 164) | void format(HoneycombRequest<F> request, Function<HoneycombRequest<F...
class OkHttpResponseFuture (line 178) | static class OkHttpResponseFuture implements Callback {
method OkHttpResponseFuture (line 181) | OkHttpResponseFuture() {}
method onFailure (line 183) | @Override
method onResponse (line 188) | @Override
class OkHttpBatchedResponseFuture (line 196) | class OkHttpBatchedResponseFuture implements Callback {
method OkHttpBatchedResponseFuture (line 199) | OkHttpBatchedResponseFuture() {}
method onFailure (line 201) | @Override
method onResponse (line 206) | @Override
method parseResponse (line 212) | private List<HoneycombResponse> parseResponse(Response wsResponse) t...
FILE: logback-honeycomb-okhttp/src/main/java/com/tersesystems/logback/honeycomb/okhttp/HoneycombOkHTTPClientService.java
class HoneycombOkHTTPClientService (line 21) | public class HoneycombOkHTTPClientService implements HoneycombClientServ...
method newClient (line 22) | @Override
FILE: logback-jdbc-appender/src/main/java/com/tersesystems/logback/jdbc/JDBCAppender.java
class JDBCAppender (line 44) | public class JDBCAppender extends UnsynchronizedAppenderBase<ILoggingEve...
method isLoggingInsert (line 71) | public boolean isLoggingInsert() {
method getUrl (line 75) | public String getUrl() {
method setUrl (line 79) | public void setUrl(String url) {
method getEncoder (line 83) | public Encoder<ILoggingEvent> getEncoder() {
method setEncoder (line 87) | public void setEncoder(Encoder<ILoggingEvent> encoder) {
method getUsername (line 91) | public String getUsername() {
method setUsername (line 95) | public void setUsername(String username) {
method getPassword (line 99) | public String getPassword() {
method setPassword (line 103) | public void setPassword(String password) {
method setPoolSize (line 107) | public void setPoolSize(int poolSize) {
method getPoolName (line 111) | public String getPoolName() {
method setPoolName (line 115) | public void setPoolName(String poolName) {
method getDriver (line 119) | public String getDriver() {
method setDriver (line 123) | public void setDriver(String driver) {
method getReaperSchedule (line 127) | public String getReaperSchedule() {
method setReaperSchedule (line 131) | public void setReaperSchedule(String reaperSchedule) {
method getReaperStatement (line 135) | public String getReaperStatement() {
method setReaperStatement (line 139) | public void setReaperStatement(String reaperStatement) {
method getCreateStatements (line 143) | public String getCreateStatements() {
method setCreateStatements (line 147) | public void setCreateStatements(String createStatements) {
method getInsertStatement (line 151) | public String getInsertStatement() {
method setInsertStatement (line 155) | public void setInsertStatement(String insertStatement) {
method start (line 159) | @Override
method stop (line 165) | @Override
method append (line 173) | @Override
method initialize (line 185) | protected void initialize() {
method createDataSource (line 200) | protected HikariDataSource createDataSource(
method shutdownThreadPool (line 217) | protected void shutdownThreadPool() {
method checkConnection (line 229) | protected void checkConnection() throws SQLException {
method createTableIfNecessary (line 242) | protected void createTableIfNecessary() {
method scheduleReaper (line 262) | protected void scheduleReaper() {
method reapOldEvents (line 287) | protected void reapOldEvents() {
method now (line 303) | protected Instant now() {
method closeConnection (line 307) | protected void closeConnection() {
method insertStatement (line 321) | protected int insertStatement(ILoggingEvent event, LongAdder adder, Pr...
method insertAdditionalData (line 345) | protected void insertAdditionalData(
method insertEvent (line 350) | protected void insertEvent(ILoggingEvent event, LongAdder adder, Prepa...
method insertStringLevel (line 356) | protected void insertStringLevel(
method insertIntLevel (line 362) | protected void insertIntLevel(ILoggingEvent event, LongAdder adder, Pr...
method insertStartTime (line 369) | protected void insertStartTime(ILoggingEvent event, LongAdder adder, P...
method insertRelativeTime (line 380) | protected void insertRelativeTime(
method insertTimestamp (line 392) | protected void insertTimestamp(ILoggingEvent event, LongAdder adder, P...
class InsertConsumer (line 399) | class InsertConsumer implements Consumer<ILoggingEvent> {
method accept (line 400) | public void accept(ILoggingEvent event) {
FILE: logback-jdbc-appender/src/test/java/com/tersesystems/logback/jdbc/JDBCAppenderTest.java
class JDBCAppenderTest (line 30) | public class JDBCAppenderTest {
method clear (line 32) | @Before
method testSimple (line 44) | @Test
method assertTablesExist (line 60) | private Boolean assertTablesExist() {
method assertRowsEntered (line 70) | public void assertRowsEntered(Integer expectedCount) throws SQLExcepti...
method getCount (line 78) | public int getCount(PreparedStatement p) throws SQLException {
method createLoggerFactory (line 88) | LoggerContext createLoggerFactory(String resourceName) throws JoranExc...
method getJDBCAppender (line 97) | JDBCAppender getJDBCAppender(LoggerContext context) {
FILE: logback-postgresjson-appender/src/main/java/com/tersesystems/logback/postgresjson/PostgresJsonAppender.java
class PostgresJsonAppender (line 22) | public class PostgresJsonAppender extends JDBCAppender {
method getObjectType (line 26) | public String getObjectType() {
method setObjectType (line 30) | public void setObjectType(String objectType) {
method start (line 34) | @Override
method insertEvent (line 40) | @Override
FILE: logback-postgresjson-appender/src/test/java/com/tersesystems/logback/postgresjson/PostgresJsonAppenderTest.java
class PostgresJsonAppenderTest (line 19) | public class PostgresJsonAppenderTest {
method testJson (line 21) | @Disabled
FILE: logback-postgresjson-appender/src/test/resources/db/migration/V1__logging_table.sql
type logging_table (line 14) | CREATE TABLE logging_table (
type idxgin (line 24) | CREATE INDEX idxgin ON logging_table USING gin (evt)
FILE: logback-tracing/src/main/java/com/tersesystems/logback/tracing/EventInfo.java
class EventInfo (line 21) | @AutoValue
method builder (line 24) | public static Builder builder() {
method toBuilder (line 28) | public abstract Builder toBuilder();
method parentId (line 30) | @Nullable
method traceId (line 33) | public abstract String traceId();
method name (line 35) | public abstract String name();
class Builder (line 37) | @AutoValue.Builder
method setName (line 39) | public abstract Builder setName(String name);
method setParentId (line 41) | public abstract Builder setParentId(String parentId);
method setTraceId (line 43) | public abstract Builder setTraceId(String traceId);
method build (line 45) | public abstract EventInfo build();
FILE: logback-tracing/src/main/java/com/tersesystems/logback/tracing/EventMarkerFactory.java
class EventMarkerFactory (line 20) | public class EventMarkerFactory {
method create (line 23) | public LogstashMarker create(EventInfo eventInfo) {
method apply (line 29) | public LogstashMarker apply(EventInfo eventInfo) {
method generateMarkers (line 33) | protected LogstashMarker[] generateMarkers(EventInfo eventInfo) {
FILE: logback-tracing/src/main/java/com/tersesystems/logback/tracing/LinkInfo.java
class LinkInfo (line 17) | @AutoValue
method builder (line 20) | public static Builder builder() {
method toBuilder (line 24) | public abstract Builder toBuilder();
method parentId (line 26) | @Nullable
method traceId (line 29) | public abstract String traceId();
method linkedSpanId (line 31) | public abstract String linkedSpanId();
method linkedTraceId (line 33) | public abstract String linkedTraceId();
class Builder (line 35) | @AutoValue.Builder
method setLinkedSpanId (line 38) | public abstract Builder setLinkedSpanId(String linkedSpanId);
method setLinkedTraceId (line 40) | public abstract Builder setLinkedTraceId(String linkedTraceId);
method setParentId (line 42) | public abstract Builder setParentId(String parentId);
method setTraceId (line 44) | public abstract Builder setTraceId(String traceId);
method build (line 46) | public abstract LinkInfo build();
FILE: logback-tracing/src/main/java/com/tersesystems/logback/tracing/LinkMarkerFactory.java
class LinkMarkerFactory (line 17) | public class LinkMarkerFactory {
method create (line 20) | public LogstashMarker create(LinkInfo linkInfo) {
method apply (line 26) | public LogstashMarker apply(LinkInfo linkInfo) {
method generateMarkers (line 30) | protected LogstashMarker[] generateMarkers(LinkInfo linkInfo) {
FILE: logback-tracing/src/main/java/com/tersesystems/logback/tracing/SpanInfo.java
class SpanInfo (line 19) | @AutoValue
method builder (line 22) | public static Builder builder() {
method toBuilder (line 26) | public abstract Builder toBuilder();
method spanId (line 28) | public abstract String spanId();
method parentId (line 30) | @Nullable
method traceId (line 33) | public abstract String traceId();
method name (line 35) | public abstract String name();
method serviceName (line 37) | public abstract String serviceName();
method startTime (line 39) | public abstract Instant startTime();
method duration (line 41) | public Duration duration() {
method durationSupplier (line 45) | public abstract Supplier<Duration> durationSupplier();
method idGenerator (line 47) | public abstract Supplier<String> idGenerator();
method childBuilder (line 55) | public Builder childBuilder() {
method withChild (line 74) | public <T> T withChild(String methodName, Function<SpanInfo, T> childF...
class Builder (line 78) | @AutoValue.Builder
method setName (line 80) | public abstract Builder setName(String name);
method setSpanId (line 82) | public abstract Builder setSpanId(String spanId);
method setParentId (line 84) | public abstract Builder setParentId(String parentId);
method setTraceId (line 86) | public abstract Builder setTraceId(String traceId);
method setIdGenerator (line 88) | public abstract Builder setIdGenerator(Supplier<String> idGenerator);
method setStartTime (line 90) | public abstract Builder setStartTime(Instant startTime);
method setServiceName (line 92) | public abstract Builder setServiceName(String serviceName);
method setDurationSupplier (line 94) | public abstract Builder setDurationSupplier(Supplier<Duration> durat...
method build (line 96) | public abstract SpanInfo build();
method setRootSpan (line 105) | public Builder setRootSpan(Supplier<String> idGenerator, String name) {
method startNow (line 112) | public Builder startNow() {
method buildNow (line 124) | public SpanInfo buildNow() {
FILE: logback-tracing/src/main/java/com/tersesystems/logback/tracing/SpanMarkerFactory.java
class SpanMarkerFactory (line 20) | public class SpanMarkerFactory {
method create (line 23) | public LogstashMarker create(SpanInfo spanInfo) {
method apply (line 30) | public LogstashMarker apply(SpanInfo spanInfo) {
method generateMarkers (line 34) | protected LogstashMarker[] generateMarkers(SpanInfo spanInfo) {
FILE: logback-tracing/src/main/java/com/tersesystems/logback/tracing/Tracer.java
class Tracer (line 18) | public class Tracer {
method stack (line 27) | private static Deque<SpanInfo> stack() {
method popSpan (line 31) | public static Optional<SpanInfo> popSpan() {
method pushEvent (line 43) | public static Optional<EventInfo> pushEvent(String name) {
method pushSpan (line 68) | public static Optional<SpanInfo> pushSpan(
method activeSpan (line 87) | public static Optional<SpanInfo> activeSpan() {
method clear (line 92) | public static void clear() {
FILE: logback-turbomarker/src/main/java/com/tersesystems/logback/turbomarker/ContextAwareTurboFilterDecider.java
type ContextAwareTurboFilterDecider (line 18) | public interface ContextAwareTurboFilterDecider<C> {
method decide (line 19) | FilterReply decide(
FILE: logback-turbomarker/src/main/java/com/tersesystems/logback/turbomarker/ContextAwareTurboMarker.java
class ContextAwareTurboMarker (line 27) | public class ContextAwareTurboMarker<C> extends TurboMarker implements T...
method ContextAwareTurboMarker (line 32) | public ContextAwareTurboMarker(
method getContextAwareDecider (line 39) | ContextAwareTurboFilterDecider<C> getContextAwareDecider() {
method getContext (line 43) | C getContext() {
method decide (line 47) | @Override
FILE: logback-turbomarker/src/main/java/com/tersesystems/logback/turbomarker/ContextDecider.java
type ContextDecider (line 19) | public interface ContextDecider<C>
method decide (line 21) | @Override
FILE: logback-turbomarker/src/main/java/com/tersesystems/logback/turbomarker/LoggerContextDecider.java
type LoggerContextDecider (line 19) | @FunctionalInterface
method decide (line 22) | default FilterReply decide(
FILE: logback-turbomarker/src/main/java/com/tersesystems/logback/turbomarker/MarkerContextDecider.java
type MarkerContextDecider (line 19) | @FunctionalInterface
method decide (line 23) | @Override
FILE: logback-turbomarker/src/main/java/com/tersesystems/logback/turbomarker/TurboMarker.java
class TurboMarker (line 20) | public abstract class TurboMarker extends TerseBasicMarker implements Tu...
method TurboMarker (line 21) | public TurboMarker(String name) {
FILE: logback-turbomarker/src/main/java/com/tersesystems/logback/turbomarker/TurboMarkerTurboFilter.java
class TurboMarkerTurboFilter (line 30) | public class TurboMarkerTurboFilter extends TurboFilter implements Turbo...
method decide (line 32) | @Override
method evaluateMarker (line 55) | private FilterReply evaluateMarker(
method stream (line 70) | @SuppressWarnings("unchecked")
FILE: logback-turbomarker/src/test/java/com/tersesystems/logback/turbomarker/ApplicationContext.java
class ApplicationContext (line 13) | public class ApplicationContext {
method ApplicationContext (line 17) | public ApplicationContext(String userId) {
method currentUserId (line 21) | public String currentUserId() {
FILE: logback-turbomarker/src/test/java/com/tersesystems/logback/turbomarker/DiagnosticLoggingExample.java
class DiagnosticLoggingExample (line 27) | public class DiagnosticLoggingExample {
method main (line 29) | public static void main(String[] args) {
class Order (line 51) | static class Order {
method Order (line 57) | public Order(String id, OrderDiagnosticLogging diagnostics) {
method getId (line 62) | public String getId() {
method addToCart (line 66) | public void addToCart(LineItem lineItem) {
method addPayment (line 70) | public void addPayment(Payment payment) {
method addShipping (line 74) | public void addShipping(Shipping shipping) {
method checkout (line 78) | public void checkout() {
method fulfill (line 82) | public void fulfill() {
method complete (line 86) | public void complete() {
method toString (line 90) | @Override
class OrderDiagnosticLogging (line 96) | static class OrderDiagnosticLogging {
method OrderDiagnosticLogging (line 100) | OrderDiagnosticLogging(Logger logger, Marker marker) {
method reportAddToCart (line 105) | void reportAddToCart(Order order, LineItem lineItem) {
method reportAddPayment (line 109) | void reportAddPayment(Order order, Payment payment) {
method reportAddShipping (line 113) | void reportAddShipping(Order order, Shipping shipping) {
method reportCheckout (line 117) | void reportCheckout(Order order) {
method reportFulfill (line 121) | void reportFulfill(Order order) {
method reportComplete (line 125) | void reportComplete(Order order) {
method reportArg (line 129) | private void reportArg(String methodName, Order order, StructuredArg...
method report (line 135) | private void report(String methodName, Order order) {
class Payment (line 142) | private static class Payment {
method toString (line 143) | @Override
class Shipping (line 149) | private static class Shipping {
method toString (line 151) | @Override
class LineItem (line 157) | private static class LineItem {
method toString (line 159) | @Override
FILE: logback-turbomarker/src/test/java/com/tersesystems/logback/turbomarker/LDMarkerFactory.java
class LDMarkerFactory (line 20) | public class LDMarkerFactory {
method LDMarkerFactory (line 23) | public LDMarkerFactory(LDClientInterface client) {
method create (line 27) | public LDMarker create(String featureFlag, LDUser user) {
class LaunchDarklyDecider (line 31) | static class LaunchDarklyDecider implements MarkerContextDecider<LDUse...
method LaunchDarklyDecider (line 34) | LaunchDarklyDecider(LDClientInterface ldClient) {
method apply (line 38) | @Override
class LDMarker (line 46) | public static class LDMarker extends ContextAwareTurboMarker<LDUser> {
method LDMarker (line 47) | LDMarker(String name, LDUser context, ContextAwareTurboFilterDecider...
FILE: logback-turbomarker/src/test/java/com/tersesystems/logback/turbomarker/LDMarkerTest.java
class LDMarkerTest (line 28) | public class LDMarkerTest {
method testMatchingMarker (line 30) | @Test
method testNonMatchingUserMarker (line 62) | @Test
FILE: logback-turbomarker/src/test/java/com/tersesystems/logback/turbomarker/UserMarker.java
class UserMarker (line 13) | public class UserMarker extends ContextAwareTurboMarker<ApplicationConte...
method UserMarker (line 14) | public UserMarker(
FILE: logback-turbomarker/src/test/java/com/tersesystems/logback/turbomarker/UserMarkerFactory.java
class UserMarkerFactory (line 17) | public class UserMarkerFactory {
method addUserId (line 25) | public void addUserId(String userId) {
method clear (line 29) | public void clear() {
method create (line 33) | public UserMarker create(ApplicationContext applicationContext) {
FILE: logback-turbomarker/src/test/java/com/tersesystems/logback/turbomarker/UserMarkerTest.java
class UserMarkerTest (line 22) | public class UserMarkerTest {
method testMatchingUserMarker (line 24) | @Test
method testNonMatchingUserMarker (line 46) | @Test
FILE: logback-typesafe-config/src/main/java/com/tersesystems/logback/typesafeconfig/ConfigConstants.java
class ConfigConstants (line 14) | public final class ConfigConstants {
FILE: logback-typesafe-config/src/main/java/com/tersesystems/logback/typesafeconfig/ConfigConversion.java
type ConfigConversion (line 21) | public interface ConfigConversion extends ContextAware {
method configAsMap (line 23) | default Map<String, String> configAsMap(Config levelsConfig) {
FILE: logback-typesafe-config/src/main/java/com/tersesystems/logback/typesafeconfig/ConfigListConverter.java
class ConfigListConverter (line 37) | public class ConfigListConverter extends ClassicConverter {
method convert (line 38) | @Override
FILE: logback-typesafe-config/src/main/java/com/tersesystems/logback/typesafeconfig/TypesafeConfigAction.java
class TypesafeConfigAction (line 71) | public class TypesafeConfigAction extends Action implements ConfigConver...
method begin (line 73) | @Override
method configureLevels (line 107) | protected void configureLevels(Config config) {
method end (line 117) | @Override
method configureContextScope (line 120) | protected void configureContextScope(
method configureLocalScope (line 129) | protected void configureLocalScope(
method generateConfig (line 138) | protected Config generateConfig(ClassLoader classLoader, boolean debug) {
class ContextObjectAction (line 172) | public static class ContextObjectAction extends Action implements Conf...
method stringToScope (line 177) | static ActionUtil.Scope stringToScope(String scopeStr) {
method resolveConfig (line 187) | Config resolveConfig(InterpretationContext ic) {
method begin (line 199) | public void begin(InterpretationContext ic, String localName, Attrib...
method isValid (line 209) | boolean isValid(String name, String value) {
method end (line 213) | public void end(InterpretationContext ic, String name) {
method getNameAttr (line 260) | private String getNameAttr() {
method setNameAttr (line 264) | void setNameAttr(String name) {
method setPath (line 268) | public void setPath(String path) {
method setScope (line 272) | public void setScope(ActionUtil.Scope scope) {
FILE: logback-typesafe-config/src/test/java/com/tersesystems/logback/typesafeconfig/ConfigListConverterTest.java
class ConfigListConverterTest (line 22) | public class ConfigListConverterTest {
method testConversion (line 24) | @Test
FILE: logback-typesafe-config/src/test/java/com/tersesystems/logback/typesafeconfig/TypesafeConfigActionTest.java
class TypesafeConfigActionTest (line 24) | public class TypesafeConfigActionTest {
method testConfigWithDefault (line 26) | @Test
method testConfigWithContext (line 50) | @Test
method testConfigWithLocal (line 74) | @Test
FILE: logback-uniqueid-appender/src/main/java/com/tersesystems/logback/uniqueid/FlakeIdGenerator.java
class FlakeIdGenerator (line 21) | public class FlakeIdGenerator implements IdGenerator {
method generateId (line 26) | @Override
FILE: logback-uniqueid-appender/src/main/java/com/tersesystems/logback/uniqueid/IdGenerator.java
type IdGenerator (line 13) | public interface IdGenerator {
method generateId (line 14) | String generateId();
FILE: logback-uniqueid-appender/src/main/java/com/tersesystems/logback/uniqueid/KsuidSubsecondIdGenerator.java
class KsuidSubsecondIdGenerator (line 11) | public class KsuidSubsecondIdGenerator implements IdGenerator {
method random (line 13) | private Random random() {
method generateId (line 19) | @Override
FILE: logback-uniqueid-appender/src/main/java/com/tersesystems/logback/uniqueid/RandomUUIDIdGenerator.java
class RandomUUIDIdGenerator (line 21) | public class RandomUUIDIdGenerator implements IdGenerator {
method random (line 22) | private Random random() {
method generateId (line 28) | @Override
FILE: logback-uniqueid-appender/src/main/java/com/tersesystems/logback/uniqueid/TsidIdgenerator.java
class TsidIdgenerator (line 9) | public class TsidIdgenerator implements IdGenerator {
method generateId (line 15) | @Override
FILE: logback-uniqueid-appender/src/main/java/com/tersesystems/logback/uniqueid/UlidIdGenerator.java
class UlidIdGenerator (line 11) | public class UlidIdGenerator implements IdGenerator {
method random (line 13) | private Random random() {
method generateId (line 19) | @Override
FILE: logback-uniqueid-appender/src/main/java/com/tersesystems/logback/uniqueid/UniqueIdComponentAppender.java
class UniqueIdComponentAppender (line 18) | public class UniqueIdComponentAppender
method getIdGenerator (line 23) | public IdGenerator getIdGenerator() {
method setIdGenerator (line 27) | public void setIdGenerator(IdGenerator idGenerator) {
method decorateEvent (line 31) | @Override
FILE: logback-uniqueid-appender/src/main/java/com/tersesystems/logback/uniqueid/UniqueIdConverter.java
class UniqueIdConverter (line 17) | public class UniqueIdConverter extends DynamicConverter<ILoggingEvent> {
method convert (line 18) | @Override
FILE: logback-uniqueid-appender/src/main/java/com/tersesystems/logback/uniqueid/UniqueIdProvider.java
type UniqueIdProvider (line 17) | public interface UniqueIdProvider extends Component {
method uniqueId (line 18) | String uniqueId();
FILE: logback-uniqueid-appender/src/test/java/com/tersesystems/logback/uniqueid/UniqueIdAppenderTest.java
class UniqueIdAppenderTest (line 26) | public class UniqueIdAppenderTest {
method testUniqueIdEventAppender (line 28) | @Test
Condensed preview — 309 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (600K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 834,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 595,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
},
{
"path": ".gitignore",
"chars": 244,
"preview": "# Ignore Gradle project-specific cache directory\n.gradle\n\nlogback-sigar/native/\n\n# Ignore Gradle build output directory\n"
},
{
"path": ".java-version",
"chars": 4,
"preview": "1.8\n"
},
{
"path": "LICENSE",
"chars": 439,
"preview": "License\n-------\nWritten in 2019 by Will Sargent will@tersesystems.com\nTo the extent possible under law, the author(s) ha"
},
{
"path": "README.md",
"chars": 3860,
"preview": "[](https://search.maven"
},
{
"path": "RELEASING.md",
"chars": 2772,
"preview": "## Release\n\nTo make sure everything works:\n\n```bash\n./gradlew clean build check\n```\n\nTo format everything using [Spotles"
},
{
"path": "build.gradle",
"chars": 3186,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "docs/guide/audio.md",
"chars": 1927,
"preview": "# Audio\n\nThe audio appender uses a system beep configured through `SystemPlayer` to notify on warnings and errors, and l"
},
{
"path": "docs/guide/budget.md",
"chars": 2600,
"preview": "# Budget Aware Logging\n\nThere are instances where logging may be overly chatty, and will log more than necessary. \n\nRat"
},
{
"path": "docs/guide/censor.md",
"chars": 3482,
"preview": "# Censors\n\nThere may be sensitive information that you don't want to show up in the logs. You can get around this by pa"
},
{
"path": "docs/guide/composite.md",
"chars": 2644,
"preview": "# Composite Appender\n\nThe composite appender presents a single appender and appends to several appenders. It is very us"
},
{
"path": "docs/guide/compression.md",
"chars": 6752,
"preview": "# Compression\n\nEncoders are powerful and useful. They give you access to the raw bytes, and let you manipulate them bef"
},
{
"path": "docs/guide/correlationid.md",
"chars": 3719,
"preview": "# Correlation ID\n\nThe `logback-correlationid` module is a set of classes designed to encompass the idea of a correlation"
},
{
"path": "docs/guide/exception-mapping.md",
"chars": 8568,
"preview": "# Exception Mapping\n\nException Mapping is done to show the important details of an exception, including the root cause i"
},
{
"path": "docs/guide/instrumentation.md",
"chars": 14158,
"preview": "# Instrumentation\n\nIf you have library code that doesn't pass around `ILoggerFactory` and doesn't let you add informatio"
},
{
"path": "docs/guide/jdbc.md",
"chars": 10421,
"preview": "# JDBC\n\nThere is a JDBC appender included which can be subclassed and extended as necessary in the `logback-jdbc-appende"
},
{
"path": "docs/guide/relativens.md",
"chars": 2642,
"preview": "# Relative Nanoseconds Appender\n\n`LoggingEvent` already has a timestamp associated with it, but that timestamp is genera"
},
{
"path": "docs/guide/select.md",
"chars": 3271,
"preview": "# Select Appender\n\nDifferent appenders are useful in different environments.\n\nDevelopment wants:\n\n* Want colorized outpu"
},
{
"path": "docs/guide/slf4jbridge.md",
"chars": 6951,
"preview": "# JUL to SLF4J Bridge\n\nIt's easy to assume that all Java libraries will depend on SLF4J. But one of the oddities of Jav"
},
{
"path": "docs/guide/tracing.md",
"chars": 7267,
"preview": "# Tracing to Honeycomb\n\nYou can connect Logback to Honeycomb directly through the Honeycomb Logback appender, using the "
},
{
"path": "docs/guide/turbomarker.md",
"chars": 9837,
"preview": "# Turbo Markers\n\n[Turbo filters](https://logback.qos.ch/manual/filters.html#TurboFilter) are filters that decide whether"
},
{
"path": "docs/guide/typesafeconfig.md",
"chars": 3873,
"preview": "# Typesafe Config\n\nThe `TypesafeConfigAction` will search in a variety of places for configuration using [standard fallb"
},
{
"path": "docs/guide/uniqueid.md",
"chars": 3912,
"preview": "# Unique ID Appenders\n\nThe unique id appender allows the logging event to carry a unique id. When used in conjunction w"
},
{
"path": "docs/index.md",
"chars": 2690,
"preview": "# Terse Logback\n\nTerse Logback is a collection of [Logback](https://logback.qos.ch/) modules that extend [Logback](https"
},
{
"path": "docs/reading/reading.md",
"chars": 7302,
"preview": "# Further Reading\n\nEverything I write on logging is going to be here:\n\n* [Terse Systems](https://tersesystems.com/catego"
},
{
"path": "gradle/LICENSE_HEADER",
"chars": 218,
"preview": "SPDX-License-Identifier: CC0-1.0\n\nCopyright ${copyrightYear} ${author}.\n\nLicensed under the CC0 Public Domain Dedication"
},
{
"path": "gradle/java-publication.gradle",
"chars": 2013,
"preview": "//Auxiliary jar files required by Maven module publications\ntask sourcesJar(type: Jar, dependsOn: classes) {\n archive"
},
{
"path": "gradle/release.gradle",
"chars": 476,
"preview": "apply plugin: \"io.github.gradle-nexus.publish-plugin\" //https://github.com/gradle-nexus/publish-plugin/\nnexusPublishing "
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 458,
"preview": "#\n# SPDX-License-Identifier: CC0-1.0\n#\n# Copyright 2018-2020 Will Sargent.\n#\n# Licensed under the CC0 Public Domain Dedi"
},
{
"path": "gradle.properties",
"chars": 837,
"preview": "#\n# SPDX-License-Identifier: CC0-1.0\n#\n# Copyright 2018-2020 Will Sargent.\n#\n# Licensed under the CC0 Public Domain Dedi"
},
{
"path": "gradlew",
"chars": 5764,
"preview": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0"
},
{
"path": "gradlew.bat",
"chars": 3056,
"preview": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (th"
},
{
"path": "logback-audio/gradle.properties",
"chars": 271,
"preview": "#\n# SPDX-License-Identifier: CC0-1.0\n#\n# Copyright 2018-2020 Will Sargent.\n#\n# Licensed under the CC0 Public Domain Dedi"
},
{
"path": "logback-audio/logback-audio.gradle",
"chars": 526,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-audio/src/main/java/com/tersesystems/logback/audio/AudioAppender.java",
"chars": 882,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-audio/src/main/java/com/tersesystems/logback/audio/AudioMarker.java",
"chars": 1061,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-audio/src/main/java/com/tersesystems/logback/audio/AudioMarkerAppender.java",
"chars": 1074,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-audio/src/main/java/com/tersesystems/logback/audio/FilePlayer.java",
"chars": 1168,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-audio/src/main/java/com/tersesystems/logback/audio/PlayMethods.java",
"chars": 1532,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-audio/src/main/java/com/tersesystems/logback/audio/Player.java",
"chars": 357,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-audio/src/main/java/com/tersesystems/logback/audio/PlayerAction.java",
"chars": 2563,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-audio/src/main/java/com/tersesystems/logback/audio/PlayerAttachable.java",
"chars": 377,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-audio/src/main/java/com/tersesystems/logback/audio/PlayerConverter.java",
"chars": 1079,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-audio/src/main/java/com/tersesystems/logback/audio/PlayerException.java",
"chars": 704,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-audio/src/main/java/com/tersesystems/logback/audio/ResourcePlayer.java",
"chars": 1303,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-audio/src/main/java/com/tersesystems/logback/audio/SimplePlayer.java",
"chars": 1771,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-audio/src/main/java/com/tersesystems/logback/audio/SystemPlayer.java",
"chars": 617,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-audio/src/main/java/com/tersesystems/logback/audio/URLPlayer.java",
"chars": 603,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-audio/src/test/java/com/tersesystems/logback/audio/TestAudio.java",
"chars": 2603,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-audio/src/test/java/com/tersesystems/logback/audio/TestNested.java",
"chars": 1265,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-audio/src/test/resources/logback-with-converter.xml",
"chars": 674,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-audio/src/test/resources/logback-with-marker-appender.xml",
"chars": 711,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-audio/src/test/resources/logback-with-nested-appender.xml",
"chars": 3574,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-budget/gradle.properties",
"chars": 264,
"preview": "#\n# SPDX-License-Identifier: CC0-1.0\n#\n# Copyright 2018-2020 Will Sargent.\n#\n# Licensed under the CC0 Public Domain Dedi"
},
{
"path": "logback-budget/logback-budget.gradle",
"chars": 507,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-budget/src/main/java/com/tersesystems/logback/budget/BudgetEvaluator.java",
"chars": 2945,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-budget/src/main/java/com/tersesystems/logback/budget/BudgetRule.java",
"chars": 1116,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-budget/src/main/java/com/tersesystems/logback/budget/BudgetRuleAction.java",
"chars": 2606,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-budget/src/main/java/com/tersesystems/logback/budget/BudgetRuleAttachable.java",
"chars": 395,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-budget/src/main/java/com/tersesystems/logback/budget/BudgetTurboFilter.java",
"chars": 3170,
"preview": "package com.tersesystems.logback.budget;\n\nimport ch.qos.logback.classic.Level;\nimport ch.qos.logback.classic.Logger;\nimp"
},
{
"path": "logback-budget/src/test/java/com.tersesystems.logback.budget/BudgetEvaluatorTest.java",
"chars": 1090,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-budget/src/test/java/com.tersesystems.logback.budget/BudgetTurboFilterTest.java",
"chars": 859,
"preview": "package com.tersesystems.logback.budget;\n\nimport ch.qos.logback.classic.Logger;\nimport ch.qos.logback.classic.LoggerCont"
},
{
"path": "logback-budget/src/test/resources/logback-budget.xml",
"chars": 1052,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-budget/src/test/resources/logback-turbofilter.xml",
"chars": 912,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-bytebuddy/gradle.properties",
"chars": 283,
"preview": "#\n# SPDX-License-Identifier: CC0-1.0\n#\n# Copyright 2018-2020 Will Sargent.\n#\n# Licensed under the CC0 Public Domain Dedi"
},
{
"path": "logback-bytebuddy/logback-bytebuddy.gradle",
"chars": 2337,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/AdviceConfig.java",
"chars": 4774,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/LogbackInstrumentationAgent.java",
"chars": 2588,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/LoggingInstrumentationAdvice.java",
"chars": 7619,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/LoggingInstrumentationByteBuddyBuilder.java",
"chars": 7033,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/MethodInfo.java",
"chars": 1282,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/MethodInfoLookup.java",
"chars": 1533,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/impl/DeclaringTypeLoggerResolver.java",
"chars": 964,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/impl/Enter.java",
"chars": 2491,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/impl/Exit.java",
"chars": 3566,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/impl/FixedLoggerResolver.java",
"chars": 666,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/impl/LoggerResolver.java",
"chars": 542,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/impl/SafeArguments.java",
"chars": 1736,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/main/java/com/tersesystems/logback/bytebuddy/impl/SystemFlow.java",
"chars": 4222,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/test/java/com/tersesystems/logback/bytebuddy/AdviceConfigTest.java",
"chars": 894,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/test/java/com/tersesystems/logback/bytebuddy/ClassCalledByAgent.java",
"chars": 675,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/test/java/com/tersesystems/logback/bytebuddy/InProcessInstrumentationExample.java",
"chars": 2742,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/test/java/com/tersesystems/logback/bytebuddy/PreloadedInstrumentationExample.java",
"chars": 758,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-bytebuddy/src/test/resources/logback-test.xml",
"chars": 560,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-bytebuddy/src/test/resources/logback.conf",
"chars": 256,
"preview": "logback.bytebuddy {\n service-name = \"example-app\"\n\n tracing {\n \"com.tersesystems.logback.bytebuddy.ClassCalledByAge"
},
{
"path": "logback-censor/gradle.properties",
"chars": 321,
"preview": "#\n# SPDX-License-Identifier: CC0-1.0\n#\n# Copyright 2018-2020 Will Sargent.\n#\n# Licensed under the CC0 Public Domain Dedi"
},
{
"path": "logback-censor/logback-censor.gradle",
"chars": 507,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-censor/src/main/java/com/tersesystems/logback/censor/Censor.java",
"chars": 457,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-censor/src/main/java/com/tersesystems/logback/censor/CensorAction.java",
"chars": 3919,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-censor/src/main/java/com/tersesystems/logback/censor/CensorAttachable.java",
"chars": 363,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-censor/src/main/java/com/tersesystems/logback/censor/CensorConstants.java",
"chars": 480,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-censor/src/main/java/com/tersesystems/logback/censor/CensorContextAware.java",
"chars": 694,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-censor/src/main/java/com/tersesystems/logback/censor/CensorConverter.java",
"chars": 2737,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-censor/src/main/java/com/tersesystems/logback/censor/CensorRefAction.java",
"chars": 2360,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-censor/src/main/java/com/tersesystems/logback/censor/CensoringJsonGeneratorDecorator.java",
"chars": 4324,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-censor/src/main/java/com/tersesystems/logback/censor/CensoringPrettyPrintingJsonGeneratorDecorator.java",
"chars": 604,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-censor/src/main/java/com/tersesystems/logback/censor/RegexCensor.java",
"chars": 1750,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-censor/src/test/java/com/tersesystems/logback/censor/CensorActionTest.java",
"chars": 3338,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-censor/src/test/java/com/tersesystems/logback/censor/CensoringJsonGeneratorDecoratorTest.java",
"chars": 3258,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-censor/src/test/java/com/tersesystems/logback/censor/RegexCensorTest.java",
"chars": 1035,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-censor/src/test/java/com/tersesystems/logback/censor/TestAppender.java",
"chars": 885,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-censor/src/test/resources/test1.xml",
"chars": 1264,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-censor/src/test/resources/test2.xml",
"chars": 1264,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-censor/src/test/resources/test3.xml",
"chars": 1485,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-censor/src/test/resources/test4.xml",
"chars": 1607,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-classic/gradle.properties",
"chars": 265,
"preview": "#\n# SPDX-License-Identifier: CC0-1.0\n#\n# Copyright 2018-2020 Will Sargent.\n#\n# Licensed under the CC0 Public Domain Dedi"
},
{
"path": "logback-classic/logback-classic.gradle",
"chars": 427,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/ChangeLogLevel.java",
"chars": 1459,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/ContainerEventAppender.java",
"chars": 801,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/ContainerProxyLoggingEvent.java",
"chars": 1078,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/ContextAwareBasicMarker.java",
"chars": 2351,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/ExceptionMessageConverter.java",
"chars": 3091,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/FormatParamsDecider.java",
"chars": 776,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/IContainerLoggingEvent.java",
"chars": 532,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/ILoggingEventFactory.java",
"chars": 595,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/LoggerDecider.java",
"chars": 748,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/LoggingEventFactory.java",
"chars": 860,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/MarkerLoggerDecider.java",
"chars": 778,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/NanoTime.java",
"chars": 1995,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/NanoTimeComponentAppender.java",
"chars": 1078,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/NanoTimeConverter.java",
"chars": 697,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/NanoTimeMarker.java",
"chars": 838,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/NanoTimeSupplier.java",
"chars": 409,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/ProxyLoggingEvent.java",
"chars": 2341,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/SLF4JBridgeHandlerAction.java",
"chars": 1298,
"preview": "package com.tersesystems.logback.classic;\n\nimport ch.qos.logback.core.joran.action.Action;\nimport ch.qos.logback.core.jo"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/SetLoggerLevelsAction.java",
"chars": 1969,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/StartTime.java",
"chars": 3047,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/StartTimeConverter.java",
"chars": 752,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/StartTimeMarker.java",
"chars": 845,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/StartTimeSupplier.java",
"chars": 373,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/TapFilter.java",
"chars": 4037,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/TerseBasicMarker.java",
"chars": 3203,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/TerseHighlightConverter.java",
"chars": 1949,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/TimeSinceEpochConverter.java",
"chars": 564,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/TurboFilterDecider.java",
"chars": 902,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/Utils.java",
"chars": 4390,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/encoder/PatternLayoutEncoder.java",
"chars": 1031,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/functional/GetAppenderFunction.java",
"chars": 1553,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/functional/GetSiftedAppenderFunction.java",
"chars": 1261,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/functional/RootLoggerSupplier.java",
"chars": 985,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/sift/DiscriminatingMarker.java",
"chars": 1011,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/sift/DiscriminatingMarkerFactory.java",
"chars": 950,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/sift/DiscriminatingValue.java",
"chars": 438,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/main/java/com/tersesystems/logback/classic/sift/MarkerBasedDiscriminator.java",
"chars": 2165,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/test/java/com/tersesystems/logback/classic/ChangeLogLevelTest.java",
"chars": 746,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/test/java/com/tersesystems/logback/classic/CorrelationIdMarker.java",
"chars": 876,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/test/java/com/tersesystems/logback/classic/CorrelationIdTurboFilter.java",
"chars": 2185,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/test/java/com/tersesystems/logback/classic/EnabledFilterTest.java",
"chars": 1203,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/test/java/com/tersesystems/logback/classic/ExceptionMessageConverterTest.java",
"chars": 4594,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/test/java/com/tersesystems/logback/classic/SetLoggerLevelsActionTest.java",
"chars": 323,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/test/java/com/tersesystems/logback/classic/TapFilterTest.java",
"chars": 5282,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/test/java/com/tersesystems/logback/classic/TerseHighlightConverterTest.java",
"chars": 1236,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/test/java/com/tersesystems/logback/classic/UtilsTest.java",
"chars": 1850,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-classic/src/test/resources/logback-tapfilter-correlation.xml",
"chars": 1100,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-classic/src/test/resources/logback-tapfilter.xml",
"chars": 876,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-compress-encoder/gradle.properties",
"chars": 274,
"preview": "#\n# SPDX-License-Identifier: CC0-1.0\n#\n# Copyright 2018-2020 Will Sargent.\n#\n# Licensed under the CC0 Public Domain Dedi"
},
{
"path": "logback-compress-encoder/logback-compress-encoder.gradle",
"chars": 525,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-compress-encoder/src/main/java/com.tersesystems.logback.compress/CompressingEncoder.java",
"chars": 2880,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-compress-encoder/src/main/java/com.tersesystems.logback.compress/CompressingFileAppender.java",
"chars": 3138,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-compress-encoder/src/test/java/com/tersesystems/logback/compress/Utils.java",
"chars": 1146,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-compress-encoder/src/test/resources/logback-with-zstd-encoder.xml",
"chars": 714,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-core/gradle.properties",
"chars": 282,
"preview": "#\n# SPDX-License-Identifier: CC0-1.0\n#\n# Copyright 2018-2020 Will Sargent.\n#\n# Licensed under the CC0 Public Domain Dedi"
},
{
"path": "logback-core/logback-core.gradle",
"chars": 470,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-core/src/main/java/com/tersesystems/logback/core/AbstractAppender.java",
"chars": 1932,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-core/src/main/java/com/tersesystems/logback/core/Component.java",
"chars": 402,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-core/src/main/java/com/tersesystems/logback/core/ComponentContainer.java",
"chars": 557,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-core/src/main/java/com/tersesystems/logback/core/CompositeAppender.java",
"chars": 1947,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-core/src/main/java/com/tersesystems/logback/core/DecoratingAppender.java",
"chars": 2019,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-core/src/main/java/com/tersesystems/logback/core/DefaultAppenderAttachable.java",
"chars": 1392,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-core/src/main/java/com/tersesystems/logback/core/EnabledFilter.java",
"chars": 801,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-core/src/main/java/com/tersesystems/logback/core/SelectAppender.java",
"chars": 2288,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-core/src/main/java/com/tersesystems/logback/core/encoder/LayoutWrappingEncoder.java",
"chars": 4739,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-core/src/main/java/com/tersesystems/logback/core/pattern/PatternLayoutEncoderBase.java",
"chars": 2306,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-core/src/test/java/com/tersesystems/logback/core/CompositeAppenderTest.java",
"chars": 1665,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-core/src/test/java/com/tersesystems/logback/core/SelectAppenderTest.java",
"chars": 2557,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-core/src/test/java/com/tersesystems/logback/core/TestAppender.java",
"chars": 883,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-core/src/test/resources/logback-with-composite-appender.xml",
"chars": 748,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-core/src/test/resources/logback-with-select-appender.xml",
"chars": 1924,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-correlationid/gradle.properties",
"chars": 282,
"preview": "#\n# SPDX-License-Identifier: CC0-1.0\n#\n# Copyright 2018-2020 Will Sargent.\n#\n# Licensed under the CC0 Public Domain Dedi"
},
{
"path": "logback-correlationid/logback-correlationid.gradle",
"chars": 448,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-correlationid/src/main/java/com/tersesystems/logback/correlationid/CorrelationIdDecider.java",
"chars": 1035,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-correlationid/src/main/java/com/tersesystems/logback/correlationid/CorrelationIdFilter.java",
"chars": 1081,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-correlationid/src/main/java/com/tersesystems/logback/correlationid/CorrelationIdMarker.java",
"chars": 933,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-correlationid/src/main/java/com/tersesystems/logback/correlationid/CorrelationIdProvider.java",
"chars": 462,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-correlationid/src/main/java/com/tersesystems/logback/correlationid/CorrelationIdTapFilter.java",
"chars": 1884,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-correlationid/src/main/java/com/tersesystems/logback/correlationid/CorrelationIdUtils.java",
"chars": 2439,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-correlationid/src/test/java/com.tersesystems.logback.correlationid/CorrelationIdFilterTest.java",
"chars": 2963,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-correlationid/src/test/java/com.tersesystems.logback.correlationid/CorrelationIdTapFilterTest.java",
"chars": 4678,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-correlationid/src/test/resources/logback-correlationid-jdbc.xml",
"chars": 2275,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-correlationid/src/test/resources/logback-correlationid-tapfilter.xml",
"chars": 938,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-correlationid/src/test/resources/logback-correlationid.xml",
"chars": 565,
"preview": "<!--\n ~ SPDX-License-Identifier: CC0-1.0\n ~\n ~ Copyright 2018-2020 Will Sargent.\n ~\n ~ Licensed under the CC0 Publi"
},
{
"path": "logback-correlationid/src/test/resources/spy.properties",
"chars": 252,
"preview": "#\n# SPDX-License-Identifier: CC0-1.0\n#\n# Copyright 2018-2020 Will Sargent.\n#\n# Licensed under the CC0 Public Domain Dedi"
},
{
"path": "logback-exception-mapping/gradle.properties",
"chars": 275,
"preview": "#\n# SPDX-License-Identifier: CC0-1.0\n#\n# Copyright 2018-2020 Will Sargent.\n#\n# Licensed under the CC0 Public Domain Dedi"
},
{
"path": "logback-exception-mapping/logback-exception-mapping.gradle",
"chars": 325,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/BeanExceptionMapping.java",
"chars": 1939,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/Constants.java",
"chars": 458,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/DefaultExceptionMappingRegistry.java",
"chars": 4479,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/ExceptionCauseIterator.java",
"chars": 1280,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
},
{
"path": "logback-exception-mapping/src/main/java/com/tersesystems/logback/exceptionmapping/ExceptionHierarchyIterator.java",
"chars": 1241,
"preview": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * Copyright 2018-2020 Will Sargent.\n *\n * Licensed under the CC0 Public Domai"
}
]
// ... and 109 more files (download for full content)
About this extraction
This page contains the full source code of the tersesystems/terse-logback GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 309 files (534.5 KB), approximately 143.6k tokens, and a symbol index with 1119 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.