Add opt-in SLF4J facade logging (Slf4jLogger), keeping java.util.logging as default (#4276) - #5361
Conversation
…ing as default Adds an Slf4jLogger that routes ArcadeDB's logs through the SLF4J facade, so an application embedding ArcadeDB receives the engine's logs in whatever backend it already uses (Logback, Log4j2, reload4j, or java.util.logging via slf4j-jdk14) with no dependency exclusions. It is opt-in and non-disruptive: the default logger remains DefaultLogger (java.util.logging, with the existing text/ANSI and JSON formatting). Select SLF4J at startup with -Darcadedb.log.impl=slf4j, or programmatically via LogManager.setLogger(new Slf4jLogger()). - engine depends only on slf4j-api (the facade); it pins no backend. - Level mapping onto SLF4J's five levels; printf-style (%s) message formatting and <context> prefixing preserved; per-request correlation (requestId/database/ traceId/spanId) exposed via MDC under arcadedb.* keys, saved/restored around the call so the host application's MDC is left untouched. - Root pom: slf4j-jdk14 moved to test scope so the JUL binding never leaks transitively to consumers of the library modules. - examples/logging: Logback and Log4j2 configs showing console/JSON/file output, a configurable log directory, and feature parity with the native logger. Refs ArcadeData#4276
|
Tick the box to add this pull request to the merge queue (same as
|
There was a problem hiding this comment.
Code Review
This pull request introduces support for routing ArcadeDB's logs through the SLF4J facade, allowing embedding applications to receive engine logs in their own logging backend. It adds a new Slf4jLogger implementation, updates LogManager to support selecting the logger via a system property, and provides configuration examples for Logback and Log4j2. The review feedback suggests removing the local loggersCache in Slf4jLogger to prevent potential memory leaks and stale loggers during logging context reloads, delegating directly to LoggerFactory.getLogger instead.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 74 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
Drop the local loggersCache: LoggerFactory.getLogger already caches context-aware, and a local map risks stale loggers / a ClassLoader leak when the logging context is reloaded (containers, Spring devtools, tests). Addresses review feedback on ArcadeData#5361.
|
Thanks @gemini-code-assist — good catch. Removed the local |
|
Thank you for the update. The removal of the local cache and unused imports is a clean approach, as it correctly leverages the caching mechanism already provided by |
…og dir default - Extract the 17-argument null check into anyNonNull() to lower the fixed-arity log() method's complexity (keeps the allocation-free path). - Expand/clean javadoc on the new/changed methods (createLogger, slf4jLevel, anyNonNull, resolveLogger). - examples/logging: default log directory now ./log, matching the native arcadedb.server.logsDirectory default (was ./logs).
…ndings Fix a regression from moving slf4j-jdk14 to test scope: the runnable modules (console, package) lost the SLF4J->JUL binding, so third-party libraries logging via SLF4J (Undertow, Ratis, Gremlin, Micrometer) would go silent in the standalone product. Re-declare slf4j-jdk14 at runtime scope in console and package, so the distribution keeps routing those logs into java.util.logging while the library modules (engine, network) still expose only slf4j-api (no binding leak). Codacy: - move LogManager.LOG_IMPL_PROPERTY to the top of the class (fields before methods); - @SuppressWarnings("PMD.ExcessiveParameterList") on the 17-arg log() overload and anyNonNull() helper — the arity is mandated by the Logger interface's allocation-free path.
|
@robfrank whenever you have a moment — this is ready for review. It implements the opt-in SLF4J-facade logging we discussed in #4276: default logger stays |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #5361 +/- ##
============================================
- Coverage 66.43% 65.38% -1.06%
- Complexity 0 1103 +1103
============================================
Files 1717 1736 +19
Lines 140939 144974 +4035
Branches 30174 31074 +900
============================================
+ Hits 93636 94787 +1151
- Misses 34630 37305 +2675
- Partials 12673 12882 +209 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Heads-up on the red checks: they're all fork-PR permission/secret limitations, not test or build failures. Because this PR comes from a fork, the workflows run with a read-only
They should go green when run with the usual permissions on your side. Happy to rebase or adjust anything if it helps. |
|
I'll check it next week, It looks fine from the surface I need to check deeply and I'm mostly off line right now. |
The parent POM moved slf4j-jdk14 to test scope, but server/pom.xml still declared it at compile scope, so every application embedding arcadedb-server kept inheriting the JUL binding and still needed <exclusions>. Drop both SLF4J declarations from server: the slf4j-api facade already arrives from arcadedb-engine, and the runnable artifacts (console, package) declare the binding themselves at runtime scope. All four distribution flavours still bundle slf4j-api and slf4j-jdk14 in lib/. Also from review: - Slf4jLogger now binds to SLF4J in its constructor, so a missing slf4j-api surfaces where LogManager can still fall back to DefaultLogger instead of escaping as a NoClassDefFoundError from the first log statement. - LogManager reports an unrecognized arcadedb.log.impl value instead of silently behaving like the default, and createLogger() is package-private so the selection is testable. - Slf4jLogger mirrors DefaultLogger's shutdown bypass: once the JVM is shutting down the backend may already have stopped its appenders, so INFO and above go to System.err. - Document that the SLF4J logger never reads config/arcadedb-log.properties, so a packaged server selecting it loses the rolling file log unless JUL is pointed at that file explicitly. - Fix the parent POM comment to point at console/package rather than server, sync the slf4j rows in ATTRIBUTIONS.md to the pinned 2.0.18, and replace fully-qualified names with imports. Adds LogManagerLoggerSelectionTest (4 tests) and two shutdown-bypass tests; 50 logging tests green.
|
Thanks @ruispereira, this is a well-built contribution: the opt-in design is genuinely non-disruptive, the I reviewed it and pushed a follow-up commit (0163062) directly to the branch rather than sending you round another cycle. Here is what it changes and why. Two substantive fixes1. The binding was still leaking from The PR moves the parent's So any application embedding I removed both SLF4J declarations from I also fixed the comment in the parent POM, which pointed at 2. The documented fallback in The javadoc claimed the No fallback message, so the catch block never ran, and the Smaller fixes
TestsAdded Not changed, for a maintainer decision
Thanks again for the care that went into this one, particularly the parity table and the MDC restore semantics. |
Closes #4276 (opt-in first step).
Summary
Adds an opt-in
Slf4jLoggerthat routes ArcadeDB's logs through the SLF4J facade, so an application embedding ArcadeDB receives the engine's logs in whatever backend it already uses (Logback, Log4j2, reload4j, orjava.util.loggingviaslf4j-jdk14) — with no dependency exclusions.It is deliberately non-disruptive: the default logger is unchanged (
DefaultLogger,java.util.logging, with the existing text/ANSI and JSON formatting), so standalone and current deployments behave exactly as before. This matches the plan agreed in #4276; SLF4J can be made the default in a follow-up if you'd prefer.How to enable
Startup, no code change:
or programmatically:
What changed
engine/.../log/Slf4jLogger.java(new) — acom.arcadedb.log.Loggerthat delegates toorg.slf4j:java.util.logging.Levelonto SLF4J's five levels by numeric weight (SEVERE→ERROR,WARNING→WARN,INFO→INFO,FINE→DEBUG, elseTRACE), so custom/intermediate JUL levels also resolve;DefaultLogger: logger name resolved from the requester (String / Class / instance /com.arcadedbfallback),<context>prefixing, and printf-style (%s) message formatting (not SLF4J{}placeholders), which is how ArcadeDB messages are written;LogManager(requestId,database,traceId,spanId) to the backend via MDC underarcadedb.*keys, saving and restoring any pre-existing values so the host application's MDC is left untouched.engine/.../log/LogManager.java— selects the implementation from the newarcadedb.log.implsystem property (default =DefaultLogger;slf4j=Slf4jLogger); falls back toDefaultLoggerif construction fails, so a misconfiguration never silences logging.engine/pom.xml— addsslf4j-apionly. The engine pins no backend.pom.xml— movesslf4j-jdk14totestscope. It was a compile-scope dependency in the parent<dependencies>, so it leaked transitively to consumers of every library module (forcing exclusions). At test scope, module tests still log through JUL while nothing is imposed on downstream consumers.examples/logging/(new) — ready-to-uselogback.xmlandlog4j2.xml(console + JSON + rolling file, configurable log directory) and aREADME.mdmapping every native logging feature (arcadedb.server.logFormat,logIncludeTrace,logsDirectory, per-package levels, correlation) to its Logback/Log4j2 equivalent.DefaultLogger,ArcadeDBServer,GlobalConfigurationand the existing logging tests are untouched.Dependency hygiene
Embedding
arcadedb-enginenow pulls onlyslf4j-api; the embedder provides exactly one binding and needs no<exclusions>. To get plain JUL, addslf4j-jdk14and SLF4J forwards to it.Backward compatibility
Default behaviour is unchanged (JUL
DefaultLogger), so there is no regression for standalone or existing embedders. SLF4J routing is entirely opt-in.Testing
Slf4jLoggerTest(12 tests): level mapping, printf substitution,<context>prefixing, logger-name resolution (String/Class/null), the fixed-arity overload, level gating, literal%without args, best-effort fallback when a format string doesn't match its args, throwable pass-through, and MDC publish/restore (including preserving a host value under anarcadedb.*key).LoggerTest,DefaultLoggerLogDirTest,DefaultFormatterUnchangedTest,LogCorrelationContextTest,LogFormatterTraceTagTest,JsonLogFormatterTest,LogFormatterMessageFormatTest) — 48 logging tests green in total.arcadedb-enginetest suite: the only failure is a pre-existing one onmain(LSMTreeSortedBuildCrashTest.crashAfterAttachmentLeavesSchemaUnpublishedAndAllowsRetry), reproduced identically on a clean checkout without this change — unrelated to logging.slf4j-jdk14): logs are emitted by Logback with correct level mapping,%ssubstitution, MDC correlation populated during the call and cleared afterwards.Note
One small thing worth a maintainer's eye: the repo's
.gitignorerule**/logalso matches the source packagecom/arcadedb/log/, so the two new files there had to begit add -f'd. You may want to tighten that rule (e.g. anchor it to build output dirs) so new sources under that package aren't silently ignored.