Use SLF4J for runtime diagnostics - #15993
Conversation
Assisted-by: opencode:gpt-5.6-sol
There was a problem hiding this comment.
Pull request overview
This PR replaces direct printStackTrace() / System.out.println() runtime diagnostics with SLF4J logging across several Grails runtime paths (CLI startup, Spring application runner lifecycle, plugin manager profiling, schema export, and Forge POM parsing), while adding regression tests that validate behavior both with and without an SLF4J provider on the classpath.
Changes:
- Switched multiple runtime error/reporting paths from raw stack traces / console output to structured SLF4J logging with fallback-to-stderr behavior where appropriate.
- Added focused, forked-process regression specs to verify provider-enabled vs API-only (no provider) behavior and to prevent stderr/log duplication.
- Updated test dependencies to support provider-backed assertions (Logback appenders) and provider/no-provider classpath variants.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| grails-shell-cli/src/test/resources/org/grails/cli/compiler/dependencies/spring-boot-dependencies-effective-bom.xml | Adds a minimal effective BOM test resource fixture. |
| grails-shell-cli/src/test/groovy/org/grails/cli/GrailsCliSpec.groovy | New forked-process spec verifying shared settings parse failures are logged and surfaced on stderr. |
| grails-shell-cli/src/test/groovy/org/grails/cli/GrailsCliLoggingVerifier.groovy | Logback-based verifier that asserts a single ERROR event and stderr prefix behavior. |
| grails-shell-cli/src/test/groovy/org/grails/cli/command/run/SpringApplicationRunnerSpec.groovy | New forked-process lifecycle spec verifying failure reporting with logging on/off. |
| grails-shell-cli/src/test/groovy/org/grails/cli/command/run/LifecycleVerifier.groovy | Logback-based verifier for runner lifecycle errors and stderr duplication rules. |
| grails-shell-cli/src/main/groovy/org/grails/cli/GrailsCli.groovy | Replaces settings load printStackTrace() with LOG.error(..., e) while preserving stderr summary. |
| grails-shell-cli/src/main/groovy/org/grails/cli/command/run/SpringApplicationRunner.java | Replaces stack traces with LOG.error/warn when enabled, otherwise prints stack traces to stderr. |
| grails-shell-cli/build.gradle | Adds Logback for tests (and adjusts logging-related dependencies). |
| grails-forge/grails-forge-core/src/test/groovy/org/grails/forge/build/dependencies/PomDependencyVersionResolverSpec.groovy | Adds malformed-POM test ensuring parsing failures are WARN-logged and skipped. |
| grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/dependencies/PomDependencyVersionResolver.java | Replaces printStackTrace() with LOG.warn(...) on malformed POM parsing. |
| grails-forge/grails-forge-core/build.gradle | Moves Logback dependency to testImplementation to support logback-based tests. |
| grails-data-hibernate7/grails-plugin/src/test/groovy/grails/plugin/hibernate/commands/SchemaExportCommandSpec.groovy | New forked-process spec verifying schema export failures are visible with/without a provider. |
| grails-data-hibernate7/grails-plugin/src/main/groovy/grails/plugin/hibernate/commands/SchemaExportCommand.groovy | Uses SLF4J for schema export failures when enabled; falls back to stderr stack traces otherwise. |
| grails-data-hibernate7/grails-plugin/build.gradle | Adds slf4j-simple test runtime for provider-enabled scenario. |
| grails-data-hibernate5/grails-plugin/src/test/groovy/grails/plugin/hibernate/commands/SchemaExportCommandSpec.groovy | New forked-process spec mirroring Hibernate 7 coverage. |
| grails-data-hibernate5/grails-plugin/src/main/groovy/grails/plugin/hibernate/commands/SchemaExportCommand.groovy | Same SLF4J + stderr fallback behavior for Hibernate 5 schema export. |
| grails-data-hibernate5/grails-plugin/build.gradle | Adds slf4j-simple test runtime for provider-enabled scenario. |
| grails-core/src/test/groovy/org/grails/plugins/ProfilingGrailsPluginManagerSpec.groovy | New spec asserting profiling output is emitted via SLF4J (stderr), not stdout. |
| grails-core/src/main/groovy/org/grails/plugins/ProfilingGrailsPluginManager.java | Replaces stdout profiling messages with SLF4J info while preserving logger category. |
| grails-bootstrap/src/test/groovy/grails/build/logging/GrailsConsoleLoggingSpec.groovy | New forked-process spec verifying invalid console configuration is visible with/without provider. |
| grails-bootstrap/src/main/groovy/grails/build/logging/GrailsConsole.java | Logs console instantiation failures via SLF4J when enabled; otherwise prints stack trace. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Testing | ||
| testImplementation 'org.slf4j:slf4j-simple' | ||
| testImplementation 'ch.qos.logback:logback-classic' | ||
| testImplementation 'org.spockframework:spock-core' |
|
The concern regarding multiple SLF4J providers on the test classpath is valid, as SLF4J 2.x can exhibit non-deterministic behavior in such environments. The current PR introduces a new test, grails-bootstrap/src/test/groovy/grails/build/logging/GrailsConsoleLoggingSpec.groovy |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## 8.0.x #15993 +/- ##
==================================================
+ Coverage 51.4814% 51.5073% +0.0259%
- Complexity 17775 17786 +11
==================================================
Files 2039 2039
Lines 95586 95600 +14
Branches 16591 16594 +3
==================================================
+ Hits 49209 49241 +32
+ Misses 39066 39047 -19
- Partials 7311 7312 +1
🚀 New features to boost your workflow:
|
Assisted-by: opencode:gpt-5.6-sol
|
It's good to see this can work - I remember reading comments in the code base that historically this couldn't work due to some edge case. I'll see if I can dig up the history to make sure this is indeed safe now and then review. |
Copilot flagged that grails-shell-cli's test classpath carries both slf4j-simple and logback-classic simultaneously, which SLF4J 2.x treats as an ambiguous binding. Verified this is real: `dependencies --configuration testRuntimeClasspath` resolves both providers together, and neither can simply be dropped - slf4j-simple is needed at runtime for the actual `grails` CLI executable's production logging, and logback-classic is needed by the new Logback-appender-based forked regression specs (SpringApplicationRunnerSpec, GrailsCliSpec). Confirmed no current regression from this (full :grails-shell-cli:test run is green, no "multiple SLF4J providers" warning appears in any test output) because both new specs that need a specific provider already pin it explicitly via -Dslf4j.provider=... in their forked child process. This commit removes the now-fully-redundant duplicate `testImplementation 'org.slf4j:slf4j-simple'` line (already pulled in via the runtimeOnly dependency, so this doesn't change the resolved classpath) and documents the coexistence so a future forked-process fixture doesn't skip the explicit pin and hit non-deterministic provider selection. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…age move
The 8.0.x branch relocated ApplicationCommand/ExecutionContext and each
SchemaExportCommand into a dedicated cli source set with new packages
(grails.dev.commands.ExecutionContext -> org.apache.grails.core.cli.
ExecutionContext, grails.plugin.hibernate.commands.SchemaExportCommand
-> org.apache.grails.data.hibernate{5,7}.cli.SchemaExportCommand) as
part of unrelated work that landed after this PR's base commit.
Merging origin/8.0.x picked this up cleanly via rename tracking for the
production SchemaExportCommand.groovy files (already updated to the new
package on merge), but the two new SchemaExportCommandSpec.groovy test
files added by this PR had no prior history for git to track the rename
through, so they were merged in unchanged - still importing the old
ExecutionContext package and referencing SchemaExportCommand unqualified
(previously fine only because the spec shared its production class's old
package). Both left compileTestGroovy failing after the merge. Fixed by
importing both classes from their new locations.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
@jdaugherty — some findings that might help with your investigation into whether this can safely work. I searched git history broadly for the historical comment/reason you're remembering (grepped commit messages and blamed the touched files - What I did instead: identified the most plausible place the "edge case" could bite, and tested it directly. That doesn't happen here, structurally: I verified this empirically rather than just reasoning about it: ran Separately, I confirmed and fixed the multi-SLF4J-provider classpath issue Copilot flagged ( This is evidence for your review, not a claim that the question is settled - happy to dig further if you want a different angle tested. |
Codecov reported 13.88% patch coverage because the fixture specs verify the new logging behavior in forked JVMs that JaCoCo never instruments. Forward the test JVM's JaCoCo -javaagent argument into the forked processes so their execution data appends to the module exec file, and exercise the remaining ProfilingGrailsPluginManager configuration phases in-process with a probe plugin. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
🚨 TestLens detected 18 failed tests 🚨Here is what you can do:
Test SummaryCI - Groovy Joint Validation Build / build_grails > :grails-core:test
CI / Build Grails-Core (macos-latest, 21) > :grails-core:test
CI / Build Grails-Core (ubuntu-latest, 21) > :grails-core:test
CI / Build Grails-Core (ubuntu-latest, 25) > :grails-core:test
CI / Build Grails-Core (windows-latest, 25) > :grails-core:test
CI / Build Grails-Core Rerunning all Tasks (ubuntu-latest, 21) > :grails-core:test
🏷️ Commit: c75b73e Test Failures (first 10 of 18)ProfilingGrailsPluginManagerSpec > configuration phases emit INFO profiling messages for each plugin (:grails-core:test in CI / Build Grails-Core (macos-latest, 21))ProfilingGrailsPluginManagerSpec > deprecated constructors log their warning in the historical category (:grails-core:test in CI / Build Grails-Core (macos-latest, 21))ProfilingGrailsPluginManagerSpec > plugin loading emits INFO profiling messages instead of standard output (:grails-core:test in CI / Build Grails-Core (macos-latest, 21))ProfilingGrailsPluginManagerSpec > configuration phases emit INFO profiling messages for each plugin (:grails-core:test in CI / Build Grails-Core (ubuntu-latest, 21))ProfilingGrailsPluginManagerSpec > deprecated constructors log their warning in the historical category (:grails-core:test in CI / Build Grails-Core (ubuntu-latest, 21))ProfilingGrailsPluginManagerSpec > plugin loading emits INFO profiling messages instead of standard output (:grails-core:test in CI / Build Grails-Core (ubuntu-latest, 21))ProfilingGrailsPluginManagerSpec > configuration phases emit INFO profiling messages for each plugin (:grails-core:test in CI / Build Grails-Core (ubuntu-latest, 25))ProfilingGrailsPluginManagerSpec > deprecated constructors log their warning in the historical category (:grails-core:test in CI / Build Grails-Core (ubuntu-latest, 25))ProfilingGrailsPluginManagerSpec > plugin loading emits INFO profiling messages instead of standard output (:grails-core:test in CI / Build Grails-Core (ubuntu-latest, 25))ProfilingGrailsPluginManagerSpec > configuration phases emit INFO profiling messages for each plugin (:grails-core:test in CI / Build Grails-Core (windows-latest, 25))Muted TestsSelect tests to mute in this pull request:
Reuse successful test results:
Click the checkbox to trigger a rerun:
Learn more about TestLens at testlens.app. |
Summary
Verification
This is an AI-generated starting point for the broader runtime logging cleanup.