You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by vy...@apache.org on 2022/10/10 20:33:56 UTC
[logging-log4j2] 01/01: LOG4J2-3584 Make StatusConsoleListener use SimpleLogger internally.
This is an automated email from the ASF dual-hosted git repository.
vy pushed a commit to branch feature/LOG4J2-3584-StatusConsoleListener-uses-SimpleLogger
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit 5ce3f8d33bca1d4c96db64e5124e9d3682261608
Author: Volkan Yazıcı <vo...@yazi.ci>
AuthorDate: Mon Oct 10 22:33:39 2022 +0200
LOG4J2-3584 Make StatusConsoleListener use SimpleLogger internally.
---
.../log4j/status/StatusConsoleListenerTest.java | 170 +++++++++++++++++++++
.../log4j/status/StatusConsoleListener.java | 32 +++-
.../apache/logging/log4j/status/StatusLogger.java | 13 +-
src/changes/changes.xml | 3 +
4 files changed, 209 insertions(+), 9 deletions(-)
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/status/StatusConsoleListenerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/status/StatusConsoleListenerTest.java
new file mode 100644
index 0000000000..68dcf343e7
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/status/StatusConsoleListenerTest.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.status;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.MessageFactory;
+import org.apache.logging.log4j.message.ParameterizedNoReferenceMessageFactory;
+import org.apache.logging.log4j.simple.SimpleLogger;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+import org.mockito.Mockito;
+import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension;
+import uk.org.webcompere.systemstubs.properties.SystemProperties;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+public class StatusConsoleListenerTest {
+
+ public static final MessageFactory MESSAGE_FACTORY = ParameterizedNoReferenceMessageFactory.INSTANCE;
+
+ @Nested
+ @ExtendWith(SystemStubsExtension.class)
+ @ResourceLock(value = Resources.SYSTEM_PROPERTIES)
+ class SimpleLogger_should_be_used {
+
+ @Test
+ void test(final SystemProperties properties) throws Exception {
+
+ // Customize the date-time formatting to be passed on to the `SimpleLogger`.
+ properties.set(StatusLogger.STATUS_DATE_FORMAT, "'LOG4J2-3584 'ss.SSS");
+
+ // Create the listener.
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ final String encoding = "UTF-8";
+ final PrintStream printStream = new PrintStream(outputStream, false, encoding);
+ final StatusConsoleListener listener = new StatusConsoleListener(Level.WARN, printStream);
+
+ // Verify the internal `SimpleLogger`.
+ Assertions
+ .assertThat(listener)
+ .extracting("logger")
+ .isInstanceOf(SimpleLogger.class);
+
+ // Log a message.
+ final Message message = MESSAGE_FACTORY.newMessage("foo");
+ listener.log(new StatusData(
+ null, // since ignored by `SimpleLogger`
+ Level.ERROR,
+ message,
+ null,
+ null)); // as set by `StatusLogger` itself
+
+ // Verify the output.
+ printStream.flush();
+ final String output = outputStream.toString(encoding);
+ Assertions
+ .assertThat(output)
+ .matches("(?s)LOG4J2-3584 \\d{2}\\.\\d{3} ERROR StatusConsoleListener foo\\r?\\n$");
+
+ }
+
+ }
+
+ @Test
+ void level_and_stream_should_be_honored() throws Exception {
+
+ // Create the listener.
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ final String encoding = "UTF-8";
+ final PrintStream printStream = new PrintStream(outputStream, false, encoding);
+ final StatusConsoleListener listener = new StatusConsoleListener(Level.WARN, printStream);
+
+ // First, log a message that is expected to be logged.
+ final RuntimeException expectedThrowable = new RuntimeException("expectedThrowable");
+ expectedThrowable.setStackTrace(new StackTraceElement[]{
+ new StackTraceElement("expectedThrowableClass", "expectedThrowableMethod", "expectedThrowableFile", 1)
+ });
+ final Message expectedMessage = MESSAGE_FACTORY.newMessage("expectedMessage");
+ listener.log(new StatusData(
+ null, // since ignored by `SimpleLogger`
+ Level.WARN,
+ expectedMessage,
+ expectedThrowable,
+ null)); // as set by `StatusLogger` itself
+
+ // Second, log a message that is expected to be discarded due to its insufficient level.
+ final RuntimeException discardedThrowable = new RuntimeException("discardedThrowable");
+ discardedThrowable.setStackTrace(new StackTraceElement[]{
+ new StackTraceElement("discardedThrowableClass", "discardedThrowableMethod", "discardedThrowableFile", 2)
+ });
+ final Message discardedMessage = MESSAGE_FACTORY.newMessage("discardedMessage");
+ listener.log(new StatusData(
+ null, // since ignored by `SimpleLogger`
+ Level.INFO,
+ discardedMessage,
+ discardedThrowable,
+ null)); // as set by `StatusLogger` itself
+
+ // Collect the output.
+ printStream.flush();
+ final String output = outputStream.toString(encoding);
+
+ // Verify the output.
+ Assertions
+ .assertThat(output)
+ .contains(expectedThrowable.getMessage())
+ .contains(expectedMessage.getFormattedMessage())
+ .doesNotContain(discardedThrowable.getMessage())
+ .doesNotContain(discardedMessage.getFormattedMessage());
+
+ }
+
+ @Test
+ void filters_should_be_honored() throws Exception {
+
+ // Create the listener.
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ final String encoding = "UTF-8";
+ final PrintStream printStream = new PrintStream(outputStream, false, encoding);
+ final StatusConsoleListener listener = new StatusConsoleListener(Level.TRACE, printStream);
+
+ // Set the filter.
+ final StackTraceElement caller = new StackTraceElement("callerClass", "callerMethod", "callerFile", 1);
+ listener.setFilters(caller.getClassName());
+
+ // Log the message to be filtered.
+ final Message message = MESSAGE_FACTORY.newMessage("foo");
+ listener.log(new StatusData(
+ caller,
+ Level.TRACE,
+ message,
+ null,
+ null)); // as set by `StatusLogger` itself
+
+ // Verify the filtering.
+ printStream.flush();
+ final String output = outputStream.toString(encoding);
+ Assertions.assertThat(output).isEmpty();
+
+ }
+
+ @Test
+ void non_system_streams_should_be_closed() throws Exception {
+ final PrintStream stream = Mockito.mock(PrintStream.class);
+ final StatusConsoleListener listener = new StatusConsoleListener(Level.WARN, stream);
+ listener.close();
+ Mockito.verify(stream).close();
+ }
+
+}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusConsoleListener.java b/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusConsoleListener.java
index 1097483e92..0fd611757b 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusConsoleListener.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusConsoleListener.java
@@ -16,21 +16,28 @@
*/
package org.apache.logging.log4j.status;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.message.ParameterizedNoReferenceMessageFactory;
+import org.apache.logging.log4j.simple.SimpleLogger;
+
import java.io.IOException;
import java.io.PrintStream;
-import org.apache.logging.log4j.Level;
-
/**
* StatusListener that writes to the Console.
*/
@SuppressWarnings("UseOfSystemOutOrSystemErr")
public class StatusConsoleListener implements StatusListener {
- private Level level = Level.FATAL;
+ private Level level;
+
private String[] filters;
+
private final PrintStream stream;
+ private final Logger logger;
+
/**
* Creates the StatusConsoleListener using the supplied Level.
* @param level The Level of status messages that should appear on the console.
@@ -52,6 +59,17 @@ public class StatusConsoleListener implements StatusListener {
}
this.level = level;
this.stream = stream;
+ this.logger = new SimpleLogger(
+ "StatusConsoleListener",
+ level,
+ false,
+ true,
+ StatusLogger.DATE_FORMAT_PROVIDED,
+ false,
+ StatusLogger.DATE_FORMAT,
+ ParameterizedNoReferenceMessageFactory.INSTANCE,
+ StatusLogger.PROPS,
+ stream);
}
/**
@@ -78,7 +96,12 @@ public class StatusConsoleListener implements StatusListener {
@Override
public void log(final StatusData data) {
if (!filtered(data)) {
- stream.println(data.getFormattedStatus());
+ logger
+ // Logging using _only_ the following 4 fields set by `StatusLogger#logMessage()`:
+ .atLevel(data.getLevel())
+ .withThrowable(data.getThrowable())
+ .withLocation(data.getStackTraceElement())
+ .log(data.getMessage());
}
}
@@ -110,4 +133,5 @@ public class StatusConsoleListener implements StatusListener {
this.stream.close();
}
}
+
}
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 32accf9b21..32ad1d1184 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
@@ -75,14 +75,19 @@ public final class StatusLogger extends AbstractLogger {
private static final String NOT_AVAIL = "?";
- private static final PropertiesUtil PROPS = new PropertiesUtil("log4j2.StatusLogger.properties");
+ static final PropertiesUtil PROPS = new PropertiesUtil("log4j2.StatusLogger.properties");
private static final int MAX_ENTRIES = PROPS.getIntegerProperty(MAX_STATUS_ENTRIES, 200);
private static final String DEFAULT_STATUS_LEVEL = PROPS.getStringProperty(DEFAULT_STATUS_LISTENER_LEVEL);
+ static final String DATE_FORMAT = PROPS.getStringProperty(STATUS_DATE_FORMAT);
+
+ static final boolean DATE_FORMAT_PROVIDED = Strings.isNotBlank(DATE_FORMAT);
+
// LOG4J2-1176: normal parameterized message remembers param object, causing memory leaks.
- private static final StatusLogger STATUS_LOGGER = new StatusLogger(StatusLogger.class.getName(),
+ private static final StatusLogger STATUS_LOGGER = new StatusLogger(
+ StatusLogger.class.getName(),
ParameterizedNoReferenceMessageFactory.INSTANCE);
private final SimpleLogger logger;
@@ -131,10 +136,8 @@ public final class StatusLogger extends AbstractLogger {
*/
private StatusLogger(final String name, final MessageFactory messageFactory) {
super(name, messageFactory);
- final String dateFormat = PROPS.getStringProperty(STATUS_DATE_FORMAT, Strings.EMPTY);
- final boolean showDateTime = !Strings.isEmpty(dateFormat);
final Level loggerLevel = isDebugPropertyEnabled() ? Level.TRACE : Level.ERROR;
- this.logger = new SimpleLogger("StatusLogger", loggerLevel, false, true, showDateTime, false, dateFormat, messageFactory, PROPS, System.err);
+ this.logger = new SimpleLogger("StatusLogger", loggerLevel, false, true, DATE_FORMAT_PROVIDED, false, DATE_FORMAT, messageFactory, PROPS, System.err);
this.listenersLevel = Level.toLevel(DEFAULT_STATUS_LEVEL, Level.WARN).intLevel();
}
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 49c2090af1..c13af687d7 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -30,6 +30,9 @@
- "remove" - Removed
-->
<release version="2.19.0" date="2022-09-09" description="GA Release 2.19.0">
+ <action issue="LOG4J2-3584" dev="vy" type="fix">
+ Make StatusConsoleListener use SimpleLogger internally.
+ </action>
<action issue="LOG4J2-3614" dev="vy" type="fix" due-to="strainu">
Harden InstantFormatter against delegate failures.
</action>