vy commented on code in PR #2249:
URL: https://github.com/apache/logging-log4j2/pull/2249#discussion_r1467428953


##########
log4j-api/src/main/java/org/apache/logging/log4j/status/StatusLogger.java:
##########
@@ -16,311 +16,567 @@
  */
 package org.apache.logging.log4j.status;
 
-import java.io.Closeable;
+import static java.util.Objects.requireNonNull;
+
+import edu.umd.cs.findbugs.annotations.Nullable;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.net.URL;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Properties;
 import java.util.Queue;
 import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Marker;
 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.apache.logging.log4j.simple.SimpleLoggerContext;
 import org.apache.logging.log4j.spi.AbstractLogger;
 import org.apache.logging.log4j.util.Constants;
-import org.apache.logging.log4j.util.PropertiesUtil;
 
 /**
- * Records events that occur in the logging system. By default, only error 
messages are logged to {@link System#err}.
- * Normally, the Log4j StatusLogger is configured via the root {@code 
<Configuration status="LEVEL"/>} node in a Log4j
- * configuration file. However, this can be overridden via a system property 
named
- * {@value #DEFAULT_STATUS_LISTENER_LEVEL} and will work with any Log4j 
provider.
- *
- * @see SimpleLogger
- * @see SimpleLoggerContext
+ * Records events that occur in the logging system.
+ * {@link StatusLogger} is expected to be a standalone, self-sufficient 
component that the logging system can rely on for low-level logging purposes.
+ * <h3>Listeners</h3>
+ * <p>
+ * Each recorded event will first get buffered and used to notify the 
registered {@link StatusListener}s.
+ * Listener registry is always initialized with a <em>default listener</em>, 
which is a {@link StatusConsoleListener}.
+ * </p>
+ * <p>
+ * You can programmatically register listeners using {@link 
#registerListener(StatusListener)} method.
+ * </p>
+ * <h3>Configuration</h3>
+ * <p>
+ * The {@code StatusLogger} can be configured in following ways:
+ * </p>
+ * <ol>
+ * <li>Passing system properties to the Java process (e.g., {@code 
-Dlog4j2.StatusLogger.level=INFO})</li>
+ * <li>Providing properties in a {@value StatusLogger#PROPERTIES_FILE_NAME} 
file in the classpath</li>
+ * <li>Using Log4j configuration (i.e., {@code <Configuration status="WARN" 
dest="out">} in a {@code log4j2.xml} in the classpath)</li>
+ * </ol>
+ * <p>
+ * It is crucial to understand that there is a time between the first {@code 
StatusLogger} access and a configuration file (e.g., {@code log4j2.xml}) read.
+ * Consider the following example:
+ * </p>
+ * <ol>
+ * <li>The default level is {@code ERROR}</li>
+ * <li>You have <Configuration status="WARN">} in your {@code log4j2.xml}</li>
+ * <li>Until your {@code log4j2.xml} configuration is read, the effective 
level will be {@code ERROR}</li>
+ * <li>Once your {@code log4j2.xml} configuration is read, the effective level 
will be {@code WARN} as you configured</li>
+ * </ol>
+ * <p>
+ * Hence, unless you use either system properties or {@value 
StatusLogger#PROPERTIES_FILE_NAME} file in the classpath, there is a time 
window that only the defaults will be effective.
+ * </p>
+ * <p>
+ * {@code StatusLogger} is designed as a singleton class accessed statically.
+ * If you are running an application containing multiple Log4j configurations 
(e.g., in a servlet environment with multiple containers) and you happen to 
have differing {@code StatusLogger} configurations (e.g, one {@code log4j2.xml} 
containing {@code <Configuration status="ERROR">} while the other {@code 
<Configuration status="INFO">}), the last loaded configuration will be 
effective one.
+ * </p>
+ * <h3>Debug mode</h3>
+ * <p>
+ * When the {@value Constants#LOG4J2_DEBUG} system property is present, any 
level-related filtering will be skipped and all events will be notified to 
listeners.
+ * </p>
  */
-public final class StatusLogger extends AbstractLogger {
+public class StatusLogger extends AbstractLogger {
+
+    private static final long serialVersionUID = 2L;
+
+    /**
+     * The name of the system property that enables debug mode in its presence.
+     * <p>
+     * This is a local clone of {@link Constants#LOG4J2_DEBUG}.
+     * The cloning is necessary to avoid cyclic initialization.
+     * </p>
+     */
+    public static final String DEBUG_PROPERTY_NAME = "log4j2.debug";
+
+    /**
+     * The name of the system property that can be configured with the maximum 
number of events buffered.
+     * <p>
+     * Once the limit is reached, older entries will be removed as new entries 
are added.
+     * </p>
+     *
+     * @since 2.23.0
+     */
+    public static final String BUFFER_CAPACITY_PROPERTY_NAME = 
"log4j2.status.entries";
+
+    /**
+     * The default value of the {@link #BUFFER_CAPACITY_PROPERTY_NAME} system 
property: {@code 0}.
+     *
+     * @since 2.23.0
+     */
+    public static final int BUFFER_CAPACITY_DEFAULT_VALUE = 0;
 
     /**
-     * System property that can be configured with the number of entries in 
the queue. Once the limit is reached older
-     * entries will be removed as new entries are added.
+     * The name of the system property that can be configured with the maximum 
number of events buffered.
+     * <p>
+     * Once the limit is reached, older entries will be removed as new entries 
are added.
+     * </p>
+     *
+     * @deprecated Use {@link #BUFFER_CAPACITY_PROPERTY_NAME} instead.
+     */
+    @Deprecated
+    public static final String MAX_STATUS_ENTRIES = 
BUFFER_CAPACITY_PROPERTY_NAME;

Review Comment:
   Trying to adhere to `*_PROPERTY_NAME` and `*_DEFAULT_VALUE` convention.



##########
log4j-api/src/main/java/org/apache/logging/log4j/status/StatusConsoleListener.java:
##########
@@ -56,50 +54,59 @@ public StatusConsoleListener(final Level level) {
      * @throws NullPointerException on null {@code level} or {@code stream}
      */
     public StatusConsoleListener(final Level level, final PrintStream stream) {
-        this(level, stream, SimpleLoggerFactory.getInstance());
-    }
-
-    StatusConsoleListener(final Level level, final PrintStream stream, final 
SimpleLoggerFactory loggerFactory) {
-        this.level = Objects.requireNonNull(level, "level");
-        this.stream = Objects.requireNonNull(stream, "stream");
-        this.logger = Objects.requireNonNull(loggerFactory, "loggerFactory")
-                .createSimpleLogger(
-                        "StatusConsoleListener", level, 
ParameterizedNoReferenceMessageFactory.INSTANCE, stream);
+        this.level = requireNonNull(level, "level");
+        this.stream = requireNonNull(stream, "stream");
     }
 
     /**
      * Sets the level to a new value.
-     * @param level The new Level.
+     *
+     * @param level the new level
+     * @throws NullPointerException on null {@code level}
      */
     public void setLevel(final Level level) {
-        this.level = level;
+        this.level = requireNonNull(level, "level");
     }
 
     /**
-     * Return the Log Level for which the Listener should receive events.
-     * @return the Log Level.
+     * Sets the output stream to a new value.
+     *
+     * @param stream the new output stream
+     * @throws NullPointerException on null {@code stream}
+     * @since 2.23.0
+     */
+    public void setStream(final PrintStream stream) {
+        this.stream = requireNonNull(stream, "stream");
+    }
+
+    /**
+     * Returns the level for which the listener should receive events.
+     *
+     * @return the log level
      */
     @Override
     public Level getStatusLevel() {
-        return this.level;
+        return level;
     }
 
     /**
      * Writes status messages to the console.
-     * @param data The StatusData.
+     *
+     * @param data a status data
+     * @throws NullPointerException on null {@code data}
      */
     @Override
     public void log(final StatusData data) {
-        logger
-                // Logging using _only_ the following 4 fields set by 
`StatusLogger#logMessage()`:
-                .atLevel(data.getLevel())
-                .withThrowable(data.getThrowable())
-                .withLocation(data.getStackTraceElement())
-                .log(data.getMessage());
+        requireNonNull(data, "data");
+        if (data.getLevel().isMoreSpecificThan(level)) {
+            final String formattedStatus = data.getFormattedStatus();
+            stream.println(formattedStatus);

Review Comment:
   Call me opinionated, but I (strongly?) prefer `var a = a(); var b = b(a); 
c(b)` over `c(b(a))`, since
   1. Variables give you documentation opportunity (e.g., `boolean 
validUserName = userName.matches(userNamePattern)`)
   2. One-statement-per-line is easier to read and understand
   3. Variables allow you to track intermediate values while stepping through 
code in a debugger. AFAIK, this is not possible in `c(b(a))` form.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscr...@logging.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to