fix(logging): stop logback.xml from shadowing logback-spring.xml - #189
Open
adityamparikh wants to merge 1 commit into
Open
fix(logging): stop logback.xml from shadowing logback-spring.xml#189adityamparikh wants to merge 1 commit into
adityamparikh wants to merge 1 commit into
Conversation
Spring Boot resolves the standard Logback locations (logback-test.xml,
logback.xml, ...) first in AbstractLoggingSystem.initializeWithConventions().
When one is found and logging.file.name is unset, Boot reinitializes from that
file and returns, so logback-spring.xml is never loaded at all.
This project shipped both files, so the <springProfile> blocks in
logback-spring.xml -- which hold the only appenders -- never took effect. HTTP
mode ran with no appenders whatsoever: no console logging and no OTLP log
export. A failed startup then exited 1 printing nothing but the Spring banner,
leaving Gradle to report only "finished with non-zero exit value 1", which
makes any HTTP-mode startup failure effectively undiagnosable.
Delete logback.xml and keep logback-spring.xml as the single logging
configuration. This is what Boot's reference documentation recommends: use the
-spring variant "to allow Spring to fully control log initialization", and
<springProfile> "cannot be used in the standard logback.xml file because it is
loaded too early". The NopStatusListener that logback.xml carried is already
declared in logback-spring.xml, so removing the file activates that
suppression rather than dropping it.
Also in this change:
* Use Boot's own console-appender.xml instead of a hand-rolled ConsoleAppender,
so logging.pattern.console, logging.charset.console and
logging.threshold.console behave as they do in a stock Boot application. The
hand-rolled encoder's fallback was already dead code -- defaults.xml defines
CONSOLE_LOG_PATTERN, so ${CONSOLE_LOG_PATTERN:-...} always resolved to Boot's
pattern.
* Register logback-spring.xml, not logback.xml, as a native-image resource.
* Add LoggingConfigurationTest, which fails the build if any standard-location
Logback file reappears and re-triggers the shadowing.
* Correct the Logging Architecture section of AGENTS.md, which described
logback-spring.xml as overriding logback.xml -- the intent, not the
behaviour.
Verified: PROFILES=http ./gradlew bootRun emits log output with no
LOGGING_CONFIG override (0 lines before this change, 34 after). STDIO stdout
cleanliness is covered by McpClientStdioIntegrationTest, which drives the MCP
JSON-RPC workflow over the real jar's stdout; it passes 39/39, and the full
./gradlew build is green at 391 tests, 0 failures.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bbs8w62uwcx12ZE8E2xg8P
Signed-off-by: Aditya Parikh <aditya.m.parikh@gmail.com>
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.
The bug
logback-spring.xmlis never loaded. HTTP mode therefore runs with no appenders at all — no console logging and no OTLP log export — and any startup failure exits 1 printing nothing but the Spring banner.Reproduce on
main:Output is the banner, four JVM
Unsafewarnings, then:No stack trace, no failure analyzer, no
APPLICATION FAILED TO START. Force the config and the real error appears:Root cause
Spring Boot's
AbstractLoggingSystem.initializeWithConventions()checks the standard Logback locations first (logback-test.xml,logback.xml, …). If it finds one andlogging.file.nameis unset, it callsreinitialize()and returns early —logback-spring.xmlis never consulted.This repo shipped both files.
logback.xmlcontains only aNopStatusListenerand no appenders, so it won a race it was never meant to enter, and the<springProfile>blocks holding CONSOLE and OTEL silently disappeared.This is Boot's documented rule, not an edge case:
The fix
Delete
logback.xml;logback-spring.xmlbecomes the single logging configuration. TheNopStatusListenerwas already declared inlogback-spring.xml, so deleting the other file activates the suppression rather than removing it.Also in this PR:
console-appender.xmlinstead of a hand-rolledConsoleAppender, sologging.pattern.console/logging.charset.console/logging.threshold.consolebehave as in a stock Boot app. The hand-rolled encoder's fallback was already dead code:defaults.xmldefinesCONSOLE_LOG_PATTERN, so${CONSOLE_LOG_PATTERN:-…}always resolved to Boot's pattern — the rendered format is unchanged.logback-spring.xml(notlogback.xml) as a native-image resource inSolrNativeHints.LoggingConfigurationTest, which fails the build if any standard-location Logback file reappears.AGENTS.md, which claimed "logback-spring.xml— Loaded by Spring Boot, overrideslogback.xml." That was the intent, not the behaviour.Why removing logback.xml is safe for STDIO
logback.xmlwas justified as "Required for native image where logback falls through toBasicConfigurator." The STDIO transport's stdout cleanliness is directly covered byMcpClientStdioIntegrationTest, which spawns the realjava -jarand runs the full MCP JSON-RPC workflow — a single stray stdout line fails it.Verification
./gradlew buildMcpClientStdioIntegrationTest(real jar, MCP over stdout)LoggingConfigurationTestmain)PROFILES=http ./gradlew bootRun, no override./gradlew nativeTest -PnativeThe native run is a meaningful check here, not a formality: in a native image
getResourceonly sees registered resources, soLoggingConfigurationTestpassing there confirms the hint swap actually took effect in the closed world —logback.xmlis absent andlogback-spring.xmlis reachable. Both of its tests ran natively (not skipped).For reference, the 144 skips are 142 pre-existing plus the two
@DisabledInNativeImageApplicationContextRunnertests that arrive with an unrelated branch in my local tree; they are not part of this PR.🤖 Generated with Claude Code
https://claude.ai/code/session_01Bbs8w62uwcx12ZE8E2xg8P