[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 21 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
Context used✅ Compliance rules (platform):
18 rules 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 59383f6 |
|
@titusfortner all clean and ready for your review. 👍 |
|
Code review by qodo was updated up to the latest commit b35594b |
|
Code review by qodo was updated up to the latest commit 529bb21 |
…() instead of static LOG_LEVEL Avoids snapshotting the debug log level at class-init time so runtime switches (SE_DEBUG, -Dselenium.debug) are respected. /act-as-mohab /test-driven-development Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Code review by qodo was updated up to the latest commit 96ec9cf |
… to avoid installing handlers when construction fails - Bidi Connection: call configureLogger() after argument checks - DevTools Connection: validate and compute wsClientConfig before configureLogger() - RemoteWebDriver: call configureLogger() after non-null checks /act-as-mohab /test-driven-development
|
Code review by qodo was updated up to the latest commit ae54eb0 |
|
Code review by qodo was updated up to the latest commit f9e03d9 |
|
My original thought was that we should pick one of the other options for addressing #12892, but after some research I think this direction is correct, but requires agreeing on the semantics based on thee 4 things. I put my opinions here. @diemol / @pujagani / @asolntsev do you have different opinions for these? Any other considerations?
For the Qodo feedback: I don't think we should re-cache Beyond that, I think this PR should:
|
|
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. |
🔗 Related Issues
Closes #12892
💥 What does this PR do?
Makes
-Dselenium.debug=trueand the legacy-Dselenium.webdriver.verbose=trueconfigure Java JUL diagnostics fororg.openqa.selenium. When enabled, Selenium raises that logger toFINEonly when needed and installs its own console handler for records below
INFO.This is a Java-binding convenience, not a full
SE_DEBUGalias.SE_DEBUGremains the broader cross-binding maintainer/CI switch; the shared Java
configuration mechanism gives each switch an explicit branch and requested
level rather than inferring the scope from a combined boolean.
The change does not modify Grid root logging, Grid output routing, or
user-installed JUL handlers. Retry diagnostics now use the fixed
FINElevel; migrating the remaining deprecated
getDebugLogLevel()call sites isleft for a follow-up.
🧪 Testing
//java/test/org/openqa/selenium/internal:DebugTest//java/test/org/openqa/selenium/remote/http:RetryRequestTest//java/test/org/openqa/selenium/devtools:ConnectionTest//java/test/org/openqa/selenium/remote:RemoteWebDriverInitializationTestThe logger tests cover preserving user handlers and the root logger,
retaining explicit/inherited
FINER,FINEST, andALLlevels, and repairingthe Selenium-owned handler and logger level after external resets while debug
remains enabled.
🔄 Types of changes
🤖 AI assistance
human-directed and reviewed.