Skip to content

Commit

Permalink
Improve Log4J log level reset support
Browse files Browse the repository at this point in the history
Update `Log4J2LoggingSystem` so that call to `setLevel` with a `null`
level with remove the logger if it was previously configured by a
`LoggingSystem` call.

To track which loggers have been configured by us, and which have been
configure directly by the user, a custom `LoggerConfig` subclass is
used. We'll only remove `LevelSetLoggerConfig` classes, for any others
we'll call `setLevel(null)` on the config.

Prior to this commit, it was impossible to set then reset a logger
level using the actuator endpoint. This is because Log4J doesn't provide
a way to get the actual configured level. If the `setLevel(null)` has
been applied, then `getLevel()` will return the value of the parent
logger or a default value of `ERROR`.

Fixes gh-24298
  • Loading branch information
philwebb committed Apr 15, 2021
1 parent 9682386 commit 238d0fe
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -214,16 +214,37 @@ public Set<LogLevel> getSupportedLogLevels() {

@Override
public void setLogLevel(String loggerName, LogLevel logLevel) {
Level level = LEVELS.convertSystemToNative(logLevel);
setLogLevel(loggerName, LEVELS.convertSystemToNative(logLevel));
}

private void setLogLevel(String loggerName, Level level) {
LoggerConfig logger = getLogger(loggerName);
if (level == null) {
clearLogLevel(loggerName, logger);
}
else {
setLogLevel(loggerName, logger, level);
}
getLoggerContext().updateLoggers();
}

private void clearLogLevel(String loggerName, LoggerConfig logger) {
if (logger instanceof LevelSetLoggerConfig) {
getLoggerContext().getConfiguration().removeLogger(loggerName);
}
else {
logger.setLevel(null);
}
}

private void setLogLevel(String loggerName, LoggerConfig logger, Level level) {
if (logger == null) {
logger = new LoggerConfig(loggerName, level, true);
getLoggerContext().getConfiguration().addLogger(loggerName, logger);
getLoggerContext().getConfiguration().addLogger(loggerName,
new LevelSetLoggerConfig(loggerName, level, true));
}
else {
logger.setLevel(level);
}
getLoggerContext().updateLoggers();
}

@Override
Expand Down Expand Up @@ -348,4 +369,15 @@ public LoggingSystem getLoggingSystem(ClassLoader classLoader) {

}

/**
* {@link LoggerConfig} used when the user has set a specific {@link Level}.
*/
private static class LevelSetLoggerConfig extends LoggerConfig {

LevelSetLoggerConfig(String name, Level level, boolean additive) {
super(name, level, additive);
}

}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -25,7 +25,6 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.logging.Log;
Expand All @@ -34,6 +33,7 @@
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.Reconfigurable;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -241,7 +241,7 @@ void loggingThatUsesJulIsCaptured(CapturedOutput output) {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(null, null, null);
java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger(getClass().getName());
julLogger.setLevel(Level.INFO);
julLogger.setLevel(java.util.logging.Level.INFO);
julLogger.severe("Hello world");
assertThat(output).contains("Hello world");
}
Expand Down Expand Up @@ -338,6 +338,38 @@ void initializationIsOnlyPerformedOnceUntilCleanedUp() {
verify(listener, times(4)).propertyChange(any(PropertyChangeEvent.class));
}

@Test
void getLoggingConfigurationWithResetLevelReturnsNull() {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(null, null, null);
this.loggingSystem.setLogLevel("com.example", LogLevel.WARN);
this.loggingSystem.setLogLevel("com.example.test", LogLevel.DEBUG);
LoggerConfiguration configuration = this.loggingSystem.getLoggerConfiguration("com.example.test");
assertThat(configuration)
.isEqualTo(new LoggerConfiguration("com.example.test", LogLevel.DEBUG, LogLevel.DEBUG));
this.loggingSystem.setLogLevel("com.example.test", null);
LoggerConfiguration updatedConfiguration = this.loggingSystem.getLoggerConfiguration("com.example.test");
assertThat(updatedConfiguration).isNull();
}

@Test
void getLoggingConfigurationWithResetLevelWhenAlreadyConfiguredReturnsParentConfiguredLevel() {
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(null, null, null);
loggerContext.getConfiguration().addLogger("com.example.test",
new LoggerConfig("com.example.test", org.apache.logging.log4j.Level.INFO, false));
this.loggingSystem.setLogLevel("com.example", LogLevel.WARN);
this.loggingSystem.setLogLevel("com.example.test", LogLevel.DEBUG);
LoggerConfiguration configuration = this.loggingSystem.getLoggerConfiguration("com.example.test");
assertThat(configuration)
.isEqualTo(new LoggerConfiguration("com.example.test", LogLevel.DEBUG, LogLevel.DEBUG));
this.loggingSystem.setLogLevel("com.example.test", null);
LoggerConfiguration updatedConfiguration = this.loggingSystem.getLoggerConfiguration("com.example.test");
assertThat(updatedConfiguration)
.isEqualTo(new LoggerConfiguration("com.example.test", LogLevel.WARN, LogLevel.WARN));
}

private String getRelativeClasspathLocation(String fileName) {
String defaultPath = ClassUtils.getPackageName(getClass());
defaultPath = defaultPath.replace('.', '/');
Expand Down

0 comments on commit 238d0fe

Please sign in to comment.