Merge ThreadContext (MDC) into event ContextData in Log4j2Logger#6
Merged
Conversation
Log4j2Logger.buildContextData built the StringMap from only the slog
contextAttrs and event attrs, completely ignoring the caller thread's
log4j2 ThreadContext. Layouts using %X{key} and appenders that read
event.getContextData().getValue(key) saw nothing — even though the
thread had MDC entries set when the log call ran. The standard log4j2
logger handles this via ContextDataInjector inside the LogEventFactory;
slog builds the event manually and so must do the equivalent.
Fix: in buildContextData, after clearing the thread-local StringMap,
copy the current ThreadContext entries in first via
ThreadContext.getImmutableContext().forEach(map::putValue), then layer
slog's contextAttrs / event attrs / duration on top so slog overrides
on key collision.
Performance: when MDC is empty (the dominant case), the only added
work is one ThreadContext.isEmpty() check — no allocation, no map
clear, the existing emptyFrozenContextData() fast path is preserved.
When MDC is non-empty, getImmutableContext() returns a backing-map
reference (no copy) and forEach iterates without allocating
intermediate entries.
merlimat
added a commit
to merlimat/bookkeeper
that referenced
this pull request
Apr 25, 2026
Pulls in the MDC propagation fix from merlimat/slog#6, which makes log4j2 ThreadContext entries visible on slog events emitted via the Log4j2Logger backend (so %X{key} layouts and appenders that read event.getContextData().getValue(key) see the caller's MDC).
merlimat
added a commit
to merlimat/bookkeeper
that referenced
this pull request
Apr 25, 2026
Pulls in the MDC propagation fix from merlimat/slog#6, which makes log4j2 ThreadContext entries visible on slog events emitted via the Log4j2Logger backend (so %X{key} layouts and appenders that read event.getContextData().getValue(key) see the caller's MDC).
merlimat
added a commit
to merlimat/bookkeeper
that referenced
this pull request
Apr 25, 2026
Pulls in the MDC propagation fix from merlimat/slog#6, which makes log4j2 ThreadContext entries visible on slog events emitted via the Log4j2Logger backend (so %X{key} layouts and appenders that read event.getContextData().getValue(key) see the caller's MDC).
merlimat
added a commit
to merlimat/bookkeeper
that referenced
this pull request
Apr 25, 2026
Pulls in the MDC propagation fix from merlimat/slog#6, which makes log4j2 ThreadContext entries visible on slog events emitted via the Log4j2Logger backend (so %X{key} layouts and appenders that read event.getContextData().getValue(key) see the caller's MDC).
merlimat
added a commit
to apache/bookkeeper
that referenced
this pull request
Apr 28, 2026
* BP-69: Convert bookkeeper-common from SLF4J to slog First phase of the slog migration for BP-69. Adds the slog dependency, Lombok @CustomLog configuration, and converts the bookkeeper-common module. * Added license header * BP-69 common: fix checkstyle ImportOrder violations CI flagged 4 ImportOrder violations in the Phase 1 conversion: - SafeRunnable.java: io.github.merlimat.slog.Logger placed after java.* - OrderedExecutor.java: lombok.CustomLog placed after org.* - AbstractLifecycleComponent.java: same - TestOrderedExecutorDecorators.java: same Move each import to its correct alphabetical position per the project's checkstyle rule (`io.` < `java.`; `lombok.` < `org.`). * BP-69 common: add slog-0.9.7 to bookkeeper-dist LICENSE files CI check-binary-license flagged the new io.github.merlimat.slog-slog-0.9.7.jar as unaccounted for. Add it to the Apache-2.0 bundled-jars list in both LICENSE-all.bin.txt and LICENSE-bkctl.bin.txt, with a new numbered source reference pointing at https://github.com/merlimat/slog/tree/v0.9.7. * BP-69 common: also add slog-0.9.7 to LICENSE-server.bin.txt The previous LICENSE fix only updated LICENSE-all.bin.txt and LICENSE-bkctl.bin.txt, but the bin-server.xml assembly descriptor packages LICENSE-server.bin.txt into the server tarball that the CI license-check script inspects. * BP-69 common: remove TestOrderedExecutorDecorators and MdcContextTest Both tests exercise SLF4J MDC context propagation through the OrderedExecutor / OrderedScheduler decorators. With the slog migration the logger API is different and these tests are not straightforward to keep working against the new logger while still testing MDC behaviour as seen by the underlying SLF4J backend. Drop them for now. The MDC propagation code itself (MdcUtils, OrderedExecutor's mdcContextMap snapshot/restore) is unchanged, so end-to-end MDC-in-logs behaviour under Logback/Log4j2 backends is preserved. * Revert "BP-69 common: remove TestOrderedExecutorDecorators and MdcContextTest" This reverts commit 790d495. * Bump slog dependency to 0.9.8 Pulls in the MDC propagation fix from merlimat/slog#6, which makes log4j2 ThreadContext entries visible on slog events emitted via the Log4j2Logger backend (so %X{key} layouts and appenders that read event.getContextData().getValue(key) see the caller's MDC).
merlimat
added a commit
to apache/bookkeeper
that referenced
this pull request
Apr 28, 2026
* BP-69 base: add slog dependency and LICENSE entries Minimal scaffolding commit for the BP-69 slog migration series: - Add `io.github.merlimat.slog:slog:0.9.7` to the root pom.xml dependencyManagement and to the global compile classpath alongside the existing SLF4J API (which stays as the rendering backend). - Add lombok.config at the repo root so `@CustomLog` generates a slog `Logger` instead of an SLF4J one. - Register the slog jar in LICENSE-all.bin.txt, LICENSE-server.bin.txt and LICENSE-bkctl.bin.txt so the CI check-binary-license script finds it accounted for in the bundled-jars list. No actual Java file is converted in this commit. Individual module migrations stack on top of this one. * BP-69: Convert stats and allocator modules from SLF4J to slog Part of BP-69. Converts the stats-api, codahale/otel/prometheus metrics providers, stats-utils, and bookkeeper-common-allocator modules. * Fix checkstyle ImportOrder violations * Bump slog dependency to 0.9.8 Pulls in the MDC propagation fix from merlimat/slog#6, which makes log4j2 ThreadContext entries visible on slog events emitted via the Log4j2Logger backend (so %X{key} layouts and appenders that read event.getContextData().getValue(key) see the caller's MDC).
merlimat
added a commit
to apache/bookkeeper
that referenced
this pull request
Apr 28, 2026
* BP-69 base: add slog dependency and LICENSE entries Minimal scaffolding commit for the BP-69 slog migration series: - Add `io.github.merlimat.slog:slog:0.9.7` to the root pom.xml dependencyManagement and to the global compile classpath alongside the existing SLF4J API (which stays as the rendering backend). - Add lombok.config at the repo root so `@CustomLog` generates a slog `Logger` instead of an SLF4J one. - Register the slog jar in LICENSE-all.bin.txt, LICENSE-server.bin.txt and LICENSE-bkctl.bin.txt so the CI check-binary-license script finds it accounted for in the bundled-jars list. No actual Java file is converted in this commit. Individual module migrations stack on top of this one. * BP-69: Convert stream module from SLF4J to slog * Bump slog dependency to 0.9.8 Pulls in the MDC propagation fix from merlimat/slog#6, which makes log4j2 ThreadContext entries visible on slog events emitted via the Log4j2Logger backend (so %X{key} layouts and appenders that read event.getContextData().getValue(key) see the caller's MDC).
merlimat
added a commit
to apache/bookkeeper
that referenced
this pull request
Apr 28, 2026
…hase 4+6) (#4758) * BP-69 base: add slog dependency and LICENSE entries Minimal scaffolding commit for the BP-69 slog migration series: - Add `io.github.merlimat.slog:slog:0.9.7` to the root pom.xml dependencyManagement and to the global compile classpath alongside the existing SLF4J API (which stays as the rendering backend). - Add lombok.config at the repo root so `@CustomLog` generates a slog `Logger` instead of an SLF4J one. - Register the slog jar in LICENSE-all.bin.txt, LICENSE-server.bin.txt and LICENSE-bkctl.bin.txt so the CI check-binary-license script finds it accounted for in the bundled-jars list. No actual Java file is converted in this commit. Individual module migrations stack on top of this one. * BP-69: Convert http, tools, benchmark and metadata-drivers modules to slog * Bump slog dependency to 0.9.8 Pulls in the MDC propagation fix from merlimat/slog#6, which makes log4j2 ThreadContext entries visible on slog events emitted via the Log4j2Logger backend (so %X{key} layouts and appenders that read event.getContextData().getValue(key) see the caller's MDC).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Log4j2Logger.buildContextDatabuilds theStringMapfrom only slog'scontextAttrsand event attrs — it ignores the caller thread's log4j2ThreadContext. Layouts using%X{key}and appenders that readevent.getContextData().getValue(key)see nothing, even when the thread had MDC entries set at the time of the log call.The standard log4j2 logger handles this via
ContextDataInjectorinsideLogEventFactory. Slog'sLog4j2Loggerbuilds theMutableLogEventmanually for performance, and so must do the equivalent injection itself.Reproduction (pre-fix)
Fix
In
Log4j2Logger.buildContextData, after clearing the thread-localStringMap, copy the currentThreadContextentries in first viaThreadContext.getImmutableContext().forEach(map::putValue), then layer slog'scontextAttrs/ event attrs / duration on top so slog overrides on key collision.Performance
The fast path is preserved with one extra check:
ThreadContext.isEmpty()check (single field read on the static map). Returns the sameemptyFrozenContextData()singleton — no allocation, no map clear.getImmutableContext()(returns a backing-map reference, no copy) + aforEachloop to copy MDC entries into the existing thread-localStringMap. The copy has to happen anyway for the appender to see those entries.ThreadContext.getImmutableContext()returns the actual immutable map reference from the underlyingThreadContextMap— it does not allocate a copy.Map.forEachiterates without per-entry allocation.Tests
Added
Log4j2MdcPropagationTestwith 7 cases covering:info(String)and fluentinfo().log(...)pathsThreadContextafter the log callContextDataStringMapdoesn't retain stale entries between callsWithout the fix, 4 of 7 tests fail. With it, all 7 pass.