Skip to content

Commit 4e014da

Browse files
committed
Return all loggers for Log4j2
This commit aligns log4j2's behavior with logback such that loggers with a null configuredLevel are also returned by the actuator endpoint. Fixes gh-20037
1 parent 92ce94b commit 4e014da

File tree

2 files changed

+101
-9
lines changed

2 files changed

+101
-9
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@
2020
import java.io.InputStream;
2121
import java.net.URL;
2222
import java.util.ArrayList;
23+
import java.util.Collection;
2324
import java.util.Collections;
25+
import java.util.LinkedHashMap;
2426
import java.util.List;
27+
import java.util.Map;
2528
import java.util.Set;
2629

2730
import org.apache.logging.log4j.Level;
@@ -31,11 +34,11 @@
3134
import org.apache.logging.log4j.core.LogEvent;
3235
import org.apache.logging.log4j.core.Logger;
3336
import org.apache.logging.log4j.core.LoggerContext;
34-
import org.apache.logging.log4j.core.config.Configuration;
3537
import org.apache.logging.log4j.core.config.ConfigurationFactory;
3638
import org.apache.logging.log4j.core.config.ConfigurationSource;
3739
import org.apache.logging.log4j.core.config.LoggerConfig;
3840
import org.apache.logging.log4j.core.filter.AbstractFilter;
41+
import org.apache.logging.log4j.core.util.NameUtil;
3942
import org.apache.logging.log4j.message.Message;
4043

4144
import org.springframework.boot.logging.LogFile;
@@ -221,30 +224,65 @@ public void setLogLevel(String loggerName, LogLevel logLevel) {
221224

222225
@Override
223226
public List<LoggerConfiguration> getLoggerConfigurations() {
227+
Map<String, LoggerConfig> allLoggers = getAllLoggers();
224228
List<LoggerConfiguration> result = new ArrayList<>();
225-
Configuration configuration = getLoggerContext().getConfiguration();
226-
for (LoggerConfig loggerConfig : configuration.getLoggers().values()) {
227-
result.add(convertLoggerConfiguration(loggerConfig));
228-
}
229+
allLoggers.forEach((key, value) -> result.add(convertLoggerConfiguration(value, key)));
229230
result.sort(CONFIGURATION_COMPARATOR);
230231
return result;
231232
}
232233

234+
private Map<String, LoggerConfig> getAllLoggers() {
235+
Collection<Logger> loggers = getLoggerContext().getLoggers();
236+
Map<String, LoggerConfig> configuredLoggers = getLoggerContext().getConfiguration().getLoggers();
237+
Map<String, LoggerConfig> result = new LinkedHashMap<>();
238+
for (Logger logger : loggers) {
239+
String name = logger.getName();
240+
while (name != null) {
241+
result.putIfAbsent(name, getLoggerContext().getConfiguration().getLoggerConfig(name));
242+
name = getSubName(name);
243+
}
244+
}
245+
configuredLoggers.keySet().forEach((name) -> {
246+
String currentName = name;
247+
while (currentName != null) {
248+
result.putIfAbsent(currentName, getLoggerContext().getConfiguration().getLoggerConfig(currentName));
249+
currentName = getSubName(currentName);
250+
}
251+
});
252+
return result;
253+
}
254+
255+
private String getSubName(String name) {
256+
if (StringUtils.isEmpty(name)) {
257+
return null;
258+
}
259+
int nested = name.lastIndexOf('$');
260+
if (nested != -1) {
261+
return name.substring(0, nested);
262+
}
263+
return NameUtil.getSubName(name);
264+
}
265+
233266
@Override
234267
public LoggerConfiguration getLoggerConfiguration(String loggerName) {
235-
return convertLoggerConfiguration(getLoggerConfig(loggerName));
268+
LoggerConfig loggerConfig = getAllLoggers().get(loggerName);
269+
if (loggerConfig == null) {
270+
return null;
271+
}
272+
return convertLoggerConfiguration(loggerConfig, loggerName);
236273
}
237274

238-
private LoggerConfiguration convertLoggerConfiguration(LoggerConfig loggerConfig) {
275+
private LoggerConfiguration convertLoggerConfiguration(LoggerConfig loggerConfig, String name) {
239276
if (loggerConfig == null) {
240277
return null;
241278
}
242279
LogLevel level = LEVELS.convertNativeToSystem(loggerConfig.getLevel());
243-
String name = loggerConfig.getName();
244280
if (!StringUtils.hasLength(name) || LogManager.ROOT_LOGGER_NAME.equals(name)) {
245281
name = ROOT_LOGGER_NAME;
246282
}
247-
return new LoggerConfiguration(name, level, level);
283+
boolean isLoggerConfigured = loggerConfig.getName().equals(name);
284+
LogLevel configuredLevel = (isLoggerConfigured) ? level : null;
285+
return new LoggerConfiguration(name, configuredLevel, level);
248286
}
249287

250288
@Override

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@
2222
import java.util.ArrayList;
2323
import java.util.Collections;
2424
import java.util.EnumSet;
25+
import java.util.LinkedHashMap;
2526
import java.util.List;
27+
import java.util.Map;
2628

2729
import com.fasterxml.jackson.databind.ObjectMapper;
30+
import org.apache.commons.logging.Log;
31+
import org.apache.commons.logging.LogFactory;
2832
import org.apache.logging.log4j.LogManager;
2933
import org.apache.logging.log4j.Logger;
3034
import org.apache.logging.log4j.core.LoggerContext;
@@ -169,6 +173,28 @@ void getLoggingConfigurations() {
169173
assertThat(configurations.get(0).getName()).isEqualTo(LoggingSystem.ROOT_LOGGER_NAME);
170174
}
171175

176+
@Test
177+
void getLoggingConfigurationsShouldReturnAllLoggers() {
178+
LogManager.getLogger("org.springframework.boot.logging.log4j2.Log4J2LoggingSystemTests$Nested");
179+
this.loggingSystem.beforeInitialize();
180+
this.loggingSystem.initialize(null, null, null);
181+
this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.DEBUG);
182+
List<LoggerConfiguration> configurations = this.loggingSystem.getLoggerConfigurations();
183+
assertThat(configurations).isNotEmpty();
184+
assertThat(configurations.get(0).getName()).isEqualTo(LoggingSystem.ROOT_LOGGER_NAME);
185+
Map<String, LogLevel> loggers = new LinkedHashMap<>();
186+
configurations.forEach((logger) -> loggers.put(logger.getName(), logger.getConfiguredLevel()));
187+
assertIsPresent("org", loggers, null);
188+
assertIsPresent("org.springframework.boot.logging.log4j2", loggers, null);
189+
assertIsPresent("org.springframework.boot.logging.log4j2.Log4J2LoggingSystemTests", loggers, LogLevel.DEBUG);
190+
assertIsPresent("org.springframework.boot.logging.log4j2.Log4J2LoggingSystemTests$Nested", loggers, null);
191+
}
192+
193+
private void assertIsPresent(String loggerName, Map<String, LogLevel> loggers, LogLevel logLevel) {
194+
assertThat(loggers.containsKey(loggerName)).isTrue();
195+
assertThat(loggers.get(loggerName)).isEqualTo(logLevel);
196+
}
197+
172198
@Test
173199
void getLoggingConfiguration() {
174200
this.loggingSystem.beforeInitialize();
@@ -179,6 +205,24 @@ void getLoggingConfiguration() {
179205
.isEqualTo(new LoggerConfiguration(getClass().getName(), LogLevel.DEBUG, LogLevel.DEBUG));
180206
}
181207

208+
@Test
209+
void getLoggingConfigurationShouldReturnLoggerWithNullConfiguredLevel() {
210+
this.loggingSystem.beforeInitialize();
211+
this.loggingSystem.initialize(null, null, null);
212+
this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.DEBUG);
213+
LoggerConfiguration configuration = this.loggingSystem.getLoggerConfiguration("org");
214+
assertThat(configuration).isEqualTo(new LoggerConfiguration("org", null, LogLevel.INFO));
215+
}
216+
217+
@Test
218+
void getLoggingConfigurationForNonExistentLoggerShouldReturnNull() {
219+
this.loggingSystem.beforeInitialize();
220+
this.loggingSystem.initialize(null, null, null);
221+
this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.DEBUG);
222+
LoggerConfiguration configuration = this.loggingSystem.getLoggerConfiguration("doesnotexist");
223+
assertThat(configuration).isEqualTo(null);
224+
}
225+
182226
@Test
183227
void setLevelOfUnconfiguredLoggerDoesNotAffectRootConfiguration(CapturedOutput output) {
184228
this.loggingSystem.beforeInitialize();
@@ -321,4 +365,14 @@ private void availableClasses(String... classNames) {
321365

322366
}
323367

368+
/**
369+
* Used for testing that loggers in nested classes are returned by
370+
* {@link Log4J2LoggingSystem#getLoggerConfigurations()} .
371+
*/
372+
static class Nested {
373+
374+
private static final Log logger = LogFactory.getLog(Nested.class);
375+
376+
}
377+
324378
}

0 commit comments

Comments
 (0)