Skip to content

Fix JulLogger CPU burn from unconditional stack-trace walk (#1511)#1515

Open
msrathore-db wants to merge 1 commit into
mainfrom
fix/issue-1511-jullogger-cpu
Open

Fix JulLogger CPU burn from unconditional stack-trace walk (#1511)#1515
msrathore-db wants to merge 1 commit into
mainfrom
fix/issue-1511-jullogger-cpu

Conversation

@msrathore-db

Copy link
Copy Markdown
Collaborator

Summary

Fixes #1511.

JulLogger.log() called getCaller() — which walks the full thread stack via Thread.currentThread().getStackTrace()before checking whether the level was loggable, and the {}-format overloads ran String.format() eagerly. DatabricksArray logs once per element during type conversion, so reading array/complex-type columns paid for a full stack walk per element even at the default INFO level (where FINEST/FINE records are discarded), dominating JDBC reader-thread CPU.

Change

  • log() short-circuits on logger.isLoggable(level) before invoking the getCaller() stack walk.
  • Each {}-format overload (trace/debug/info/warn/error) guards String.format(...) behind the matching isLoggable(...) check, so disabled levels do no formatting work.

Behavior is unchanged — JUL discards those records anyway; only the wasted work is removed.

Repro / measurement

A 5M-element loop simulating per-element trace logging with the driver logger at INFO, run against the compiled driver on an identical classpath:

CPU time
Before ~4,170 ms
After ~4 ms

Tests

  • Added regression tests testNoLoggingWhenLevelDisabled and testFormatVariantsSkipFormattingWhenLevelDisabled (the latter uses an exploding toString() argument to prove the format path is skipped when the level is disabled).
  • Updated JulLoggerTest setUp to stub isLoggable → true so existing verify-based tests still exercise the logging path.
  • JulLoggerTest 18/18 and DatabricksArrayTest/Slf4jLoggerTest/JdbcLoggerFactoryTest 43/43 pass; spotless:check passes.

NO_CHANGELOG=true

This pull request and its description were written by Isaac.

JulLogger.log() called getCaller() — which walks the full thread stack
via Thread.currentThread().getStackTrace() — before checking whether the
level was loggable, and the {}-format overloads ran String.format()
eagerly. DatabricksArray logs once per element during type conversion,
so reading array/complex-type columns paid for a full stack walk per
element even at the default INFO level where those records are discarded,
dominating reader-thread CPU.

Short-circuit on logger.isLoggable(level) before getCaller(), and guard
each {}-format overload behind the matching isLoggable check so disabled
levels do no formatting work. Behavior is unchanged (JUL discards those
records anyway); a 5M-element disabled-level trace loop drops from
~4,170 ms to ~4 ms of CPU.

Co-authored-by: Isaac
Signed-off-by: Madhavendra Rathore <madhavendra.rathore@databricks.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] JulLogger burns CPU for logging array/complex-type results even when not activated

1 participant