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:55 UTC

[logging-log4j2] branch feature/LOG4J2-3584-StatusConsoleListener-uses-SimpleLogger created (now 5ce3f8d33b)

This is an automated email from the ASF dual-hosted git repository.

vy pushed a change to branch feature/LOG4J2-3584-StatusConsoleListener-uses-SimpleLogger
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


      at 5ce3f8d33b LOG4J2-3584 Make StatusConsoleListener use SimpleLogger internally.

This branch includes the following new commits:

     new 5ce3f8d33b LOG4J2-3584 Make StatusConsoleListener use SimpleLogger internally.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[logging-log4j2] 01/01: LOG4J2-3584 Make StatusConsoleListener use SimpleLogger internally.

Posted by vy...@apache.org.
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>