I know this post is ancient but ... it was one of the first that came up in my search so I thought I would update it for anyone looking for something similar in 2023. So it is absolutely possible to do this on a request basis, you probably only want to do this for testing purposes but with that being said let's get started. To start with I also found this reference (https://www.springcloud.io/post/2022-03/dynamically-modifying-logger-log-levels/#gsc.tab=0) which gave a pretty ok idea how to programmatically set the log level but not a request basis. If you want to be able to do it on a request basis you should use a spring interceptor. Below is the interceptor implementation I wrote up. Do note that if you change the log level on the inbound request it will stay at that level until you reset it so in the interceptor I also have a postHandle that will reset the log level to the original value.
Note: I do have feature switches and some constants in this example you will have to sub or setup in your project.
package com.me.SpringBoot2022Example.v1.interceptor;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.me.SpringBoot2022Example.v1.util.ExampleConstants.*;
import static com.me.SpringBoot2022Example.v1.util.ExampleFeatureSwitches.ALLOW_POSTMAN_SET_LOG_LEVEL;
@Slf4j
public class LogLevelInterceptor implements HandlerInterceptor {
/**
* Executed before actual handler is executed
**/
@Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {
// If the feature switch is set to true then we can allow overriding certain request attributes in support of
// testing. (Note: This would normally be used by postman. See the postman files in this repository.)
if (ALLOW_POSTMAN_SET_LOG_LEVEL) {
this.setLogLevelByRequest(request);
}
return true;
}
/**
* Executed before after handler is executed
**/
@Override
public void postHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final ModelAndView modelAndView) throws Exception {
// If the feature switch is set to true then we can allow overriding certain request attributes in support of
// testing. (Note: This would normally be used by postman. See the postman files in this repository.)
if (ALLOW_POSTMAN_SET_LOG_LEVEL) {
log.info("setLogLevelByRequest: Resetting log level to original value ...");
final LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
// Ensure that the logger context is returned
if (loggerContext != null) {
// See if the caller attempted to specify a specific logger to change the log level on
String loggerName = request.getHeader(HEADER_REQUEST_LOGGER_NAME);
// If the logger was not specified the logger context will be defaulted to the ROOT logger
if (loggerName == null || loggerName.isEmpty()) {
log.warn("setLogLevelByRequest: Logger to update log level not provided, defaulting to ROOT logger ...");
loggerName = org.slf4j.Logger.ROOT_LOGGER_NAME;
}
// Check if the provided logger exists
final Logger logger = loggerContext.exists(loggerName);
// If the logger exists ensure that it was returned
if (logger != null) {
final String previousLogLevel = request.getAttribute("previousLogLevel").toString();
// Attempt to create the new log level with the provided values
final Level newLevel = Level.toLevel(previousLogLevel, null);
// Verify the updated log level has been created and apply to the logger
if (newLevel != null) {
log.info("setLogLevelByRequest: Reset log level to original value [{}] for this request ...", newLevel.levelStr);
logger.setLevel(newLevel);
} else {
log.error("setLogLevelByRequest: Failed to create new log level - custom log level can not be reset ...");
}
} else {
log.error("setLogLevelByRequest: Failed to get logger [{}] - custom log level can not be reset ...", loggerName);
}
} else {
log.error("setLogLevelByRequest: Failed to get loggerContext - custom log level can not be reset ...");
}
}
}
private boolean setLogLevelByRequest(final HttpServletRequest request) {
log.info("setLogLevelByRequest: Setting custom log level for the inbound request ...");
// If feature is enabled attempt to get the specified log level
final String logLevel = request.getHeader(HEADER_REQUEST_LOG_LEVEL_OVERRIDE);
// If no log level value was specified then the log level will not be reset
if (logLevel != null && !logLevel.isEmpty()) {
final LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
// Ensure that the logger context is returned
if (loggerContext != null) {
// See if the caller attempted to specify a specific logger to change the log level on
String loggerName = request.getHeader(HEADER_REQUEST_LOGGER_NAME);
// If the logger was not specified the logger context will be defaulted to the ROOT logger
if (loggerName == null || loggerName.isEmpty()) {
log.warn("setLogLevelByRequest: Logger to update log level not provided, defaulting to ROOT logger ...");
loggerName = org.slf4j.Logger.ROOT_LOGGER_NAME;
}
// Check if the provided logger exists
final Logger logger = loggerContext.exists(loggerName);
// If the logger exists ensure that it was returned
if (logger != null) {
// Attempt to create the new log level with the provided values
final Level newLevel = Level.toLevel(logLevel, null);
// Verify the updated log level has been created and apply to the logger
if (newLevel != null) {
request.setAttribute("previousLogLevel", this.currentLogLevel(loggerContext, loggerName));
log.info("setLogLevelByRequest: Custom log level will be set to [{}] for this request ...", newLevel.levelStr);
logger.setLevel(newLevel);
} else {
log.error("setLogLevelByRequest: Failed to create new log level - custom log level will not be applied to this call ...");
}
} else {
log.error("setLogLevelByRequest: Failed to get logger [{}] - custom log level will not be applied to this call ...", loggerName);
}
} else {
log.error("setLogLevelByRequest: Failed to get loggerContext - custom log level will not be applied to this call ...");
}
} else {
log.warn("setLogLevelByRequest: Request for custom log level present but no log level provided - custom log level will not be applied to this call ...");
}
return true;
}
private String currentLogLevel(final LoggerContext loggerContext, final String loggerName) {
// Get all loggers of the system
return loggerContext.getLoggerList().stream()
// There are too many loggers, and they are filtered here for demonstration purposes.
// Only the logger for the passed in name is returned.
.filter(logger -> logger.getName().equals(loggerName))
// Mapping to map
.map(logger -> logger.getEffectiveLevel().levelStr)
.collect(Collectors.toList())
.get(0);
}
}```