Management Logging — Logback & Log4j2 Backends
The juneau-rest-server-management-logging module ships opt-in LogBackend adapters that let the /loggers
management endpoint drive Logback or Log4j2 instead of the built-in java.util.logging default.
Background: the /loggers endpoint
The /loggers runtime log-level management endpoint is built into juneau-rest-server — no extra module is
needed to expose it. It surfaces as:
| Method | Path | Effect |
|---|---|---|
GET | /loggers | Lists all known loggers and their configured levels (empty string = inherited). |
GET | /loggers/{name} | Returns one logger's configured level. 404 if not registered. |
PUT / POST | /loggers/{name} | Sets a logger's level at runtime. Disabled by default — see below. |
Wire the endpoint into your resource using one of two forms:
Mixin (adds /loggers paths directly to the host resource):
@Rest(mixins = LoggersMixin.class)
public class MyRestServlet extends BasicRestServlet { ... }
Child resource (mounts at /loggers as a sub-resource):
@Rest(children = LoggersResource.class)
public class RootResources extends BasicRestServlet { ... }
The default backend is java.util.logging. That is the only backend shipped in core juneau-rest-server, which
keeps the module dependency-free. juneau-rest-server-management-logging is only needed when your application
logs through Logback or Log4j2.
Maven dependency
<dependency>
<groupId>org.apache.juneau</groupId>
<artifactId>juneau-rest-server-management-logging</artifactId>
<version>${juneau.version}</version>
</dependency>
Both logback-classic and log4j-core are declared provided in this module — they never leak as transitive
dependencies. Declare whichever backend runtime you use in your own POM, at the version you want.
Choosing a backend
Default — java.util.logging (no extra module)
JulLogBackend is the default. When no LoggersSettings bean is registered, the endpoint drives JUL
automatically. Level names are JUL-standard: FINEST, FINER, FINE, CONFIG, INFO, WARNING,
SEVERE, OFF, ALL. The root logger is addressed as "ROOT".
Logback
@Bean
public LoggersSettings loggersSettings() {
return LoggersSettings.create()
.backend(new LogbackLogBackend())
.build();
}
LogbackLogBackend drives the concrete Logback LoggerContext — not the SLF4J facade. An
SLF4J → Logback binding routes here because the emitting backend is Logback.
Level names are Logback-standard: TRACE, DEBUG, INFO, WARN, ERROR, OFF. A logger whose own level
is not set returns an empty string (inherited from ancestor). The root logger is addressed as "ROOT".
Constructor options:
// Uses the SLF4J-bound LoggerContext (fails fast if SLF4J is not bound to Logback).
new LogbackLogBackend()
// Explicit LoggerContext — for tests or multi-context setups.
new LogbackLogBackend(loggerContext)
Log4j2
@Bean
public LoggersSettings loggersSettings() {
return LoggersSettings.create()
.backend(new Log4j2LogBackend())
.build();
}
Log4j2LogBackend drives the Log4j2 LoggerContext / Configuration. An SLF4J → Log4j2 binding routes here
because the emitting backend is Log4j2.
Level names are Log4j2-standard: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF, ALL. Unlike JUL
and Logback, every LoggerConfig in the configuration carries a concrete level — there is no "inherited / no
own level" sentinel, so reads return a level name (never an empty string) for configured loggers, and null
for names that have no LoggerConfig. The root logger is addressed as "ROOT".
Constructor options:
// Uses the current LoggerContext (LogManager.getContext(false)).
new Log4j2LogBackend()
// Explicit LoggerContext — for tests or multi-context setups.
new Log4j2LogBackend(loggerContext)
Enabling write access
The GET (read) endpoints are always exposed. The PUT / POST set-level endpoints are
deny-by-default — they respond 403 Forbidden unless the registered LoggersSettings bean explicitly
enables writes:
@Bean
public LoggersSettings loggersSettings() {
return LoggersSettings.create()
.enableWrite() // opt in to PUT/POST /loggers/{name}
.backend(new LogbackLogBackend()) // omit to keep JUL
.build();
}
The mutating endpoints should be protected with an auth guard — see Logging / Debugging for how to layer guards on management endpoints.
Changes made via the set-level endpoints are process-lifetime-only: they do not rewrite the backend's configuration file and are lost on restart.
Implementing a custom backend
LogBackend is a plain three-method interface:
public interface LogBackend {
Map<String,String> getLevels();
String getLevel(String name);
void setLevel(String name, String level);
}
Implement it directly to drive any backend Juneau does not ship an adapter for (Dropwizard Metrics, system-property bridges, …):
@Bean
public LoggersSettings loggersSettings() {
return LoggersSettings.create()
.enableWrite()
.backend(new MyCustomBackend())
.build();
}
The endpoint never auto-detects a backend from the classpath — it only drives what the registered
LoggersSettings bean declares, consistent with Juneau's explicit-over-magic posture.
Level-name reference
| Backend | Level names |
|---|---|
JUL (JulLogBackend) | FINEST FINER FINE CONFIG INFO WARNING SEVERE OFF ALL |
Logback (LogbackLogBackend) | TRACE DEBUG INFO WARN ERROR OFF |
Log4j2 (Log4j2LogBackend) | TRACE DEBUG INFO WARN ERROR FATAL OFF ALL |
All backends accept "ROOT" as the name for the root logger. A blank or null level clears the logger's own
level so it inherits from its ancestor (JUL and Logback); Log4j2 resets to the parent logger's configured level.
See also
- Logging / Debugging — standard REST server call logging (JUL logger level,
RestDebugFormatter) - Observability — Micrometer + OpenTelemetry — metrics and tracing