[java] Make selenium.debug/webdriver.verbose configure the real logger (Mechanism Only) - #17841
[java] Make selenium.debug/webdriver.verbose configure the real logger (Mechanism Only)#17841MohabMohie wants to merge 29 commits into
Conversation
Debug.java now owns a single configureLogger() entry point that installs a level-aware handler on the shared Selenium logger, replacing the old pair of switches that didn't actually agree with each other. LoggingOptions and RemoteWebDriver call into it so Grid and driver sessions both honor the same debug-log level, closing SeleniumHQ#12892. Adds DebugTest, LoggingOptionsTest, RemoteWebDriverInitializationTest, and devtools/ConnectionTest as the direct tests for this mechanism.
…tructed Connections BiDi and CDP Connection instances built without going through RemoteWebDriver or DriverFinder (a caller wiring one up directly) never triggered the debug-log-level configuration, so their FINE wire diagnostics stayed governed by whatever level was set last -- or never set at all. Both constructors now call Debug.configureLogger() first, same idempotent pattern DriverFinder.getBinaryPaths() already uses.
PR Summary by Qodo[java] Make selenium.debug/webdriver.verbose configure the real logger
AI Description
Diagram
High-Level Assessment
Files changed (12)
|
Code Review by Qodo
1.
|
…tors Qodo review findings 1 and 2 on SeleniumHQ#17841: the BiDi and CDP Connection constructors modified to call Debug.configureLogger() had no Javadoc, so API consumers had no way to discover the parameters or the debug- logging side effect. Adds Javadoc mirroring RemoteWebDriver's canonical-constructor doc block in this same branch. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LfxetzdJtF13MCJDRNQwgG
…s' reset exemption to it Fixes Qodo review findings 3-6 on SeleniumHQ#17841: - Debug.configureLogger()'s fast-path used to key solely on shouldDebug == loggerConfigured, so if something outside this class removed the installed handler while debugging stayed on (a JUL LogManager.reset(), or unrelated removeHandler() code), the handler was never reinstalled. The fast-path now also consults isHandlerCurrentlyInstalled(), and the level-raising bookkeeping only runs on a genuine off->on transition so a repair-only call can't corrupt the snapshot turning debug off later needs. The turn-off path also guards against installedHandler already being null, since Logger.removeHandler(null) throws NPE. - LoggingOptions.configureLogging()'s handler-reset loop used to skip org.openqa.selenium unconditionally, preserving any handler attached there, not just the one Debug.configureLogger() installed. Adds Debug.isOwnHandler(Handler) so the loop can strip everything else and keep only Debug's own. - Adds missing Javadoc to configureLogging(). DebugTest and LoggingOptionsTest each gain a regression test exercising the fixed behavior, watched red against the prior code before the fix landed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LfxetzdJtF13MCJDRNQwgG
|
Thanks for the review, @qodo-code-review — addressed all six findings in two new commits ( 1 & 2 — Missing Javadoc on the BiDi/CDP 3 — Missing Javadoc on 4 — 5 — 6 — One note on finding 6's suggested verification approach: the "snapshot handler count before/after" check in the original suggestion doesn't actually catch this bug in this codebase — All four scoped Bazel test targets pass: |
Thanks for the detailed follow-up. The two commits and regression coverage address all six findings:
The identity-based handler assertion is the appropriate verification here, since a count-only assertion would be masked by |
|
Code review by qodo was updated up to the latest commit 4296a51 |
…ts own Qodo review finding on SeleniumHQ#17841 (comment 3672251850): configureLogger() treated a null own level on org.openqa.selenium as always needing a raise to FINE, even when a parent logger already had an explicit, more-verbose level (e.g. FINER) -- silently clobbering inherited verbosity down to FINE. Adds a small effectiveLevel() helper that walks the logger's parent chain to the first non-null level (falling back to Level.INFO), and uses that instead of the logger's own level to decide whether a raise is needed. What gets snapshotted/restored on turn-off is still the logger's own (possibly null) level, unchanged. Fixing this surfaced a real order-dependent brittleness in LoggingOptionsTest#configureLoggingRaisesSeleniumLoggerEvenWithExternalJulConfigSet: it asserted the logger's own explicit level was FINE, which no longer holds once an earlier test in the same JVM already left an ancestor logger (the root logger, via this same configureLogging() path) at FINE -- correctly, there's nothing left to raise. Updated the assertion to check effective loggability, which is what the test was actually meant to prove. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LfxetzdJtF13MCJDRNQwgG
|
Code review by qodo was updated up to the latest commit 072512c |
…he inherited-level test Qodo review finding on SeleniumHQ#17841 (comment 3672369609): configureLoggerDoesNotClampAnInheritedMoreVerboseEffectiveLevel asserted org.openqa.selenium's own level was null as a starting-state precondition but never enforced it -- if an earlier test in the same JVM run had left an explicit (non-null) level set, this test would fail on that precondition before ever exercising the behavior under test. @AfterEach already restores the logger's own level after every test, so forcing it to null at the start of this test is safe and cannot leak into others. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LfxetzdJtF13MCJDRNQwgG
|
Code review by qodo was updated up to the latest commit 4d51cec |
|
Code review by qodo was updated up to the latest commit c569d65 |
…er on distinct destinations Qodo review finding on SeleniumHQ#17841 (comment 3672531999, "Grid drops debug logs"): rootHandlerFilter() suppressed org.openqa.selenium FINE/CONFIG records from Grid's root handler whenever no log-file was configured, on the assumption that destination always coincides with Debug's own stderr-only ConsoleHandler. That's only true when Debug.isDebugAll() (SE_DEBUG) is set: getOutputStream() only defaults to System.err in that case. With debugging enabled instead via -Dselenium.debug=true/-Dselenium.webdriver.verbose=true (no SE_DEBUG), getOutputStream() defaults to System.out -- a genuinely different destination from Debug's handler -- so the suppression made Selenium's FINE-level wire diagnostics invisible to a Grid operator watching stdout/structured log output, this PR's own advertised primary use case. Narrows the suppression condition to !logFileConfigured && Debug.isDebugAll(), matching getOutputStream()'s own exact condition for routing to stderr. Investigating this surfaced that the existing regression test (configureLoggingDoesNotDuplicateSeleniumDebugRecordsWhenNoLogFileIsConfigured) only ever set the selenium.debug property, never the SE_DEBUG environment variable its own comment claimed to cover -- it was unknowingly exercising the very branch this fix changes, and would have started failing once the production fix landed. Corrected its setup to genuinely exercise Debug.isDebugAll() by setting SE_DEBUG via an environment-variable stub (uk.org.webcompere:system-stubs, following the existing pattern already used in remote/service/DriverFinderTest.java), rather than leaving a now-contradictory assertion in place. Added a new test proving the fixed scenario -- selenium.debug=true without SE_DEBUG, no log-file -- where the record must now reach Grid's root handler (stdout) as well as Debug's own (stderr), since they are genuinely different destinations there. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LfxetzdJtF13MCJDRNQwgG
|
Code review by qodo was updated up to the latest commit 325168c |
With SE_DEBUG stubbed on and no log-file, BOTH Grid's root handler and Debug's own ConsoleHandler target System.err -- nothing goes to stdout in this scenario at all, so the old `captured.out()` doesNotContain assertion was vacuously true and a real duplication regression (the marker printed twice to stderr, once per handler) would have passed unnoticed. Verified by mutation: with rootHandlerFilter()'s suppression temporarily disabled, the old assertions still passed while containsOnlyOnce correctly failed with "appeared 2 times". The stdout check stays as a secondary sanity check. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LfxetzdJtF13MCJDRNQwgG
The @AfterEach cleanup calls Debug.configureLogger() to re-sync Debug with the restored switch state, but that only works for switches restored synchronously inside the method itself (the plain system properties). The stubbed SE_DEBUG is restored by SystemStubsExtension's own lifecycle callback, which runs AFTER user @AfterEach methods complete -- so the re-sync still saw SE_DEBUG=true, kept Debug's handler installed, and the stub's later restoration went unnoticed, leaking the handler (and Debug's loggerConfigured bookkeeping) into later tests in the same JVM. Explicitly setting the stub to "false" first makes configureLogger() see the correct final state and tear the handler down deterministically. Regression test invokes the real cleanup method and was watched fail (handler still installed) before the fix, pass after. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LfxetzdJtF13MCJDRNQwgG
|
Code review by qodo was updated up to the latest commit 9b8a93e |
|
Code review by qodo was updated up to the latest commit f87fd10 |
|
@titusfortner updated accordingly. Kindly check. |
|
Only 2 issues I see, but I still want other maintainers to agree to approach, I'll ping in Slack.
|
|
Qodo's scan has completed for c7ba454. The two remaining summary recommendations are intentionally not adopted: changing retry diagnostics to |
|
Code review by qodo was updated up to the latest commit c7ba454 |
|
Code review by qodo was updated up to the latest commit 7372c1e |
|
Code review by qodo was updated up to the latest commit 6b4b204 |
|
Code review by qodo was updated up to the latest commit 5ed151e |
|
Code review by qodo was updated up to the latest commit a503868 |
|
Code review by qodo was updated up to the latest commit 1c44aca |
Related Issues
Part of #12892
Follow-up call-site migrations are tracked in #17835.
What does this PR do?
Makes
-Dselenium.debug=true, the legacy-Dselenium.webdriver.verbose=true, andSE_DEBUGconfigure Java JULdiagnostics on the
org.openqa.seleniumlogger.The mechanism:
FINEonly when needed;INFO, without modifying caller-owned handlers or the root logger;FINEloggability after external divergence; andwhen debugging is disabled.
RemoteWebDriverapplies the live configuration for each construction whileretaining the existing early class-initialization hook for driver-discovery
logs.
getDebugLogLevel()is deprecated but retains its legacy behavior whilethe remaining callers move to fixed levels in small follow-up PRs.
BiDi/CDP constructor wiring, Grid changes, and bulk call-site migrations are
intentionally outside this mechanism-only PR.
Testing
//java/test/org/openqa/selenium/internal:DebugTest//java/test/org/openqa/selenium/remote:RemoteWebDriverInitializationTestThe focused tests cover live properties, legacy compatibility, owned-handler
lifecycle and idempotence, explicit and inherited more-verbose levels, user
handler/root logger preservation, duplicate INFO suppression,
SE_DEBUG, andrepair after external handler/level changes.
Types of changes
AI assistance
human-directed and reviewed.