diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/status/StatusLoggerLevelTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/status/StatusLoggerLevelTest.java index 3d58cf454a7..9aa6ad9db75 100644 --- a/log4j-api-test/src/test/java/org/apache/logging/log4j/status/StatusLoggerLevelTest.java +++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/status/StatusLoggerLevelTest.java @@ -18,12 +18,19 @@ import static org.apache.logging.log4j.status.StatusLogger.DEFAULT_FALLBACK_LISTENER_LEVEL; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.util.Arrays; import java.util.Properties; import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.message.ParameterizedNoReferenceMessageFactory; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import uk.org.webcompere.systemstubs.SystemStubs; class StatusLoggerLevelTest { @@ -104,4 +111,79 @@ void invalid_level_should_cause_fallback_to_defaults() throws Exception { // Verify the level assertThat(statusLoggerConfig.fallbackListenerLevel).isEqualTo(DEFAULT_FALLBACK_LISTENER_LEVEL); } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void debug_mode_should_override_log_filtering(final boolean debugEnabled) { + + // Create a logger with debug enabled + final StatusLogger.Config loggerConfig = new StatusLogger.Config(debugEnabled, 0, null); + final Level loggerLevel = Level.ERROR; + final StatusConsoleListener fallbackListener = mock(StatusConsoleListener.class); + when(fallbackListener.getStatusLevel()).thenReturn(loggerLevel); + final StatusLogger logger = new StatusLogger( + StatusLoggerLevelTest.class.getSimpleName(), + ParameterizedNoReferenceMessageFactory.INSTANCE, + loggerConfig, + fallbackListener); + + // Log at all levels + final Level[] levels = Level.values(); + for (final Level level : levels) { + logger.log(level, "test for level `{}`", level); + } + + // Calculate the number of expected messages + final int expectedMessageCount; + if (debugEnabled) { + expectedMessageCount = levels.length; + } else { + expectedMessageCount = (int) Arrays.stream(levels) + .filter(loggerLevel::isLessSpecificThan) + .count(); + } + + // Verify the fallback listener invocation + assertThat(expectedMessageCount).isGreaterThan(0); + verify(fallbackListener, times(expectedMessageCount)).log(any()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void debug_mode_should_override_listener_filtering(final boolean debugEnabled) { + + // Create a logger with debug enabled + final StatusLogger.Config loggerConfig = new StatusLogger.Config(debugEnabled, 0, null); + final StatusLogger logger = new StatusLogger( + StatusLoggerLevelTest.class.getSimpleName(), + ParameterizedNoReferenceMessageFactory.INSTANCE, + loggerConfig, + new StatusConsoleListener(Level.ERROR)); + + // Register a listener + final Level listenerLevel = Level.INFO; + final StatusListener listener = mock(StatusListener.class); + when(listener.getStatusLevel()).thenReturn(listenerLevel); + logger.registerListener(listener); + + // Log at all levels + final Level[] levels = Level.values(); + for (final Level level : levels) { + logger.log(level, "test for level `{}`", level); + } + + // Calculate the number of expected messages + final int expectedMessageCount; + if (debugEnabled) { + expectedMessageCount = levels.length; + } else { + expectedMessageCount = (int) Arrays.stream(levels) + .filter(listenerLevel::isLessSpecificThan) + .count(); + } + + // Verify the listener invocation + assertThat(expectedMessageCount).isGreaterThan(0); + verify(listener, times(expectedMessageCount)).log(any()); + } } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusLogger.java b/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusLogger.java index bdebc1150a0..56d9ef98625 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusLogger.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusLogger.java @@ -768,7 +768,8 @@ private void notifyListeners(final StatusData statusData) { } private void notifyListener(final StatusListener listener, final StatusData statusData) { - if (config.debugEnabled || listener.getStatusLevel().isLessSpecificThan(statusData.getLevel())) { + final boolean levelEnabled = isLevelEnabled(listener.getStatusLevel(), statusData.getLevel()); + if (levelEnabled) { listener.log(statusData); } } @@ -963,8 +964,20 @@ public boolean isEnabled(final Level level, final Marker marker, final Message m } @Override - public boolean isEnabled(final Level level, final Marker marker) { - requireNonNull(level, "level"); - return getLevel().isLessSpecificThan(level); + public boolean isEnabled(final Level messageLevel, final Marker marker) { + requireNonNull(messageLevel, "messageLevel"); + final Level loggerLevel = getLevel(); + return isLevelEnabled(loggerLevel, messageLevel); + } + + /** + * Checks if the message level is allowed for the filtering level (e.g., of logger, of listener) by taking debug mode into account. + * + * @param filteringLevel the level (e.g., of logger, of listener) to filter messages + * @param messageLevel the level of the message + * @return {@code true}, if the sink level is less specific than the message level; {@code false}, otherwise + */ + private boolean isLevelEnabled(final Level filteringLevel, final Level messageLevel) { + return config.debugEnabled || filteringLevel.isLessSpecificThan(messageLevel); } } diff --git a/src/changelog/.2.x.x/fix_StatusLogger_debug_mode.xml b/src/changelog/.2.x.x/fix_StatusLogger_debug_mode.xml new file mode 100644 index 00000000000..0eafe16cbcc --- /dev/null +++ b/src/changelog/.2.x.x/fix_StatusLogger_debug_mode.xml @@ -0,0 +1,8 @@ + + + + Fix `StatusLogger` log level filtering when debug mode is enabled +