Uh oh!
There was an error while loading. Please reload this page.
Support custom Log4j2 configurations - #149
Conversation
`toLog4jLogEvent()` no longer throws `IllegalStateException` when the current configuration is not `DefaultConfiguration`, e.g., when an application supplies its own `log4j2.xml`. Following the upstream Flogger approach, with a custom configuration only the literal log message becomes the Log4j2 message. The metadata is carried by the context data map of the log event, where a user-defined layout can render it, e.g., via `%X`. Under `DefaultConfiguration`, whose hard-wired layout ignores context data, the metadata is still appended to the message itself. New `Log4j2CustomConfigSpec` covers the custom-configuration branch. Test helpers shared by the specs are extracted into `given/TestLoggers.kt`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Log4j2 backend selects the message-formatting strategy by checking whether the current configuration is `DefaultConfiguration`. This check now reads the configuration of the context owning the logger which renders the event, instead of the statically looked-up `LoggerContext.getContext(false)`. This makes the decision consistent with the rendering context in multi-context deployments (per-webapp or OSGi context selectors), and replaces a stack-walking global lookup on the hot path with a field read. This is a deliberate divergence from the original Flogger implementation, documented in the code. The conversion functions become `internal` extensions on `LogData`: `LogData.toLog4jEvent(logger)` and `LogData.toLog4jEvent(logger, error)`. `Log4j2CustomConfigSpec` now runs against a private `LoggerContext` instead of mutating the global configuration, and gains a test proving that two backends in the same JVM each format per the configuration of their own context. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Replace `requireNonNull(value)` followed by `value!!` with Kotlin's
`requireNotNull(value)`, which smart-casts the value.
- Drop a `@Suppress("NAME_SHADOWING")` which no longer suppresses
anything.
Both were flagged as pre-existing nits by the Kotlin review.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>There was a problem hiding this comment.
Pull request overview
This PR updates the Log4j2 backend to work correctly when an application provides a non-DefaultConfiguration (e.g., via log4j2.xml), avoiding runtime failures and ensuring metadata is still available via Log4j2 context data. In addition, the PR includes a substantial set of build/dependency-reporting and dependency/version/tooling updates.
Changes:
- Log4j2 backend: format
%msgas the literal message under custom configurations, while keeping metadata inLogEventcontext data (and preserving the existing[CONTEXT ...]suffix behavior underDefaultConfiguration). - Tests: add coverage for custom Log4j2 configuration and multi-context behavior; extract shared test logger helpers.
- Build/dependencies/tooling: version bumps, dependency-reporting changes (POM generation), dependency coordinate updates, and Gradle wrapper/workflow churn.
Reviewed changes
Copilot reviewed 41 out of 46 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| version.gradle.kts | Bumps the published snapshot version. |
| logging/src/commonMain/kotlin/io/spine/logging/backend/SimpleMessageFormatter.kt | Removes an unused import (no functional change). |
| gradle/wrapper/gradle-wrapper.properties | Updates the Gradle wrapper distribution URL. |
| docs/dependencies/pom.xml | Updates the documented/generated dependency POM (versions/coordinates changed). |
| buildSrc/src/test/kotlin/io/spine/gradle/report/pom/PomGeneratorIgTest.kt | Adds Gradle TestKit-based functional coverage for generatePom. |
| buildSrc/src/test/kotlin/io/spine/gradle/report/pom/DependencyWriterSpec.kt | Updates dependency writer tests to use resolved-version collection. |
| buildSrc/src/main/kotlin/io/spine/gradle/report/pom/ResolvedVersions.kt | Introduces per-project resolved-version collection and persistence. |
| buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomXmlWriter.kt | Wires resolved versions into POM XML writing. |
| buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.kt | Makes generatePom depend on per-project resolved-version collectors. |
| buildSrc/src/main/kotlin/io/spine/gradle/report/pom/DependencyWriter.kt | Refactors dependency collection to take per-project resolved versions. |
| buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt | Adjusts path constants usage and opts license report task out of build cache. |
| buildSrc/src/main/kotlin/io/spine/dependency/test/Testcontainers.kt | Updates Testcontainers coordinates/version and adds PostgreSQL module. |
| buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt | Bumps Validation version; removes some artifacts. |
| buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt | Updates ToolBase version and splits artifacts; deprecates old all-in-one artifact. |
| buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt | Bumps Spine Time version. |
| buildSrc/src/main/kotlin/io/spine/dependency/local/ProtoTap.kt | Bumps ProtoTap version. |
| buildSrc/src/main/kotlin/io/spine/dependency/local/ModelCompiler.kt | Removes ModelCompiler dependency definition. |
| buildSrc/src/main/kotlin/io/spine/dependency/local/Logging.kt | Bumps Logging dependency version reference. |
| buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt | Refactors artifact naming and bumps CoreJvmCompiler versions. |
| buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt | Bumps CoreJvm version. |
| buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt | Bumps fallback versions; renames fat CLI artifact; removes CLI API reference. |
| buildSrc/src/main/kotlin/io/spine/dependency/local/Change.kt | Bumps Spine Change version. |
| buildSrc/src/main/kotlin/io/spine/dependency/local/BaseTypes.kt | Bumps BaseTypes version. |
| buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt | Bumps Spine Base versions. |
| buildSrc/src/main/kotlin/io/spine/dependency/lib/Roaster.kt | Removes outdated comment about Java 11 compatibility. |
| buildSrc/src/main/kotlin/io/spine/dependency/lib/PalantirJavaFormat.kt | Bumps Palantir Java Format version. |
| buildSrc/src/main/kotlin/io/spine/dependency/lib/Log4j2.kt | Bumps Log4j2 version; adds SLF4J 2 bridge coordinate. |
| buildSrc/src/main/kotlin/io/spine/dependency/lib/JacksonV2.kt | Introduces a dedicated Jackson 2.x BOM/dependency set. |
| buildSrc/src/main/kotlin/io/spine/dependency/lib/Jackson.kt | Switches Jackson definitions to 3.x (tools.jackson) and updates module declarations. |
| buildSrc/src/main/kotlin/io/spine/dependency/boms/Boms.kt | Points optional Jackson BOM to Jackson 2.x BOM (via JacksonV2). |
| buildSrc/src/main/kotlin/BuildExtensions.kt | Adds helper to exclude JetBrains annotations from published deps. |
| buildSrc/build.gradle.kts | Clarifies buildSrc Jackson pin rationale; configures TestKit and classpath injection. |
| build.gradle.kts | Removes ToolBase forcing and an exclusion entry. |
| backends/log4j2-backend/src/test/kotlin/io/spine/logging/backend/log4j2/Log4j2LoggerBackendSpec.kt | Refactors test logger creation to shared helpers. |
| backends/log4j2-backend/src/test/kotlin/io/spine/logging/backend/log4j2/Log4j2CustomConfigSpec.kt | Adds tests for custom Log4j2 configuration behavior + multi-context check. |
| backends/log4j2-backend/src/test/kotlin/io/spine/logging/backend/log4j2/given/TestLoggers.kt | Extracts shared test logger/appender utilities. |
| backends/log4j2-backend/src/main/kotlin/io/spine/logging/backend/log4j2/LogEvents.kt | Implements configuration-aware message formatting and refactors conversion helpers to LogData extensions. |
| backends/log4j2-backend/src/main/kotlin/io/spine/logging/backend/log4j2/Log4j2LoggerBackend.kt | Updates backend to use the new LogData.toLog4jEvent(...) conversions. |
| .idea/kotlinc.xml | Removes IDE-local Kotlin compiler settings file. |
| .gitignore | Clarifies .idea/kotlinc.xml should remain ignored. |
| .github/workflows/gradle-wrapper-validation.yml | Removes Gradle wrapper validation workflow. |
| .agents/tasks/log4j2-context-binding.md | Adds an internal task note/document for the Log4j2 context-binding change. |
| .agents/tasks/custom-log4j2-formatting.md | Adds an internal task note/document for custom Log4j2 formatting support. |
Files not reviewed (1)
- .idea/kotlinc.xml: Generated file
Suppressed comments (1)
backends/log4j2-backend/src/main/kotlin/io/spine/logging/backend/log4j2/Log4j2LoggerBackend.kt:51
- Same as above: avoid
logger.get()here; log via the logger API directly to avoid relying on the internalget()contract.
logger.get().log(badData.toLog4jEvent(logger, error))
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@## master #149 +/- ##
============================================
+ Coverage 67.80% 68.67% +0.87% - Complexity 464 470 +6
============================================
Files 109 109 Lines 2578 2586 +8 Branches 403 403 ============================================
+ Hits 1748 1776 +28 + Misses 696 675 -21 - Partials 134 135 +1 🚀 New features to boost your workflow:
|
Codecov flagged the error-conversion overload `LogData.toLog4jEvent(logger, error)` as uncovered patch lines; the review had also noted the missing `handleError` coverage as a pre-existing gap. The new tests verify that a malformed log statement is re-targeted to `WARN` when its level is below `WARNING`, keeps its level otherwise, carries the error as `thrown`, and describes both the error and the original message. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
What
The Log4j2 backend threw
IllegalStateException("Unable to format a message for the configuration: ...") fromLogEvents.toLog4jLogEventwhenever the current Log4j2 configuration was notDefaultConfiguration— i.e., whenever an application provides its ownlog4j2.xml. This made the backend unusable in any configured application (reported fromdelivery-server, which packageslog4j2.xmlin its fat JAR).The fix
Following the upstream Flogger approach: with a user-provided configuration, only the literal log message becomes the Log4j2 message. The metadata keeps traveling in the log event's context data map (populated by
createContextMap()), where a user-defined layout renders it, e.g., via%X. UnderDefaultConfiguration, whose hard-wired layout ignores context data, the metadata is still appended to the message itself in the[CONTEXT ... ]form, as before. Spine'sLogDatacarries no template arguments (the Kotlin lambda API evaluates messages before the backend sees them), so Flogger'sBaseMessageFormatterbranch reduces to the literal message.The refactoring
The configuration check now reads the configuration of the context owning the logger that renders the event (
logger.context.configuration) instead of the statically looked-upLoggerContext.getContext(false). This makes the branch decision consistent with the rendering context in multi-context deployments (per-webapp or OSGi context selectors) and replaces a per-event stack-walking global lookup with a field read. This is a deliberate, documented divergence from the original Flogger implementation. The conversion functions becameinternalextensions —LogData.toLog4jEvent(logger)andLogData.toLog4jEvent(logger, error); the previouspublicfunctions had no external usages.Testing
Log4j2CustomConfigSpecruns against a private, startedLoggerContext(no global-state mutation): plain message without the[CONTEXT ... ]suffix, metadata via the context data map, the cause viaLogEvent.thrown, and a multi-context test — two backends in one JVM, each formatting per the configuration of its own context.given/TestLoggers.kt.:log4j2-backend:test: 41 tests green; fullbuildanddokkaGeneratepass.WithLoggingand the actualdelivery-serverlog4j2.xmlon the classpath (XmlConfiguration) logs per the pattern with no exception.Note for consumers
With a custom configuration,
%msgnow carries only the log message. To render Spine metadata (tags, key-value context), add%X(e.g.,%notEmpty{ [%X]}) to the pattern layout.Additional changes riding along
Besides the backend work, this branch carries routine housekeeping:
configsubmodule float, which brings the config-distributed updates:buildSrc/**dependency objects, the Gradle wrapper (9.6.1 → 9.7.1), the wrapper-validation workflow,.gitignore, and IDE settings. Per this organization's convention, config-distributed files are reviewed in theconfigrepository, not here.docs/dependencies/.build.gradle.ktsandSimpleMessageFormatter.kt.2.0.0-SNAPSHOT.423→.424per the versioning policy.🤖 Generated with Claude Code