11 KiB
Loggers Endpoint
Spring Boot Actuator includes the ability to view and configure the log levels of your application at runtime. You can view either the entire list or an individual logger's configuration, which is made up of both the explicitly configured logging level as well as the effective logging level given to it by the logging framework. These levels can be one of:
TRACEDEBUGINFOWARNERRORFATALOFFnull
null indicates that there is no explicit configuration.
Viewing Logger Configuration
View All Loggers
To view the configuration of all loggers:
GET /actuator/loggers
Response example:
{
"levels": ["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"],
"loggers": {
"ROOT": {
"configuredLevel": "INFO",
"effectiveLevel": "INFO"
},
"com.example": {
"configuredLevel": null,
"effectiveLevel": "INFO"
},
"com.example.MyClass": {
"configuredLevel": "DEBUG",
"effectiveLevel": "DEBUG"
}
}
}
View Specific Logger
To view the configuration of a specific logger:
GET /actuator/loggers/com.example.MyClass
Response example:
{
"configuredLevel": "DEBUG",
"effectiveLevel": "DEBUG"
}
Configuring a Logger
To configure a given logger, POST a partial entity to the resource's URI, as the following example shows:
POST /actuator/loggers/com.example.MyClass
Content-Type: application/json
{
"configuredLevel": "DEBUG"
}
Tip
To "reset" the specific level of the logger (and use the default configuration instead), you can pass a value of
nullas theconfiguredLevel.
Reset Logger Level
To reset a logger to its default level:
POST /actuator/loggers/com.example.MyClass
Content-Type: application/json
{
"configuredLevel": null
}
Configuration Examples
Enable Debug Logging for Specific Package
curl -X POST http://localhost:8080/actuator/loggers/com.example.service \
-H "Content-Type: application/json" \
-d '{"configuredLevel": "DEBUG"}'
Enable Trace Logging for Spring Security
curl -X POST http://localhost:8080/actuator/loggers/org.springframework.security \
-H "Content-Type: application/json" \
-d '{"configuredLevel": "TRACE"}'
Set Root Logger Level
curl -X POST http://localhost:8080/actuator/loggers/ROOT \
-H "Content-Type: application/json" \
-d '{"configuredLevel": "WARN"}'
Programmatic Logger Management
You can also manage loggers programmatically in your application:
@RestController
public class LoggerController {
private final LoggingSystem loggingSystem;
public LoggerController(LoggingSystem loggingSystem) {
this.loggingSystem = loggingSystem;
}
@PostMapping("/admin/logger/{name}")
public void setLogLevel(@PathVariable String name, @RequestBody LogLevelRequest request) {
LogLevel level = request.getLevel() != null ?
LogLevel.valueOf(request.getLevel().toUpperCase()) : null;
loggingSystem.setLogLevel(name, level);
}
public static class LogLevelRequest {
private String level;
public String getLevel() { return level; }
public void setLevel(String level) { this.level = level; }
}
}
Conditional Logging
Environment-based Configuration
logging:
level:
com.example: ${LOGGING_LEVEL_EXAMPLE:INFO}
org.springframework.web: ${LOGGING_LEVEL_WEB:WARN}
org.hibernate.SQL: ${LOGGING_LEVEL_SQL:WARN}
org.hibernate.type.descriptor.sql: ${LOGGING_LEVEL_SQL_PARAMS:WARN}
---
spring:
config:
activate:
on-profile: development
logging:
level:
com.example: DEBUG
org.hibernate.SQL: DEBUG
org.hibernate.type.descriptor.sql: TRACE
---
spring:
config:
activate:
on-profile: production
logging:
level:
root: WARN
com.example: INFO
Feature Toggle Logging
@Component
public class FeatureLoggingController {
private final LoggingSystem loggingSystem;
private final Environment environment;
public FeatureLoggingController(LoggingSystem loggingSystem, Environment environment) {
this.loggingSystem = loggingSystem;
this.environment = environment;
}
@EventListener
public void handleFeatureToggleChange(FeatureToggleEvent event) {
if ("debug-logging".equals(event.getFeatureName())) {
if (event.isEnabled()) {
enableDebugLogging();
} else {
disableDebugLogging();
}
}
}
private void enableDebugLogging() {
loggingSystem.setLogLevel("com.example.service", LogLevel.DEBUG);
loggingSystem.setLogLevel("com.example.repository", LogLevel.DEBUG);
}
private void disableDebugLogging() {
loggingSystem.setLogLevel("com.example.service", null);
loggingSystem.setLogLevel("com.example.repository", null);
}
}
Security Considerations
Securing the Loggers Endpoint
@Configuration
public class LoggersSecurityConfig {
@Bean
@Order(1)
public SecurityFilterChain loggersSecurityFilterChain(HttpSecurity http) throws Exception {
return http
.requestMatcher(EndpointRequest.to("loggers"))
.authorizeHttpRequests(requests ->
requests.anyRequest().hasRole("ADMIN"))
.httpBasic(withDefaults())
.build();
}
}
Read-only Access
To provide read-only access to the loggers endpoint:
management:
endpoint:
loggers:
access: read-only
Or configure programmatically:
@Configuration
public class LoggersAccessConfig {
@Bean
@Order(1)
public SecurityFilterChain loggersSecurityFilterChain(HttpSecurity http) throws Exception {
return http
.requestMatcher(EndpointRequest.to("loggers"))
.authorizeHttpRequests(requests ->
requests
.requestMatchers(HttpMethod.GET).hasRole("LOGGER_READER")
.requestMatchers(HttpMethod.POST).hasRole("LOGGER_ADMIN")
.anyRequest().denyAll())
.httpBasic(withDefaults())
.build();
}
}
OpenTelemetry Integration
By default, logging via OpenTelemetry is not configured. You have to provide the location of the OpenTelemetry logs endpoint to configure it:
management:
otlp:
logging:
endpoint: "https://otlp.example.com:4318/v1/logs"
Note
The OpenTelemetry Logback appender and Log4j appender are not part of Spring Boot. For more details, see the OpenTelemetry Logback appender or the OpenTelemetry Log4j2 appender in the OpenTelemetry Java instrumentation GitHub repository.
Tip
You have to configure the appender in your
logback-spring.xmlorlog4j2-spring.xmlconfiguration to get OpenTelemetry logging working.
The OpenTelemetryAppender for both Logback and Log4j requires access to an OpenTelemetry instance to function properly. This instance must be set programmatically during application startup:
@Component
public class OpenTelemetryAppenderInitializer {
public OpenTelemetryAppenderInitializer(OpenTelemetry openTelemetry) {
// Configure Logback appender
if (LoggerFactory.getILoggerFactory() instanceof LoggerContext) {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
context.getStatusManager().add(new OnConsoleStatusListener());
OpenTelemetryAppender appender = new OpenTelemetryAppender();
appender.setContext(context);
appender.setOpenTelemetry(openTelemetry);
appender.start();
ch.qos.logback.classic.Logger rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME);
rootLogger.addAppender(appender);
}
}
}
Best Practices
- Monitor Performance: Changing log levels at runtime can impact application performance
- Security: Always secure the loggers endpoint in production environments
- Audit Changes: Log when log levels are changed and by whom
- Temporary Changes: Consider making runtime log level changes temporary
- Documentation: Document the purpose of different log levels in your application
- Testing: Test your application with different log levels to ensure it performs well
- Correlation IDs: Use correlation IDs to track requests across log entries
Audit Log Level Changes
@Component
public class LoggerAuditListener {
private static final Logger logger = LoggerFactory.getLogger(LoggerAuditListener.class);
@EventListener
public void handleLoggerConfigurationChange(LoggerConfigurationChangeEvent event) {
String username = getCurrentUsername();
logger.info("Logger level changed: logger={}, oldLevel={}, newLevel={}, user={}",
event.getLoggerName(),
event.getOldLevel(),
event.getNewLevel(),
username);
}
private String getCurrentUsername() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return auth != null ? auth.getName() : "system";
}
}
Temporary Log Level Changes
@Component
public class TemporaryLogLevelManager {
private final LoggingSystem loggingSystem;
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private final Map<String, LogLevel> originalLevels = new ConcurrentHashMap<>();
public TemporaryLogLevelManager(LoggingSystem loggingSystem) {
this.loggingSystem = loggingSystem;
}
public void setTemporaryLogLevel(String loggerName, LogLevel level, Duration duration) {
// Store original level
LoggerConfiguration config = loggingSystem.getLoggerConfiguration(loggerName);
originalLevels.put(loggerName, config.getConfiguredLevel());
// Set new level
loggingSystem.setLogLevel(loggerName, level);
// Schedule reset
scheduler.schedule(() -> resetLogLevel(loggerName), duration.toMillis(), TimeUnit.MILLISECONDS);
}
private void resetLogLevel(String loggerName) {
LogLevel originalLevel = originalLevels.remove(loggerName);
loggingSystem.setLogLevel(loggerName, originalLevel);
}
}