aboutsummaryrefslogtreecommitdiff
path: root/slf4j-api/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'slf4j-api/src/main/java')
-rw-r--r--slf4j-api/src/main/java/org/slf4j/ILoggerFactory.java2
-rw-r--r--slf4j-api/src/main/java/org/slf4j/Logger.java236
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/LoggerFactory.java467
-rwxr-xr-x[-rw-r--r--]slf4j-api/src/main/java/org/slf4j/LoggerFactoryFriend.java (renamed from slf4j-api/src/main/java/org/slf4j/impl/StaticMDCBinder.java)42
-rw-r--r--slf4j-api/src/main/java/org/slf4j/MDC.java94
-rw-r--r--slf4j-api/src/main/java/org/slf4j/Marker.java25
-rw-r--r--slf4j-api/src/main/java/org/slf4j/MarkerFactory.java27
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/event/DefaultLoggingEvent.java140
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/event/EventConstants.java13
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/event/EventRecordingLogger.java84
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/event/KeyValuePair.java32
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/event/Level.java56
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/event/LoggingEvent.java45
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/event/SubstituteLoggingEvent.java115
-rw-r--r--slf4j-api/src/main/java/org/slf4j/helpers/AbstractLogger.java423
-rw-r--r--slf4j-api/src/main/java/org/slf4j/helpers/BasicMDCAdapter.java95
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/helpers/BasicMarker.java61
-rw-r--r--slf4j-api/src/main/java/org/slf4j/helpers/BasicMarkerFactory.java2
-rw-r--r--slf4j-api/src/main/java/org/slf4j/helpers/CheckReturnValue.java30
-rw-r--r--slf4j-api/src/main/java/org/slf4j/helpers/FormattingTuple.java22
-rw-r--r--slf4j-api/src/main/java/org/slf4j/helpers/LegacyAbstractLogger.java39
-rw-r--r--slf4j-api/src/main/java/org/slf4j/helpers/MarkerIgnoringBase.java1
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/helpers/MessageFormatter.java134
-rw-r--r--slf4j-api/src/main/java/org/slf4j/helpers/NOPLogger.java225
-rw-r--r--slf4j-api/src/main/java/org/slf4j/helpers/NOPLoggerFactory.java3
-rw-r--r--slf4j-api/src/main/java/org/slf4j/helpers/NOPMDCAdapter.java18
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/helpers/NOP_FallbackServiceProvider.java47
-rw-r--r--slf4j-api/src/main/java/org/slf4j/helpers/NamedLoggerBase.java5
-rw-r--r--slf4j-api/src/main/java/org/slf4j/helpers/NormalizedParameters.java116
-rw-r--r--slf4j-api/src/main/java/org/slf4j/helpers/Reporter.java181
-rw-r--r--slf4j-api/src/main/java/org/slf4j/helpers/SubstituteLogger.java291
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/helpers/SubstituteLoggerFactory.java37
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/helpers/SubstituteServiceProvider.java41
-rw-r--r--slf4j-api/src/main/java/org/slf4j/helpers/ThreadLocalMapOfStacks.java89
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/helpers/Util.java64
-rw-r--r--slf4j-api/src/main/java/org/slf4j/impl/StaticLoggerBinder.java73
-rw-r--r--slf4j-api/src/main/java/org/slf4j/impl/StaticMarkerBinder.java70
-rw-r--r--slf4j-api/src/main/java/org/slf4j/impl/package.html17
-rw-r--r--slf4j-api/src/main/java/org/slf4j/package.html2
-rw-r--r--slf4j-api/src/main/java/org/slf4j/spi/CallerBoundaryAware.java25
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/spi/DefaultLoggingEventBuilder.java246
-rw-r--r--slf4j-api/src/main/java/org/slf4j/spi/LocationAwareLogger.java7
-rw-r--r--slf4j-api/src/main/java/org/slf4j/spi/LoggerFactoryBinder.java5
-rw-r--r--slf4j-api/src/main/java/org/slf4j/spi/LoggingEventAware.java23
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/spi/LoggingEventBuilder.java169
-rw-r--r--slf4j-api/src/main/java/org/slf4j/spi/MDCAdapter.java46
-rw-r--r--slf4j-api/src/main/java/org/slf4j/spi/MarkerFactoryBinder.java5
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/spi/NOPLoggingEventBuilder.java100
-rwxr-xr-xslf4j-api/src/main/java/org/slf4j/spi/SLF4JServiceProvider.java60
49 files changed, 3466 insertions, 684 deletions
diff --git a/slf4j-api/src/main/java/org/slf4j/ILoggerFactory.java b/slf4j-api/src/main/java/org/slf4j/ILoggerFactory.java
index 8ed82f3c..6fec242c 100644
--- a/slf4j-api/src/main/java/org/slf4j/ILoggerFactory.java
+++ b/slf4j-api/src/main/java/org/slf4j/ILoggerFactory.java
@@ -29,7 +29,7 @@ package org.slf4j;
* instances by name.
*
* <p>Most users retrieve {@link Logger} instances through the static
- * {@link LoggerFactory#getLogger(String)} method. An instance of of this
+ * {@link LoggerFactory#getLogger(String)} method. An instance of this
* interface is bound internally with {@link LoggerFactory} class at
* compile time.
*
diff --git a/slf4j-api/src/main/java/org/slf4j/Logger.java b/slf4j-api/src/main/java/org/slf4j/Logger.java
index 93dd9945..2803f782 100644
--- a/slf4j-api/src/main/java/org/slf4j/Logger.java
+++ b/slf4j-api/src/main/java/org/slf4j/Logger.java
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2004-2011 QOS.ch
+ * Copyright (c) 2004-2022 QOS.ch
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
@@ -25,12 +25,29 @@
package org.slf4j;
+import static org.slf4j.event.EventConstants.DEBUG_INT;
+import static org.slf4j.event.EventConstants.ERROR_INT;
+import static org.slf4j.event.EventConstants.INFO_INT;
+import static org.slf4j.event.EventConstants.TRACE_INT;
+import static org.slf4j.event.EventConstants.WARN_INT;
+import static org.slf4j.event.Level.DEBUG;
+import static org.slf4j.event.Level.ERROR;
+import static org.slf4j.event.Level.INFO;
+import static org.slf4j.event.Level.TRACE;
+import static org.slf4j.event.Level.WARN;
+
+import org.slf4j.event.Level;
+import org.slf4j.helpers.CheckReturnValue;
+import org.slf4j.spi.DefaultLoggingEventBuilder;
+import org.slf4j.spi.LoggingEventBuilder;
+import org.slf4j.spi.NOPLoggingEventBuilder;
+
/**
* The org.slf4j.Logger interface is the main user entry point of SLF4J API.
* It is expected that logging takes place through concrete implementations
* of this interface.
- * <p/>
- * <h3>Typical usage pattern:</h3>
+ *
+ * <H3>Typical usage pattern:</H3>
* <pre>
* import org.slf4j.Logger;
* import org.slf4j.LoggerFactory;
@@ -45,26 +62,29 @@ package org.slf4j;
* oldT = t;
* t = temperature;
* <span style="color:green">logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);</span>
- * if(temperature.intValue() > 50) {
+ * if (temperature.intValue() &gt; 50) {
* <span style="color:green">logger.info("Temperature has risen above 50 degrees.");</span>
* }
* }
* }
* </pre>
*
- * Be sure to read the FAQ entry relating to <a href="../../../faq.html#logging_performance">parameterized
+ * <p>Note that version 2.0 of the SLF4J API introduces a <a href="../../../manual.html#fluent">fluent api</a>,
+ * the most significant API change to occur in the last 20 years.
+ *
+ * <p>Be sure to read the FAQ entry relating to <a href="../../../faq.html#logging_performance">parameterized
* logging</a>. Note that logging statements can be parameterized in
* <a href="../../../faq.html#paramException">presence of an exception/throwable</a>.
*
* <p>Once you are comfortable using loggers, i.e. instances of this interface, consider using
- * <a href="MDC.html">MDC</a> as well as <a href="Marker.html">Markers</a>.</p>
+ * <a href="MDC.html">MDC</a> as well as <a href="Marker.html">Markers</a>.
*
* @author Ceki G&uuml;lc&uuml;
*/
public interface Logger {
/**
- * Case insensitive String constant used to retrieve the name of the root logger.
+ * Case-insensitive String constant used to retrieve the name of the root logger.
*
* @since 1.3
*/
@@ -77,6 +97,70 @@ public interface Logger {
public String getName();
/**
+ * <p>Make a new {@link LoggingEventBuilder} instance as appropriate for this logger implementation.
+ * This default implementation always returns a new instance of {@link DefaultLoggingEventBuilder}.</p>
+ * <p></p>
+ * <p>This method is intended to be used by logging systems implementing the SLF4J API and <b>not</b>
+ * by end users.</p>
+ * <p></p>
+ * <p>Also note that a {@link LoggingEventBuilder} instance should be built for all levels,
+ * independently of the level argument. In other words, this method is an <b>unconditional</b>
+ * constructor for the {@link LoggingEventBuilder} appropriate for this logger implementation.</p>
+ * <p></p>
+ * @param level desired level for the event builder
+ * @return a new {@link LoggingEventBuilder} instance as appropriate for <b>this</b> logger
+ * @since 2.0
+ */
+ default public LoggingEventBuilder makeLoggingEventBuilder(Level level) {
+ return new DefaultLoggingEventBuilder(this, level);
+ }
+
+ /**
+ * Make a new {@link LoggingEventBuilder} instance as appropriate for this logger and the
+ * desired {@link Level} passed as parameter. If this Logger is disabled for the given Level, then
+ * a {@link NOPLoggingEventBuilder} is returned.
+ *
+ *
+ * @param level desired level for the event builder
+ * @return a new {@link LoggingEventBuilder} instance as appropriate for this logger
+ * @since 2.0
+ */
+ @CheckReturnValue
+ default public LoggingEventBuilder atLevel(Level level) {
+ if (isEnabledForLevel(level)) {
+ return makeLoggingEventBuilder(level);
+ } else {
+ return NOPLoggingEventBuilder.singleton();
+ }
+ }
+
+
+
+ /**
+ * Returns whether this Logger is enabled for a given {@link Level}.
+ *
+ * @param level
+ * @return true if enabled, false otherwise.
+ */
+ default public boolean isEnabledForLevel(Level level) {
+ int levelInt = level.toInt();
+ switch (levelInt) {
+ case (TRACE_INT):
+ return isTraceEnabled();
+ case (DEBUG_INT):
+ return isDebugEnabled();
+ case (INFO_INT):
+ return isInfoEnabled();
+ case (WARN_INT):
+ return isWarnEnabled();
+ case (ERROR_INT):
+ return isErrorEnabled();
+ default:
+ throw new IllegalArgumentException("Level [" + level + "] not recognized.");
+ }
+ }
+
+ /**
* Is the logger instance enabled for the TRACE level?
*
* @return True if this Logger is enabled for the TRACE level,
@@ -96,9 +180,9 @@ public interface Logger {
/**
* Log a message at the TRACE level according to the specified format
* and argument.
- * <p/>
+ *
* <p>This form avoids superfluous object creation when the logger
- * is disabled for the TRACE level. </p>
+ * is disabled for the TRACE level.
*
* @param format the format string
* @param arg the argument
@@ -109,9 +193,9 @@ public interface Logger {
/**
* Log a message at the TRACE level according to the specified format
* and arguments.
- * <p/>
+ *
* <p>This form avoids superfluous object creation when the logger
- * is disabled for the TRACE level. </p>
+ * is disabled for the TRACE level.
*
* @param format the format string
* @param arg1 the first argument
@@ -123,12 +207,12 @@ public interface Logger {
/**
* Log a message at the TRACE level according to the specified format
* and arguments.
- * <p/>
+ *
* <p>This form avoids superfluous string concatenation when the logger
* is disabled for the TRACE level. However, this variant incurs the hidden
* (and relatively small) cost of creating an <code>Object[]</code> before invoking the method,
* even if this logger is disabled for TRACE. The variants taking {@link #trace(String, Object) one} and
- * {@link #trace(String, Object, Object) two} arguments exist solely in order to avoid this hidden cost.</p>
+ * {@link #trace(String, Object, Object) two} arguments exist solely in order to avoid this hidden cost.
*
* @param format the format string
* @param arguments a list of 3 or more arguments
@@ -159,6 +243,21 @@ public interface Logger {
public boolean isTraceEnabled(Marker marker);
/**
+ * Entry point for fluent-logging for {@link org.slf4j.event.Level#TRACE} level.
+ *
+ * @return LoggingEventBuilder instance as appropriate for level TRACE
+ * @since 2.0
+ */
+ @CheckReturnValue
+ default public LoggingEventBuilder atTrace() {
+ if (isTraceEnabled()) {
+ return makeLoggingEventBuilder(TRACE);
+ } else {
+ return NOPLoggingEventBuilder.singleton();
+ }
+ }
+
+ /**
* Log a message with the specific Marker at the TRACE level.
*
* @param marker the marker data specific to this log statement
@@ -232,9 +331,9 @@ public interface Logger {
/**
* Log a message at the DEBUG level according to the specified format
* and argument.
- * <p/>
+ *
* <p>This form avoids superfluous object creation when the logger
- * is disabled for the DEBUG level. </p>
+ * is disabled for the DEBUG level.
*
* @param format the format string
* @param arg the argument
@@ -244,9 +343,9 @@ public interface Logger {
/**
* Log a message at the DEBUG level according to the specified format
* and arguments.
- * <p/>
+ *
* <p>This form avoids superfluous object creation when the logger
- * is disabled for the DEBUG level. </p>
+ * is disabled for the DEBUG level.
*
* @param format the format string
* @param arg1 the first argument
@@ -257,13 +356,13 @@ public interface Logger {
/**
* Log a message at the DEBUG level according to the specified format
* and arguments.
- * <p/>
+ *
* <p>This form avoids superfluous string concatenation when the logger
* is disabled for the DEBUG level. However, this variant incurs the hidden
* (and relatively small) cost of creating an <code>Object[]</code> before invoking the method,
* even if this logger is disabled for DEBUG. The variants taking
* {@link #debug(String, Object) one} and {@link #debug(String, Object, Object) two}
- * arguments exist solely in order to avoid this hidden cost.</p>
+ * arguments exist solely in order to avoid this hidden cost.
*
* @param format the format string
* @param arguments a list of 3 or more arguments
@@ -341,6 +440,21 @@ public interface Logger {
public void debug(Marker marker, String msg, Throwable t);
/**
+ * Entry point for fluent-logging for {@link org.slf4j.event.Level#DEBUG} level.
+ *
+ * @return LoggingEventBuilder instance as appropriate for level DEBUG
+ * @since 2.0
+ */
+ @CheckReturnValue
+ default public LoggingEventBuilder atDebug() {
+ if (isDebugEnabled()) {
+ return makeLoggingEventBuilder(DEBUG);
+ } else {
+ return NOPLoggingEventBuilder.singleton();
+ }
+ }
+
+ /**
* Is the logger instance enabled for the INFO level?
*
* @return True if this Logger is enabled for the INFO level,
@@ -358,9 +472,9 @@ public interface Logger {
/**
* Log a message at the INFO level according to the specified format
* and argument.
- * <p/>
+ *
* <p>This form avoids superfluous object creation when the logger
- * is disabled for the INFO level. </p>
+ * is disabled for the INFO level.
*
* @param format the format string
* @param arg the argument
@@ -370,9 +484,9 @@ public interface Logger {
/**
* Log a message at the INFO level according to the specified format
* and arguments.
- * <p/>
+ *
* <p>This form avoids superfluous object creation when the logger
- * is disabled for the INFO level. </p>
+ * is disabled for the INFO level.
*
* @param format the format string
* @param arg1 the first argument
@@ -383,13 +497,13 @@ public interface Logger {
/**
* Log a message at the INFO level according to the specified format
* and arguments.
- * <p/>
+ *
* <p>This form avoids superfluous string concatenation when the logger
* is disabled for the INFO level. However, this variant incurs the hidden
* (and relatively small) cost of creating an <code>Object[]</code> before invoking the method,
* even if this logger is disabled for INFO. The variants taking
* {@link #info(String, Object) one} and {@link #info(String, Object, Object) two}
- * arguments exist solely in order to avoid this hidden cost.</p>
+ * arguments exist solely in order to avoid this hidden cost.
*
* @param format the format string
* @param arguments a list of 3 or more arguments
@@ -410,7 +524,8 @@ public interface Logger {
* data is also taken into consideration.
*
* @param marker The marker data to take into consideration
- * @return true if this logger is warn enabled, false otherwise
+ * @return true if this Logger is enabled for the INFO level,
+ * false otherwise.
*/
public boolean isInfoEnabled(Marker marker);
@@ -466,6 +581,21 @@ public interface Logger {
public void info(Marker marker, String msg, Throwable t);
/**
+ * Entry point for fluent-logging for {@link org.slf4j.event.Level#INFO} level.
+ *
+ * @return LoggingEventBuilder instance as appropriate for level INFO
+ * @since 2.0
+ */
+ @CheckReturnValue
+ default public LoggingEventBuilder atInfo() {
+ if (isInfoEnabled()) {
+ return makeLoggingEventBuilder(INFO);
+ } else {
+ return NOPLoggingEventBuilder.singleton();
+ }
+ }
+
+ /**
* Is the logger instance enabled for the WARN level?
*
* @return True if this Logger is enabled for the WARN level,
@@ -483,9 +613,9 @@ public interface Logger {
/**
* Log a message at the WARN level according to the specified format
* and argument.
- * <p/>
+ *
* <p>This form avoids superfluous object creation when the logger
- * is disabled for the WARN level. </p>
+ * is disabled for the WARN level.
*
* @param format the format string
* @param arg the argument
@@ -495,13 +625,13 @@ public interface Logger {
/**
* Log a message at the WARN level according to the specified format
* and arguments.
- * <p/>
+ *
* <p>This form avoids superfluous string concatenation when the logger
* is disabled for the WARN level. However, this variant incurs the hidden
* (and relatively small) cost of creating an <code>Object[]</code> before invoking the method,
* even if this logger is disabled for WARN. The variants taking
* {@link #warn(String, Object) one} and {@link #warn(String, Object, Object) two}
- * arguments exist solely in order to avoid this hidden cost.</p>
+ * arguments exist solely in order to avoid this hidden cost.
*
* @param format the format string
* @param arguments a list of 3 or more arguments
@@ -511,9 +641,9 @@ public interface Logger {
/**
* Log a message at the WARN level according to the specified format
* and arguments.
- * <p/>
+ *
* <p>This form avoids superfluous object creation when the logger
- * is disabled for the WARN level. </p>
+ * is disabled for the WARN level.
*
* @param format the format string
* @param arg1 the first argument
@@ -592,6 +722,21 @@ public interface Logger {
public void warn(Marker marker, String msg, Throwable t);
/**
+ * Entry point for fluent-logging for {@link org.slf4j.event.Level#WARN} level.
+ *
+ * @return LoggingEventBuilder instance as appropriate for level WARN
+ * @since 2.0
+ */
+ @CheckReturnValue
+ default public LoggingEventBuilder atWarn() {
+ if (isWarnEnabled()) {
+ return makeLoggingEventBuilder(WARN);
+ } else {
+ return NOPLoggingEventBuilder.singleton();
+ }
+ }
+
+ /**
* Is the logger instance enabled for the ERROR level?
*
* @return True if this Logger is enabled for the ERROR level,
@@ -609,9 +754,9 @@ public interface Logger {
/**
* Log a message at the ERROR level according to the specified format
* and argument.
- * <p/>
+ *
* <p>This form avoids superfluous object creation when the logger
- * is disabled for the ERROR level. </p>
+ * is disabled for the ERROR level.
*
* @param format the format string
* @param arg the argument
@@ -621,9 +766,9 @@ public interface Logger {
/**
* Log a message at the ERROR level according to the specified format
* and arguments.
- * <p/>
+ *
* <p>This form avoids superfluous object creation when the logger
- * is disabled for the ERROR level. </p>
+ * is disabled for the ERROR level.
*
* @param format the format string
* @param arg1 the first argument
@@ -634,13 +779,13 @@ public interface Logger {
/**
* Log a message at the ERROR level according to the specified format
* and arguments.
- * <p/>
+ *
* <p>This form avoids superfluous string concatenation when the logger
* is disabled for the ERROR level. However, this variant incurs the hidden
* (and relatively small) cost of creating an <code>Object[]</code> before invoking the method,
* even if this logger is disabled for ERROR. The variants taking
* {@link #error(String, Object) one} and {@link #error(String, Object, Object) two}
- * arguments exist solely in order to avoid this hidden cost.</p>
+ * arguments exist solely in order to avoid this hidden cost.
*
* @param format the format string
* @param arguments a list of 3 or more arguments
@@ -718,4 +863,19 @@ public interface Logger {
*/
public void error(Marker marker, String msg, Throwable t);
+ /**
+ * Entry point for fluent-logging for {@link org.slf4j.event.Level#ERROR} level.
+ *
+ * @return LoggingEventBuilder instance as appropriate for level ERROR
+ * @since 2.0
+ */
+ @CheckReturnValue
+ default public LoggingEventBuilder atError() {
+ if (isErrorEnabled()) {
+ return makeLoggingEventBuilder(ERROR);
+ } else {
+ return NOPLoggingEventBuilder.singleton();
+ }
+ }
+
}
diff --git a/slf4j-api/src/main/java/org/slf4j/LoggerFactory.java b/slf4j-api/src/main/java/org/slf4j/LoggerFactory.java
index 2f74c18b..4e1f0b0d 100755
--- a/slf4j-api/src/main/java/org/slf4j/LoggerFactory.java
+++ b/slf4j-api/src/main/java/org/slf4j/LoggerFactory.java
@@ -25,34 +25,41 @@
package org.slf4j;
import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
import java.util.Set;
+import java.util.concurrent.LinkedBlockingQueue;
-import org.slf4j.helpers.NOPLoggerFactory;
+import org.slf4j.event.SubstituteLoggingEvent;
+import org.slf4j.helpers.NOP_FallbackServiceProvider;
+import org.slf4j.helpers.Reporter;
import org.slf4j.helpers.SubstituteLogger;
-import org.slf4j.helpers.SubstituteLoggerFactory;
+import org.slf4j.helpers.SubstituteServiceProvider;
import org.slf4j.helpers.Util;
-import org.slf4j.impl.StaticLoggerBinder;
+import org.slf4j.spi.SLF4JServiceProvider;
/**
* The <code>LoggerFactory</code> is a utility class producing Loggers for
- * various logging APIs, most notably for log4j, logback and JDK 1.4 logging.
- * Other implementations such as {@link org.slf4j.impl.NOPLogger NOPLogger} and
- * {@link org.slf4j.impl.SimpleLogger SimpleLogger} are also supported.
- * <p/>
- * <p/>
- * <code>LoggerFactory</code> is essentially a wrapper around an
- * {@link ILoggerFactory} instance bound with <code>LoggerFactory</code> at
- * compile time.
- * <p/>
- * <p/>
- * Please note that all methods in <code>LoggerFactory</code> are static.
+ * various logging APIs, e.g. logback, reload4j, log4j and JDK 1.4 logging.
+ * Other implementations such as {@link org.slf4j.helpers.NOPLogger NOPLogger} and
+ * SimpleLogger are also supported.
+ *
+ * <p><code>LoggerFactory</code> is essentially a wrapper around an
+ * {@link ILoggerFactory} instance provided by a {@link SLF4JServiceProvider}.
*
+ * <p>
+ * Please note that all methods in <code>LoggerFactory</code> are static.
*
* @author Alexander Dorokhine
* @author Robert Elliot
@@ -61,17 +68,27 @@ import org.slf4j.impl.StaticLoggerBinder;
*/
public final class LoggerFactory {
- static final String CODES_PREFIX = "http://www.slf4j.org/codes.html";
+ static final String CODES_PREFIX = "https://www.slf4j.org/codes.html";
+
+ static final String NO_PROVIDERS_URL = CODES_PREFIX + "#noProviders";
+ static final String IGNORED_BINDINGS_URL = CODES_PREFIX + "#ignoredBindings";
- static final String NO_STATICLOGGERBINDER_URL = CODES_PREFIX + "#StaticLoggerBinder";
static final String MULTIPLE_BINDINGS_URL = CODES_PREFIX + "#multiple_bindings";
- static final String NULL_LF_URL = CODES_PREFIX + "#null_LF";
static final String VERSION_MISMATCH = CODES_PREFIX + "#version_mismatch";
static final String SUBSTITUTE_LOGGER_URL = CODES_PREFIX + "#substituteLogger";
static final String LOGGER_NAME_MISMATCH_URL = CODES_PREFIX + "#loggerNameMismatch";
+ static final String REPLAY_URL = CODES_PREFIX + "#replay";
static final String UNSUCCESSFUL_INIT_URL = CODES_PREFIX + "#unsuccessfulInit";
- static final String UNSUCCESSFUL_INIT_MSG = "org.slf4j.LoggerFactory could not be successfully initialized. See also " + UNSUCCESSFUL_INIT_URL;
+ static final String UNSUCCESSFUL_INIT_MSG = "org.slf4j.LoggerFactory in failed state. Original exception was thrown EARLIER. See also "
+ + UNSUCCESSFUL_INIT_URL;
+ /**
+ * System property for explicitly setting the provider class. If set and the provider could be instantiated,
+ * then the service loading mechanism will be bypassed.
+ *
+ * @since 2.0.9
+ */
+ static final public String PROVIDER_PROPERTY_KEY = "slf4j.provider";
static final int UNINITIALIZED = 0;
static final int ONGOING_INITIALIZATION = 1;
@@ -79,22 +96,69 @@ public final class LoggerFactory {
static final int SUCCESSFUL_INITIALIZATION = 3;
static final int NOP_FALLBACK_INITIALIZATION = 4;
- static int INITIALIZATION_STATE = UNINITIALIZED;
- static SubstituteLoggerFactory TEMP_FACTORY = new SubstituteLoggerFactory();
- static NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory();
+ static volatile int INITIALIZATION_STATE = UNINITIALIZED;
+ static final SubstituteServiceProvider SUBST_PROVIDER = new SubstituteServiceProvider();
+ static final NOP_FallbackServiceProvider NOP_FALLBACK_SERVICE_PROVIDER = new NOP_FallbackServiceProvider();
// Support for detecting mismatched logger names.
static final String DETECT_LOGGER_NAME_MISMATCH_PROPERTY = "slf4j.detectLoggerNameMismatch";
- static boolean DETECT_LOGGER_NAME_MISMATCH = Boolean.getBoolean(DETECT_LOGGER_NAME_MISMATCH_PROPERTY);
+ static final String JAVA_VENDOR_PROPERTY = "java.vendor.url";
+
+ static boolean DETECT_LOGGER_NAME_MISMATCH = Util.safeGetBooleanSystemProperty(DETECT_LOGGER_NAME_MISMATCH_PROPERTY);
+
+ static volatile SLF4JServiceProvider PROVIDER;
+
+ // Package access for tests
+ static List<SLF4JServiceProvider> findServiceProviders() {
+ List<SLF4JServiceProvider> providerList = new ArrayList<>();
+
+ // retain behaviour similar to that of 1.7 series and earlier. More specifically, use the class loader that
+ // loaded the present class to search for services
+ final ClassLoader classLoaderOfLoggerFactory = LoggerFactory.class.getClassLoader();
+
+ SLF4JServiceProvider explicitProvider = loadExplicitlySpecified(classLoaderOfLoggerFactory);
+ if(explicitProvider != null) {
+ providerList.add(explicitProvider);
+ return providerList;
+ }
+
+
+ ServiceLoader<SLF4JServiceProvider> serviceLoader = getServiceLoader(classLoaderOfLoggerFactory);
+
+ Iterator<SLF4JServiceProvider> iterator = serviceLoader.iterator();
+ while (iterator.hasNext()) {
+ safelyInstantiate(providerList, iterator);
+ }
+ return providerList;
+ }
+
+ private static ServiceLoader<SLF4JServiceProvider> getServiceLoader(final ClassLoader classLoaderOfLoggerFactory) {
+ ServiceLoader<SLF4JServiceProvider> serviceLoader;
+ SecurityManager securityManager = System.getSecurityManager();
+ if(securityManager == null) {
+ serviceLoader = ServiceLoader.load(SLF4JServiceProvider.class, classLoaderOfLoggerFactory);
+ } else {
+ final PrivilegedAction<ServiceLoader<SLF4JServiceProvider>> action = () -> ServiceLoader.load(SLF4JServiceProvider.class, classLoaderOfLoggerFactory);
+ serviceLoader = AccessController.doPrivileged(action);
+ }
+ return serviceLoader;
+ }
+
+ private static void safelyInstantiate(List<SLF4JServiceProvider> providerList, Iterator<SLF4JServiceProvider> iterator) {
+ try {
+ SLF4JServiceProvider provider = iterator.next();
+ providerList.add(provider);
+ } catch (ServiceConfigurationError e) {
+ Reporter.error("A service provider failed to instantiate:\n" + e.getMessage());
+ }
+ }
/**
* It is LoggerFactory's responsibility to track version changes and manage
* the compatibility list.
- * <p/>
- * <p/>
- * It is assumed that all versions in the 1.6 are mutually compatible.
+ * <p>
*/
- static private final String[] API_COMPATIBILITY_LIST = new String[] { "1.6", "1.7" };
+ static private final String[] API_COMPATIBILITY_LIST = new String[] { "2.0" };
// private constructor prevents instantiation
private LoggerFactory() {
@@ -102,18 +166,17 @@ public final class LoggerFactory {
/**
* Force LoggerFactory to consider itself uninitialized.
- * <p/>
- * <p/>
+ * <p>
+ * <p>
* This method is intended to be called by classes (in the same package) for
* testing purposes. This method is internal. It can be modified, renamed or
* removed at any time without notice.
- * <p/>
- * <p/>
+ * <p>
+ * <p>
* You are strongly discouraged from calling this method in production code.
*/
static void reset() {
INITIALIZATION_STATE = UNINITIALIZED;
- TEMP_FACTORY = new SubstituteLoggerFactory();
}
private final static void performInitialization() {
@@ -123,110 +186,77 @@ public final class LoggerFactory {
}
}
- private static boolean messageContainsOrgSlf4jImplStaticLoggerBinder(String msg) {
- if (msg == null)
- return false;
- if (msg.indexOf("org/slf4j/impl/StaticLoggerBinder") != -1)
- return true;
- if (msg.indexOf("org.slf4j.impl.StaticLoggerBinder") != -1)
- return true;
- return false;
- }
-
private final static void bind() {
try {
- Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
- reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
- // the next line does the binding
- StaticLoggerBinder.getSingleton();
- INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
- reportActualBinding(staticLoggerBinderPathSet);
- fixSubstitutedLoggers();
- } catch (NoClassDefFoundError ncde) {
- String msg = ncde.getMessage();
- if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
- INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
- Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
- Util.report("Defaulting to no-operation (NOP) logger implementation");
- Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");
+ List<SLF4JServiceProvider> providersList = findServiceProviders();
+ reportMultipleBindingAmbiguity(providersList);
+ if (providersList != null && !providersList.isEmpty()) {
+ PROVIDER = providersList.get(0);
+ // SLF4JServiceProvider.initialize() is intended to be called here and nowhere else.
+ PROVIDER.initialize();
+ INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
+ reportActualBinding(providersList);
} else {
- failedBinding(ncde);
- throw ncde;
- }
- } catch (java.lang.NoSuchMethodError nsme) {
- String msg = nsme.getMessage();
- if (msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) {
- INITIALIZATION_STATE = FAILED_INITIALIZATION;
- Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
- Util.report("Your binding is version 1.5.5 or earlier.");
- Util.report("Upgrade your binding to version 1.6.x.");
+ INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
+ Reporter.warn("No SLF4J providers were found.");
+ Reporter.warn("Defaulting to no-operation (NOP) logger implementation");
+ Reporter.warn("See " + NO_PROVIDERS_URL + " for further details.");
+
+ Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
+ reportIgnoredStaticLoggerBinders(staticLoggerBinderPathSet);
}
- throw nsme;
+ postBindCleanUp();
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
}
}
- static void failedBinding(Throwable t) {
- INITIALIZATION_STATE = FAILED_INITIALIZATION;
- Util.report("Failed to instantiate SLF4J LoggerFactory", t);
+ static SLF4JServiceProvider loadExplicitlySpecified(ClassLoader classLoader) {
+ String explicitlySpecified = System.getProperty(PROVIDER_PROPERTY_KEY);
+ if (null == explicitlySpecified || explicitlySpecified.isEmpty()) {
+ return null;
+ }
+ try {
+ String message = String.format("Attempting to load provider \"%s\" specified via \"%s\" system property", explicitlySpecified, PROVIDER_PROPERTY_KEY);
+ Reporter.info(message);
+ Class<?> clazz = classLoader.loadClass(explicitlySpecified);
+ Constructor<?> constructor = clazz.getConstructor();
+ Object provider = constructor.newInstance();
+ return (SLF4JServiceProvider) provider;
+ } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
+ String message = String.format("Failed to instantiate the specified SLF4JServiceProvider (%s)", explicitlySpecified);
+ Reporter.error(message, e);
+ return null;
+ } catch (ClassCastException e) {
+ String message = String.format("Specified SLF4JServiceProvider (%s) does not implement SLF4JServiceProvider interface", explicitlySpecified);
+ Reporter.error(message, e);
+ return null;
+ }
}
- private final static void fixSubstitutedLoggers() {
- List<SubstituteLogger> loggers = TEMP_FACTORY.getLoggers();
-
- if (loggers.isEmpty()) {
+ private static void reportIgnoredStaticLoggerBinders(Set<URL> staticLoggerBinderPathSet) {
+ if (staticLoggerBinderPathSet.isEmpty()) {
return;
}
+ Reporter.warn("Class path contains SLF4J bindings targeting slf4j-api versions 1.7.x or earlier.");
- Util.report("The following set of substitute loggers may have been accessed");
- Util.report("during the initialization phase. Logging calls during this");
- Util.report("phase were not honored. However, subsequent logging calls to these");
- Util.report("loggers will work as normally expected.");
- Util.report("See also " + SUBSTITUTE_LOGGER_URL);
- for (SubstituteLogger subLogger : loggers) {
- subLogger.setDelegate(getLogger(subLogger.getName()));
- Util.report(subLogger.getName());
+ for (URL path : staticLoggerBinderPathSet) {
+ Reporter.warn("Ignoring binding found at [" + path + "]");
}
+ Reporter.warn("See " + IGNORED_BINDINGS_URL + " for an explanation.");
- TEMP_FACTORY.clear();
- }
-
- private final static void versionSanityCheck() {
- try {
- String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
-
- boolean match = false;
- for (int i = 0; i < API_COMPATIBILITY_LIST.length; i++) {
- if (requested.startsWith(API_COMPATIBILITY_LIST[i])) {
- match = true;
- }
- }
- if (!match) {
- Util.report("The requested version " + requested + " by your slf4j binding is not compatible with "
- + Arrays.asList(API_COMPATIBILITY_LIST).toString());
- Util.report("See " + VERSION_MISMATCH + " for further details.");
- }
- } catch (java.lang.NoSuchFieldError nsfe) {
- // given our large user base and SLF4J's commitment to backward
- // compatibility, we cannot cry here. Only for implementations
- // which willingly declare a REQUESTED_API_VERSION field do we
- // emit compatibility warnings.
- } catch (Throwable e) {
- // we should never reach here
- Util.report("Unexpected problem occured during version sanity check", e);
- }
}
- // We need to use the name of the StaticLoggerBinder class, but we can't reference
- // the class itself.
- private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
+ // We need to use the name of the StaticLoggerBinder class, but we can't
+ // reference the class itself.
+ private static final String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
- private static Set<URL> findPossibleStaticLoggerBinderPathSet() {
+ static Set<URL> findPossibleStaticLoggerBinderPathSet() {
// use Set instead of list in order to deal with bug #138
- // LinkedHashSet appropriate here because it preserves insertion order during iteration
- Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
+ // LinkedHashSet appropriate here because it preserves insertion order
+ // during iteration
+ Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<>();
try {
ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
Enumeration<URL> paths;
@@ -236,47 +266,155 @@ public final class LoggerFactory {
paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
}
while (paths.hasMoreElements()) {
- URL path = (URL) paths.nextElement();
+ URL path = paths.nextElement();
staticLoggerBinderPathSet.add(path);
}
} catch (IOException ioe) {
- Util.report("Error getting resources from path", ioe);
+ Reporter.error("Error getting resources from path", ioe);
}
return staticLoggerBinderPathSet;
}
- private static boolean isAmbiguousStaticLoggerBinderPathSet(Set<URL> staticLoggerBinderPathSet) {
- return staticLoggerBinderPathSet.size() > 1;
+ private static void postBindCleanUp() {
+ fixSubstituteLoggers();
+ replayEvents();
+ // release all resources in SUBST_FACTORY
+ SUBST_PROVIDER.getSubstituteLoggerFactory().clear();
+ }
+
+ private static void fixSubstituteLoggers() {
+ synchronized (SUBST_PROVIDER) {
+ SUBST_PROVIDER.getSubstituteLoggerFactory().postInitialization();
+ for (SubstituteLogger substLogger : SUBST_PROVIDER.getSubstituteLoggerFactory().getLoggers()) {
+ Logger logger = getLogger(substLogger.getName());
+ substLogger.setDelegate(logger);
+ }
+ }
+
+ }
+
+ static void failedBinding(Throwable t) {
+ INITIALIZATION_STATE = FAILED_INITIALIZATION;
+ Reporter.error("Failed to instantiate SLF4J LoggerFactory", t);
+ }
+
+ private static void replayEvents() {
+ final LinkedBlockingQueue<SubstituteLoggingEvent> queue = SUBST_PROVIDER.getSubstituteLoggerFactory().getEventQueue();
+ final int queueSize = queue.size();
+ int count = 0;
+ final int maxDrain = 128;
+ List<SubstituteLoggingEvent> eventList = new ArrayList<>(maxDrain);
+ while (true) {
+ int numDrained = queue.drainTo(eventList, maxDrain);
+ if (numDrained == 0)
+ break;
+ for (SubstituteLoggingEvent event : eventList) {
+ replaySingleEvent(event);
+ if (count++ == 0)
+ emitReplayOrSubstituionWarning(event, queueSize);
+ }
+ eventList.clear();
+ }
+ }
+
+ private static void emitReplayOrSubstituionWarning(SubstituteLoggingEvent event, int queueSize) {
+ if (event.getLogger().isDelegateEventAware()) {
+ emitReplayWarning(queueSize);
+ } else if (event.getLogger().isDelegateNOP()) {
+ // nothing to do
+ } else {
+ emitSubstitutionWarning();
+ }
+ }
+
+ private static void replaySingleEvent(SubstituteLoggingEvent event) {
+ if (event == null)
+ return;
+
+ SubstituteLogger substLogger = event.getLogger();
+ String loggerName = substLogger.getName();
+ if (substLogger.isDelegateNull()) {
+ throw new IllegalStateException("Delegate logger cannot be null at this state.");
+ }
+
+ if (substLogger.isDelegateNOP()) {
+ // nothing to do
+ } else if (substLogger.isDelegateEventAware()) {
+ if(substLogger.isEnabledForLevel(event.getLevel())) {
+ substLogger.log(event);
+ }
+ } else {
+ Reporter.warn(loggerName);
+ }
+ }
+
+ private static void emitSubstitutionWarning() {
+ Reporter.warn("The following set of substitute loggers may have been accessed");
+ Reporter.warn("during the initialization phase. Logging calls during this");
+ Reporter.warn("phase were not honored. However, subsequent logging calls to these");
+ Reporter.warn("loggers will work as normally expected.");
+ Reporter.warn("See also " + SUBSTITUTE_LOGGER_URL);
+ }
+
+ private static void emitReplayWarning(int eventCount) {
+ Reporter.warn("A number (" + eventCount + ") of logging calls during the initialization phase have been intercepted and are");
+ Reporter.warn("now being replayed. These are subject to the filtering rules of the underlying logging system.");
+ Reporter.warn("See also " + REPLAY_URL);
+ }
+
+ private final static void versionSanityCheck() {
+ try {
+ String requested = PROVIDER.getRequestedApiVersion();
+
+ boolean match = false;
+ for (String aAPI_COMPATIBILITY_LIST : API_COMPATIBILITY_LIST) {
+ if (requested.startsWith(aAPI_COMPATIBILITY_LIST)) {
+ match = true;
+ }
+ }
+ if (!match) {
+ Reporter.warn("The requested version " + requested + " by your slf4j provider is not compatible with "
+ + Arrays.asList(API_COMPATIBILITY_LIST).toString());
+ Reporter.warn("See " + VERSION_MISMATCH + " for further details.");
+ }
+ } catch (Throwable e) {
+ // we should never reach here
+ Reporter.error("Unexpected problem occurred during version sanity check", e);
+ }
+ }
+
+ private static boolean isAmbiguousProviderList(List<SLF4JServiceProvider> providerList) {
+ return providerList.size() > 1;
}
/**
- * Prints a warning message on the console if multiple bindings were found on the class path.
- * No reporting is done otherwise.
+ * Prints a warning message on the console if multiple bindings were found
+ * on the class path. No reporting is done otherwise.
*
*/
- private static void reportMultipleBindingAmbiguity(Set<URL> staticLoggerBinderPathSet) {
- if (isAmbiguousStaticLoggerBinderPathSet(staticLoggerBinderPathSet)) {
- Util.report("Class path contains multiple SLF4J bindings.");
- Iterator<URL> iterator = staticLoggerBinderPathSet.iterator();
- while (iterator.hasNext()) {
- URL path = (URL) iterator.next();
- Util.report("Found binding in [" + path + "]");
+ private static void reportMultipleBindingAmbiguity(List<SLF4JServiceProvider> providerList) {
+ if (isAmbiguousProviderList(providerList)) {
+ Reporter.warn("Class path contains multiple SLF4J providers.");
+ for (SLF4JServiceProvider provider : providerList) {
+ Reporter.warn("Found provider [" + provider + "]");
}
- Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
+ Reporter.warn("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
}
}
- private static void reportActualBinding(Set<URL> staticLoggerBinderPathSet) {
- if (isAmbiguousStaticLoggerBinderPathSet(staticLoggerBinderPathSet)) {
- Util.report("Actual binding is of type [" + StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr() + "]");
+ private static void reportActualBinding(List<SLF4JServiceProvider> providerList) {
+ // binderPathSet can be null under Android
+ if (!providerList.isEmpty() && isAmbiguousProviderList(providerList)) {
+ Reporter.info("Actual provider is of type [" + providerList.get(0) + "]");
}
}
/**
- * Return a logger named according to the name parameter using the statically
- * bound {@link ILoggerFactory} instance.
+ * Return a logger named according to the name parameter using the
+ * statically bound {@link ILoggerFactory} instance.
*
- * @param name The name of the logger.
+ * @param name
+ * The name of the logger.
* @return logger
*/
public static Logger getLogger(String name) {
@@ -285,29 +423,34 @@ public final class LoggerFactory {
}
/**
- * Return a logger named corresponding to the class passed as parameter, using
- * the statically bound {@link ILoggerFactory} instance.
+ * Return a logger named corresponding to the class passed as parameter,
+ * using the statically bound {@link ILoggerFactory} instance.
+ *
+ * <p>
+ * In case the <code>clazz</code> parameter differs from the name of the
+ * caller as computed internally by SLF4J, a logger name mismatch warning
+ * will be printed but only if the
+ * <code>slf4j.detectLoggerNameMismatch</code> system property is set to
+ * true. By default, this property is not set and no warnings will be
+ * printed even in case of a logger name mismatch.
*
- * <p>In case the the <code>clazz</code> parameter differs from the name of
- * the caller as computed internally by SLF4J, a logger name mismatch warning will be
- * printed but only if the <code>slf4j.detectLoggerNameMismatch</code> system property is
- * set to true. By default, this property is not set and no warnings will be printed
- * even in case of a logger name mismatch.
- *
- * @param clazz the returned logger will be named after clazz
+ * @param clazz
+ * the returned logger will be named after clazz
* @return logger
*
*
- * @see <a href="http://www.slf4j.org/codes.html#loggerNameMismatch">Detected logger name mismatch</a>
+ * @see <a
+ * href="http://www.slf4j.org/codes.html#loggerNameMismatch">Detected
+ * logger name mismatch</a>
*/
public static Logger getLogger(Class<?> clazz) {
Logger logger = getLogger(clazz.getName());
if (DETECT_LOGGER_NAME_MISMATCH) {
Class<?> autoComputedCallingClass = Util.getCallingClass();
- if (nonMatchingClasses(clazz, autoComputedCallingClass)) {
- Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),
+ if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
+ Reporter.warn(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),
autoComputedCallingClass.getName()));
- Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
+ Reporter.warn("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
}
}
return logger;
@@ -319,28 +462,42 @@ public final class LoggerFactory {
/**
* Return the {@link ILoggerFactory} instance in use.
- * <p/>
- * <p/>
+ * <p>
+ * <p>
* ILoggerFactory instance is bound with this class at compile time.
*
* @return the ILoggerFactory instance in use
*/
public static ILoggerFactory getILoggerFactory() {
+ return getProvider().getLoggerFactory();
+ }
+
+ /**
+ * Return the {@link SLF4JServiceProvider} in use.
+
+ * @return provider in use
+ * @since 1.8.0
+ */
+ static SLF4JServiceProvider getProvider() {
if (INITIALIZATION_STATE == UNINITIALIZED) {
- INITIALIZATION_STATE = ONGOING_INITIALIZATION;
- performInitialization();
+ synchronized (LoggerFactory.class) {
+ if (INITIALIZATION_STATE == UNINITIALIZED) {
+ INITIALIZATION_STATE = ONGOING_INITIALIZATION;
+ performInitialization();
+ }
+ }
}
switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITIALIZATION:
- return StaticLoggerBinder.getSingleton().getLoggerFactory();
+ return PROVIDER;
case NOP_FALLBACK_INITIALIZATION:
- return NOP_FALLBACK_FACTORY;
+ return NOP_FALLBACK_SERVICE_PROVIDER;
case FAILED_INITIALIZATION:
throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
case ONGOING_INITIALIZATION:
// support re-entrant behavior.
- // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
- return TEMP_FACTORY;
+ // See also http://jira.qos.ch/browse/SLF4J-97
+ return SUBST_PROVIDER;
}
throw new IllegalStateException("Unreachable code");
}
diff --git a/slf4j-api/src/main/java/org/slf4j/impl/StaticMDCBinder.java b/slf4j-api/src/main/java/org/slf4j/LoggerFactoryFriend.java
index 4fd65f40..06ca2017 100644..100755
--- a/slf4j-api/src/main/java/org/slf4j/impl/StaticMDCBinder.java
+++ b/slf4j-api/src/main/java/org/slf4j/LoggerFactoryFriend.java
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2004-2011 QOS.ch
+ * Copyright (c) 2004-2021 QOS.ch
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
@@ -22,36 +22,34 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
-package org.slf4j.impl;
-
-import org.slf4j.spi.MDCAdapter;
+package org.slf4j;
/**
- * This class is only a stub. Real implementations are found in
- * each SLF4J binding project, e.g. slf4j-nop, slf4j-log4j12 etc.
+ * All methods in this class are reserved for internal use, for testing purposes.
+ *
+ * <p>They can be modified, renamed or removed at any time without notice.
+ *
+ * <p>You are strongly discouraged calling any of the methods of this class.
+ *
+ * @since 1.8.0
*
- * @author Ceki G&uuml;lc&uuml;
+ * @author Ceki G&uuml;lc&uuml;
*/
-public class StaticMDCBinder {
+public class LoggerFactoryFriend {
- /**
- * The unique instance of this class.
+ /*
+ * Force LoggerFactory to consider itself uninitialized.
*/
- public static final StaticMDCBinder SINGLETON = new StaticMDCBinder();
-
- private StaticMDCBinder() {
- throw new UnsupportedOperationException("This code should never make it into the jar");
+ static public void reset() {
+ LoggerFactory.reset();
}
/**
- * Currently this method always returns an instance of
- * {@link StaticMDCBinder}.
+ * Set LoggerFactory.DETECT_LOGGER_NAME_MISMATCH variable.
+ *
+ * @param enabled a boolean
*/
- public MDCAdapter getMDCA() {
- throw new UnsupportedOperationException("This code should never make it into the jar");
- }
-
- public String getMDCAdapterClassStr() {
- throw new UnsupportedOperationException("This code should never make it into the jar");
+ public static void setDetectLoggerNameMismatch(boolean enabled) {
+ LoggerFactory.DETECT_LOGGER_NAME_MISMATCH = enabled;
}
}
diff --git a/slf4j-api/src/main/java/org/slf4j/MDC.java b/slf4j-api/src/main/java/org/slf4j/MDC.java
index 06981772..6b0feded 100644
--- a/slf4j-api/src/main/java/org/slf4j/MDC.java
+++ b/slf4j-api/src/main/java/org/slf4j/MDC.java
@@ -25,13 +25,15 @@
package org.slf4j;
import java.io.Closeable;
+import java.util.Deque;
import java.util.Map;
-import org.slf4j.helpers.NOPMDCAdapter;
import org.slf4j.helpers.BasicMDCAdapter;
+import org.slf4j.helpers.NOPMDCAdapter;
+import org.slf4j.helpers.Reporter;
import org.slf4j.helpers.Util;
-import org.slf4j.impl.StaticMDCBinder;
import org.slf4j.spi.MDCAdapter;
+import org.slf4j.spi.SLF4JServiceProvider;
/**
* This class hides and serves as a substitute for the underlying logging
@@ -42,7 +44,7 @@ import org.slf4j.spi.MDCAdapter;
* i.e. this class, will delegate to the underlying system's MDC. Note that at
* this time, only two logging systems, namely log4j and logback, offer MDC
* functionality. For java.util.logging which does not support MDC,
- * {@link BasicMDCAdapter} will be used. For other systems, i.e slf4j-simple
+ * {@link BasicMDCAdapter} will be used. For other systems, i.e. slf4j-simple
* and slf4j-nop, {@link NOPMDCAdapter} will be used.
*
* <p>
@@ -64,6 +66,7 @@ import org.slf4j.spi.MDCAdapter;
public class MDC {
static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA";
+ private static final String MDC_APAPTER_CANNOT_BE_NULL_MESSAGE = "MDCAdapter cannot be null. See also " + NULL_MDCA_URL;
static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder";
static MDCAdapter mdcAdapter;
@@ -86,21 +89,16 @@ public class MDC {
}
static {
- try {
- mdcAdapter = StaticMDCBinder.SINGLETON.getMDCA();
- } catch (NoClassDefFoundError ncde) {
+ SLF4JServiceProvider provider = LoggerFactory.getProvider();
+ if (provider != null) {
+ // obtain and attach the MDCAdapter from the provider
+ // If you wish to change the adapter, Setting the MDC.mdcAdapter variable might not be enough as
+ // the provider might perform additional assignments that you would need to replicate/adapt.
+ mdcAdapter = provider.getMDCAdapter();
+ } else {
+ Reporter.error("Failed to find provider.");
+ Reporter.error("Defaulting to no-operation MDCAdapter implementation.");
mdcAdapter = new NOPMDCAdapter();
- String msg = ncde.getMessage();
- if (msg != null && msg.indexOf("StaticMDCBinder") != -1) {
- Util.report("Failed to load class \"org.slf4j.impl.StaticMDCBinder\".");
- Util.report("Defaulting to no-operation MDCAdapter implementation.");
- Util.report("See " + NO_STATIC_MDC_BINDER_URL + " for further details.");
- } else {
- throw ncde;
- }
- } catch (Exception e) {
- // we should never get here
- Util.report("MDC binding unsuccessful.", e);
}
}
@@ -124,7 +122,7 @@ public class MDC {
throw new IllegalArgumentException("key parameter cannot be null");
}
if (mdcAdapter == null) {
- throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
+ throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
}
mdcAdapter.put(key, val);
}
@@ -169,7 +167,7 @@ public class MDC {
* <p>
* This method delegates all work to the MDC of the underlying logging system.
*
- * @param key
+ * @param key a key
* @return the string value identified by the <code>key</code> parameter.
* @throws IllegalArgumentException
* in case the "key" parameter is null
@@ -180,7 +178,7 @@ public class MDC {
}
if (mdcAdapter == null) {
- throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
+ throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
}
return mdcAdapter.get(key);
}
@@ -191,7 +189,7 @@ public class MDC {
* cannot be null. This method does nothing if there is no previous value
* associated with <code>key</code>.
*
- * @param key
+ * @param key a key
* @throws IllegalArgumentException
* in case the "key" parameter is null
*/
@@ -201,7 +199,7 @@ public class MDC {
}
if (mdcAdapter == null) {
- throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
+ throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
}
mdcAdapter.remove(key);
}
@@ -211,7 +209,7 @@ public class MDC {
*/
public static void clear() {
if (mdcAdapter == null) {
- throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
+ throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
}
mdcAdapter.clear();
}
@@ -225,7 +223,7 @@ public class MDC {
*/
public static Map<String, String> getCopyOfContextMap() {
if (mdcAdapter == null) {
- throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
+ throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
}
return mdcAdapter.getCopyOfContextMap();
}
@@ -235,13 +233,15 @@ public class MDC {
* then copying the map passed as parameter. The context map passed as
* parameter must only contain keys and values of type String.
*
+ * Null valued argument is allowed (since SLF4J version 2.0.0).
+ *
* @param contextMap
* must contain only keys and values of type String
* @since 1.5.1
*/
public static void setContextMap(Map<String, String> contextMap) {
if (mdcAdapter == null) {
- throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
+ throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
}
mdcAdapter.setContextMap(contextMap);
}
@@ -256,4 +256,48 @@ public class MDC {
return mdcAdapter;
}
+
+
+ /**
+ * Push a value into the deque(stack) referenced by 'key'.
+ *
+ * @param key identifies the appropriate stack
+ * @param value the value to push into the stack
+ * @since 2.0.0
+ */
+ static public void pushByKey(String key, String value) {
+ if (mdcAdapter == null) {
+ throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
+ }
+ mdcAdapter.pushByKey(key, value);
+ }
+
+ /**
+ * Pop the stack referenced by 'key' and return the value possibly null.
+ *
+ * @param key identifies the deque(stack)
+ * @return the value just popped. May be null/
+ * @since 2.0.0
+ */
+ static public String popByKey(String key) {
+ if (mdcAdapter == null) {
+ throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
+ }
+ return mdcAdapter.popByKey(key);
+ }
+
+ /**
+ * Returns a copy of the deque(stack) referenced by 'key'. May be null.
+ *
+ * @param key identifies the stack
+ * @return copy of stack referenced by 'key'. May be null.
+ *
+ * @since 2.0.0
+ */
+ public Deque<String> getCopyOfDequeByKey(String key) {
+ if (mdcAdapter == null) {
+ throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
+ }
+ return mdcAdapter.getCopyOfDequeByKey(key);
+ }
}
diff --git a/slf4j-api/src/main/java/org/slf4j/Marker.java b/slf4j-api/src/main/java/org/slf4j/Marker.java
index c6bfd5e2..4a6dd100 100644
--- a/slf4j-api/src/main/java/org/slf4j/Marker.java
+++ b/slf4j-api/src/main/java/org/slf4j/Marker.java
@@ -29,14 +29,16 @@ import java.util.Iterator;
/**
* Markers are named objects used to enrich log statements. Conforming logging
- * system Implementations of SLF4J determine how information conveyed by markers
- * are used, if at all. In particular, many conforming logging systems ignore
- * marker data.
- *
- * <p>
- * Markers can contain references to other markers, which in turn may contain
- * references of their own.
- *
+ * system implementations of SLF4J should determine how information conveyed by
+ * any markers are used, if at all. Many conforming logging systems ignore marker
+ * data entirely.
+ *
+ * <p>Markers can contain references to nested markers, which in turn may
+ * contain references of their own. Note that the fluent API (new in 2.0) allows adding
+ * multiple markers to a logging statement. It is often preferable to use
+ * multiple markers instead of nested markers.
+ * </p>
+ *
* @author Ceki G&uuml;lc&uuml;
*/
public interface Marker extends Serializable {
@@ -60,7 +62,11 @@ public interface Marker extends Serializable {
/**
* Add a reference to another Marker.
- *
+ *
+ * <p>Note that the fluent API allows adding multiple markers to a logging statement.
+ * It is often preferable to use multiple markers instead of nested markers.
+ * </p>
+ *
* @param reference
* a reference to another marker
* @throws IllegalArgumentException
@@ -80,6 +86,7 @@ public interface Marker extends Serializable {
/**
* @deprecated Replaced by {@link #hasReferences()}.
*/
+ @Deprecated
public boolean hasChildren();
/**
diff --git a/slf4j-api/src/main/java/org/slf4j/MarkerFactory.java b/slf4j-api/src/main/java/org/slf4j/MarkerFactory.java
index 8d50698d..0b15d6ff 100644
--- a/slf4j-api/src/main/java/org/slf4j/MarkerFactory.java
+++ b/slf4j-api/src/main/java/org/slf4j/MarkerFactory.java
@@ -25,8 +25,9 @@
package org.slf4j;
import org.slf4j.helpers.BasicMarkerFactory;
+import org.slf4j.helpers.Reporter;
import org.slf4j.helpers.Util;
-import org.slf4j.impl.StaticMarkerBinder;
+import org.slf4j.spi.SLF4JServiceProvider;
/**
* MarkerFactory is a utility class producing {@link Marker} instances as
@@ -42,20 +43,20 @@ import org.slf4j.impl.StaticMarkerBinder;
* @author Ceki G&uuml;lc&uuml;
*/
public class MarkerFactory {
- static IMarkerFactory markerFactory;
+ static IMarkerFactory MARKER_FACTORY;
private MarkerFactory() {
}
+ // this is where the binding happens
static {
- try {
- markerFactory = StaticMarkerBinder.SINGLETON.getMarkerFactory();
- } catch (NoClassDefFoundError e) {
- markerFactory = new BasicMarkerFactory();
-
- } catch (Exception e) {
- // we should never get here
- Util.report("Unexpected failure while binding MarkerFactory", e);
+ SLF4JServiceProvider provider = LoggerFactory.getProvider();
+ if (provider != null) {
+ MARKER_FACTORY = provider.getMarkerFactory();
+ } else {
+ Reporter.error("Failed to find provider");
+ Reporter.error("Defaulting to BasicMarkerFactory.");
+ MARKER_FACTORY = new BasicMarkerFactory();
}
}
@@ -68,7 +69,7 @@ public class MarkerFactory {
* @return marker
*/
public static Marker getMarker(String name) {
- return markerFactory.getMarker(name);
+ return MARKER_FACTORY.getMarker(name);
}
/**
@@ -79,7 +80,7 @@ public class MarkerFactory {
* @since 1.5.1
*/
public static Marker getDetachedMarker(String name) {
- return markerFactory.getDetachedMarker(name);
+ return MARKER_FACTORY.getDetachedMarker(name);
}
/**
@@ -91,6 +92,6 @@ public class MarkerFactory {
* @return the IMarkerFactory instance in use
*/
public static IMarkerFactory getIMarkerFactory() {
- return markerFactory;
+ return MARKER_FACTORY;
}
} \ No newline at end of file
diff --git a/slf4j-api/src/main/java/org/slf4j/event/DefaultLoggingEvent.java b/slf4j-api/src/main/java/org/slf4j/event/DefaultLoggingEvent.java
new file mode 100755
index 00000000..5a101438
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/event/DefaultLoggingEvent.java
@@ -0,0 +1,140 @@
+package org.slf4j.event;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.Marker;
+
+/**
+ * A default implementation of {@link LoggingEvent}.
+ *
+ * @author Ceki G&uuml;lc&uuml;
+ *
+ * @since 2.0.0
+ */
+public class DefaultLoggingEvent implements LoggingEvent {
+
+ Logger logger;
+ Level level;
+
+ String message;
+ List<Marker> markers;
+ List<Object> arguments;
+ List<KeyValuePair> keyValuePairs;
+
+ Throwable throwable;
+ String threadName;
+ long timeStamp;
+
+ String callerBoundary;
+
+ public DefaultLoggingEvent(Level level, Logger logger) {
+ this.logger = logger;
+ this.level = level;
+ }
+
+ public void addMarker(Marker marker) {
+ if (markers == null) {
+ markers = new ArrayList<>(2);
+ }
+ markers.add(marker);
+ }
+
+ @Override
+ public List<Marker> getMarkers() {
+ return markers;
+ }
+
+ public void addArgument(Object p) {
+ getNonNullArguments().add(p);
+ }
+
+ public void addArguments(Object... args) {
+ getNonNullArguments().addAll(Arrays.asList(args));
+ }
+
+ private List<Object> getNonNullArguments() {
+ if (arguments == null) {
+ arguments = new ArrayList<>(3);
+ }
+ return arguments;
+ }
+
+ @Override
+ public List<Object> getArguments() {
+ return arguments;
+ }
+
+ @Override
+ public Object[] getArgumentArray() {
+ if (arguments == null)
+ return null;
+ return arguments.toArray();
+ }
+
+ public void addKeyValue(String key, Object value) {
+ getNonnullKeyValuePairs().add(new KeyValuePair(key, value));
+ }
+
+ private List<KeyValuePair> getNonnullKeyValuePairs() {
+ if (keyValuePairs == null) {
+ keyValuePairs = new ArrayList<>(4);
+ }
+ return keyValuePairs;
+ }
+
+ @Override
+ public List<KeyValuePair> getKeyValuePairs() {
+ return keyValuePairs;
+ }
+
+ public void setThrowable(Throwable cause) {
+ this.throwable = cause;
+ }
+
+ @Override
+ public Level getLevel() {
+ return level;
+ }
+
+ @Override
+ public String getLoggerName() {
+ return logger.getName();
+ }
+
+ @Override
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ @Override
+ public Throwable getThrowable() {
+ return throwable;
+ }
+
+ public String getThreadName() {
+ return threadName;
+ }
+
+ public long getTimeStamp() {
+ return timeStamp;
+ }
+
+ public void setTimeStamp(long timeStamp) {
+ this.timeStamp = timeStamp;
+ }
+
+ public void setCallerBoundary(String fqcn) {
+ this.callerBoundary = fqcn;
+ }
+
+ public String getCallerBoundary() {
+ return callerBoundary;
+ }
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/event/EventConstants.java b/slf4j-api/src/main/java/org/slf4j/event/EventConstants.java
new file mode 100755
index 00000000..00975da2
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/event/EventConstants.java
@@ -0,0 +1,13 @@
+package org.slf4j.event;
+
+import org.slf4j.spi.LocationAwareLogger;
+
+public class EventConstants {
+ public static final int ERROR_INT = LocationAwareLogger.ERROR_INT;
+ public static final int WARN_INT = LocationAwareLogger.WARN_INT;
+ public static final int INFO_INT = LocationAwareLogger.INFO_INT;
+ public static final int DEBUG_INT = LocationAwareLogger.DEBUG_INT;
+ public static final int TRACE_INT = LocationAwareLogger.TRACE_INT;
+ public static final String NA_SUBST = "NA/SubstituteLogger";
+
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/event/EventRecordingLogger.java b/slf4j-api/src/main/java/org/slf4j/event/EventRecordingLogger.java
new file mode 100755
index 00000000..b21a2e45
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/event/EventRecordingLogger.java
@@ -0,0 +1,84 @@
+package org.slf4j.event;
+
+import java.util.Queue;
+
+import org.slf4j.Marker;
+import org.slf4j.helpers.LegacyAbstractLogger;
+import org.slf4j.helpers.SubstituteLogger;
+
+/**
+ *
+ * This class is used to record events during the initialization phase of the
+ * underlying logging framework. It is called by {@link SubstituteLogger}.
+ *
+ *
+ * @author Ceki G&uuml;lc&uuml;
+ * @author Wessel van Norel
+ *
+ */
+public class EventRecordingLogger extends LegacyAbstractLogger {
+
+ private static final long serialVersionUID = -176083308134819629L;
+
+ String name;
+ SubstituteLogger logger;
+ Queue<SubstituteLoggingEvent> eventQueue;
+
+ // as an event recording logger we have no choice but to record all events
+ final static boolean RECORD_ALL_EVENTS = true;
+
+ public EventRecordingLogger(SubstituteLogger logger, Queue<SubstituteLoggingEvent> eventQueue) {
+ this.logger = logger;
+ this.name = logger.getName();
+ this.eventQueue = eventQueue;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isTraceEnabled() {
+ return RECORD_ALL_EVENTS;
+ }
+
+ public boolean isDebugEnabled() {
+ return RECORD_ALL_EVENTS;
+ }
+
+ public boolean isInfoEnabled() {
+ return RECORD_ALL_EVENTS;
+ }
+
+ public boolean isWarnEnabled() {
+ return RECORD_ALL_EVENTS;
+ }
+
+ public boolean isErrorEnabled() {
+ return RECORD_ALL_EVENTS;
+ }
+
+ // WARNING: this method assumes that any throwable is properly extracted
+ protected void handleNormalizedLoggingCall(Level level, Marker marker, String msg, Object[] args, Throwable throwable) {
+ SubstituteLoggingEvent loggingEvent = new SubstituteLoggingEvent();
+ loggingEvent.setTimeStamp(System.currentTimeMillis());
+ loggingEvent.setLevel(level);
+ loggingEvent.setLogger(logger);
+ loggingEvent.setLoggerName(name);
+ if (marker != null) {
+ loggingEvent.addMarker(marker);
+ }
+ loggingEvent.setMessage(msg);
+ loggingEvent.setThreadName(Thread.currentThread().getName());
+
+ loggingEvent.setArgumentArray(args);
+ loggingEvent.setThrowable(throwable);
+
+ eventQueue.add(loggingEvent);
+
+ }
+
+ @Override
+ protected String getFullyQualifiedCallerName() {
+ return null;
+ }
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/event/KeyValuePair.java b/slf4j-api/src/main/java/org/slf4j/event/KeyValuePair.java
new file mode 100755
index 00000000..9aebe198
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/event/KeyValuePair.java
@@ -0,0 +1,32 @@
+package org.slf4j.event;
+
+import java.util.Objects;
+
+public class KeyValuePair {
+
+ public final String key;
+ public final Object value;
+
+ public KeyValuePair(String key, Object value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(key) + "=\"" + String.valueOf(value) +"\"";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if(this == o) return true;
+ if(o == null || getClass() != o.getClass()) return false;
+ KeyValuePair that = (KeyValuePair) o;
+ return Objects.equals(key, that.key) && Objects.equals(value, that.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(key, value);
+ }
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/event/Level.java b/slf4j-api/src/main/java/org/slf4j/event/Level.java
new file mode 100755
index 00000000..a288b06b
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/event/Level.java
@@ -0,0 +1,56 @@
+package org.slf4j.event;
+
+import static org.slf4j.event.EventConstants.DEBUG_INT;
+import static org.slf4j.event.EventConstants.ERROR_INT;
+import static org.slf4j.event.EventConstants.INFO_INT;
+import static org.slf4j.event.EventConstants.TRACE_INT;
+import static org.slf4j.event.EventConstants.WARN_INT;
+
+/**
+ * SLF4J's internal representation of Level.
+ *
+ *
+ * @author Ceki G&uuml;lc&uuml;
+ * @since 1.7.15
+ */
+public enum Level {
+
+ ERROR(ERROR_INT, "ERROR"), WARN(WARN_INT, "WARN"), INFO(INFO_INT, "INFO"), DEBUG(DEBUG_INT, "DEBUG"), TRACE(TRACE_INT, "TRACE");
+
+ private final int levelInt;
+ private final String levelStr;
+
+ Level(int i, String s) {
+ levelInt = i;
+ levelStr = s;
+ }
+
+ public int toInt() {
+ return levelInt;
+ }
+
+ public static Level intToLevel(int levelInt) {
+ switch (levelInt) {
+ case (TRACE_INT):
+ return TRACE;
+ case (DEBUG_INT):
+ return DEBUG;
+ case (INFO_INT):
+ return INFO;
+ case (WARN_INT):
+ return WARN;
+ case (ERROR_INT):
+ return ERROR;
+ default:
+ throw new IllegalArgumentException("Level integer [" + levelInt + "] not recognized.");
+ }
+ }
+
+ /**
+ * Returns the string representation of this Level.
+ */
+ public String toString() {
+ return levelStr;
+ }
+
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/event/LoggingEvent.java b/slf4j-api/src/main/java/org/slf4j/event/LoggingEvent.java
new file mode 100755
index 00000000..27bdf3e2
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/event/LoggingEvent.java
@@ -0,0 +1,45 @@
+package org.slf4j.event;
+
+import java.util.List;
+
+import org.slf4j.Marker;
+
+/**
+ * The minimal interface sufficient for the restitution of data passed
+ * by the user to the SLF4J API.
+ *
+ * @author Ceki G&uuml;lc&uuml;
+ * @since 1.7.15
+ */
+public interface LoggingEvent {
+
+ Level getLevel();
+
+ String getLoggerName();
+
+ String getMessage();
+
+ List<Object> getArguments();
+
+ Object[] getArgumentArray();
+
+ List<Marker> getMarkers();
+
+ List<KeyValuePair> getKeyValuePairs();
+
+ Throwable getThrowable();
+
+ long getTimeStamp();
+
+ String getThreadName();
+
+ /**
+ * Returns the presumed caller boundary provided by the logging library (not the user of the library).
+ * Null by default.
+ *
+ * @return presumed caller, null by default.
+ */
+ default String getCallerBoundary() {
+ return null;
+ }
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/event/SubstituteLoggingEvent.java b/slf4j-api/src/main/java/org/slf4j/event/SubstituteLoggingEvent.java
new file mode 100755
index 00000000..4c8f7ad7
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/event/SubstituteLoggingEvent.java
@@ -0,0 +1,115 @@
+package org.slf4j.event;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.slf4j.Marker;
+import org.slf4j.helpers.SubstituteLogger;
+
+public class SubstituteLoggingEvent implements LoggingEvent {
+
+ Level level;
+ List<Marker> markers;
+ String loggerName;
+ SubstituteLogger logger;
+ String threadName;
+ String message;
+ Object[] argArray;
+ List<KeyValuePair> keyValuePairList;
+
+ long timeStamp;
+ Throwable throwable;
+
+ public Level getLevel() {
+ return level;
+ }
+
+ public void setLevel(Level level) {
+ this.level = level;
+ }
+
+ public List<Marker> getMarkers() {
+ return markers;
+ }
+
+ public void addMarker(Marker marker) {
+ if (marker == null)
+ return;
+
+ if (markers == null) {
+ markers = new ArrayList<>(2);
+ }
+
+ markers.add(marker);
+ }
+
+ public String getLoggerName() {
+ return loggerName;
+ }
+
+ public void setLoggerName(String loggerName) {
+ this.loggerName = loggerName;
+ }
+
+ public SubstituteLogger getLogger() {
+ return logger;
+ }
+
+ public void setLogger(SubstituteLogger logger) {
+ this.logger = logger;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public Object[] getArgumentArray() {
+ return argArray;
+ }
+
+ public void setArgumentArray(Object[] argArray) {
+ this.argArray = argArray;
+ }
+
+ @Override
+ public List<Object> getArguments() {
+ if (argArray == null) {
+ return null;
+ }
+ return Arrays.asList(argArray);
+ }
+
+ public long getTimeStamp() {
+ return timeStamp;
+ }
+
+ public void setTimeStamp(long timeStamp) {
+ this.timeStamp = timeStamp;
+ }
+
+ public String getThreadName() {
+ return threadName;
+ }
+
+ public void setThreadName(String threadName) {
+ this.threadName = threadName;
+ }
+
+ public Throwable getThrowable() {
+ return throwable;
+ }
+
+ public void setThrowable(Throwable throwable) {
+ this.throwable = throwable;
+ }
+
+ @Override
+ public List<KeyValuePair> getKeyValuePairs() {
+ return keyValuePairList;
+ }
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/AbstractLogger.java b/slf4j-api/src/main/java/org/slf4j/helpers/AbstractLogger.java
new file mode 100644
index 00000000..0caaf810
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/AbstractLogger.java
@@ -0,0 +1,423 @@
+/**
+ * Copyright (c) 2004-2019 QOS.ch
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+package org.slf4j.helpers;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Marker;
+import org.slf4j.event.Level;
+
+/**
+ * An abstract implementation which delegates actual logging work to the
+ * {@link #handleNormalizedLoggingCall(Level, Marker, String, Object[], Throwable)} method.
+ *
+ * @author Ceki G&uuml;lc&uuml;
+ * @since 2.0
+ */
+public abstract class AbstractLogger implements Logger, Serializable {
+
+ private static final long serialVersionUID = -2529255052481744503L;
+
+ protected String name;
+
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Replace this instance with a homonymous (same name) logger returned
+ * by LoggerFactory. Note that this method is only called during
+ * deserialization.
+ *
+ * <p>
+ * This approach will work well if the desired ILoggerFactory is the one
+ * referenced by {@link org.slf4j.LoggerFactory} However, if the user manages its logger hierarchy
+ * through a different (non-static) mechanism, e.g. dependency injection, then
+ * this approach would be mostly counterproductive.
+ *
+ * @return logger with same name as returned by LoggerFactory
+ * @throws ObjectStreamException
+ */
+ protected Object readResolve() throws ObjectStreamException {
+ // using getName() instead of this.name works even for
+ // NOPLogger
+ return LoggerFactory.getLogger(getName());
+ }
+
+ @Override
+ public void trace(String msg) {
+ if (isTraceEnabled()) {
+ handle_0ArgsCall(Level.TRACE, null, msg, null);
+ }
+ }
+
+ @Override
+ public void trace(String format, Object arg) {
+ if (isTraceEnabled()) {
+ handle_1ArgsCall(Level.TRACE, null, format, arg);
+ }
+ }
+
+ @Override
+ public void trace(String format, Object arg1, Object arg2) {
+ if (isTraceEnabled()) {
+ handle2ArgsCall(Level.TRACE, null, format, arg1, arg2);
+ }
+ }
+
+ @Override
+ public void trace(String format, Object... arguments) {
+ if (isTraceEnabled()) {
+ handleArgArrayCall(Level.TRACE, null, format, arguments);
+ }
+ }
+
+ @Override
+ public void trace(String msg, Throwable t) {
+ if (isTraceEnabled()) {
+ handle_0ArgsCall(Level.TRACE, null, msg, t);
+ }
+ }
+
+ @Override
+ public void trace(Marker marker, String msg) {
+ if (isTraceEnabled(marker)) {
+ handle_0ArgsCall(Level.TRACE, marker, msg, null);
+ }
+ }
+
+ @Override
+ public void trace(Marker marker, String format, Object arg) {
+ if (isTraceEnabled(marker)) {
+ handle_1ArgsCall(Level.TRACE, marker, format, arg);
+ }
+ }
+
+ @Override
+ public void trace(Marker marker, String format, Object arg1, Object arg2) {
+ if (isTraceEnabled(marker)) {
+ handle2ArgsCall(Level.TRACE, marker, format, arg1, arg2);
+ }
+ }
+
+ @Override
+ public void trace(Marker marker, String format, Object... argArray) {
+ if (isTraceEnabled(marker)) {
+ handleArgArrayCall(Level.TRACE, marker, format, argArray);
+ }
+ }
+
+ public void trace(Marker marker, String msg, Throwable t) {
+ if (isTraceEnabled(marker)) {
+ handle_0ArgsCall(Level.TRACE, marker, msg, t);
+ }
+ }
+
+ public void debug(String msg) {
+ if (isDebugEnabled()) {
+ handle_0ArgsCall(Level.DEBUG, null, msg, null);
+ }
+ }
+
+ public void debug(String format, Object arg) {
+ if (isDebugEnabled()) {
+ handle_1ArgsCall(Level.DEBUG, null, format, arg);
+ }
+ }
+
+ public void debug(String format, Object arg1, Object arg2) {
+ if (isDebugEnabled()) {
+ handle2ArgsCall(Level.DEBUG, null, format, arg1, arg2);
+ }
+ }
+
+ public void debug(String format, Object... arguments) {
+ if (isDebugEnabled()) {
+ handleArgArrayCall(Level.DEBUG, null, format, arguments);
+ }
+ }
+
+ public void debug(String msg, Throwable t) {
+ if (isDebugEnabled()) {
+ handle_0ArgsCall(Level.DEBUG, null, msg, t);
+ }
+ }
+
+ public void debug(Marker marker, String msg) {
+ if (isDebugEnabled(marker)) {
+ handle_0ArgsCall(Level.DEBUG, marker, msg, null);
+ }
+ }
+
+ public void debug(Marker marker, String format, Object arg) {
+ if (isDebugEnabled(marker)) {
+ handle_1ArgsCall(Level.DEBUG, marker, format, arg);
+ }
+ }
+
+ public void debug(Marker marker, String format, Object arg1, Object arg2) {
+ if (isDebugEnabled(marker)) {
+ handle2ArgsCall(Level.DEBUG, marker, format, arg1, arg2);
+ }
+ }
+
+ public void debug(Marker marker, String format, Object... arguments) {
+ if (isDebugEnabled(marker)) {
+ handleArgArrayCall(Level.DEBUG, marker, format, arguments);
+ }
+ }
+
+ public void debug(Marker marker, String msg, Throwable t) {
+ if (isDebugEnabled(marker)) {
+ handle_0ArgsCall(Level.DEBUG, marker, msg, t);
+ }
+ }
+
+ public void info(String msg) {
+ if (isInfoEnabled()) {
+ handle_0ArgsCall(Level.INFO, null, msg, null);
+ }
+ }
+
+ public void info(String format, Object arg) {
+ if (isInfoEnabled()) {
+ handle_1ArgsCall(Level.INFO, null, format, arg);
+ }
+ }
+
+ public void info(String format, Object arg1, Object arg2) {
+ if (isInfoEnabled()) {
+ handle2ArgsCall(Level.INFO, null, format, arg1, arg2);
+ }
+ }
+
+ public void info(String format, Object... arguments) {
+ if (isInfoEnabled()) {
+ handleArgArrayCall(Level.INFO, null, format, arguments);
+ }
+ }
+
+ public void info(String msg, Throwable t) {
+ if (isInfoEnabled()) {
+ handle_0ArgsCall(Level.INFO, null, msg, t);
+ }
+ }
+
+ public void info(Marker marker, String msg) {
+ if (isInfoEnabled(marker)) {
+ handle_0ArgsCall(Level.INFO, marker, msg, null);
+ }
+ }
+
+ public void info(Marker marker, String format, Object arg) {
+ if (isInfoEnabled(marker)) {
+ handle_1ArgsCall(Level.INFO, marker, format, arg);
+ }
+ }
+
+ public void info(Marker marker, String format, Object arg1, Object arg2) {
+ if (isInfoEnabled(marker)) {
+ handle2ArgsCall(Level.INFO, marker, format, arg1, arg2);
+ }
+ }
+
+ public void info(Marker marker, String format, Object... arguments) {
+ if (isInfoEnabled(marker)) {
+ handleArgArrayCall(Level.INFO, marker, format, arguments);
+ }
+ }
+
+ public void info(Marker marker, String msg, Throwable t) {
+ if (isInfoEnabled(marker)) {
+ handle_0ArgsCall(Level.INFO, marker, msg, t);
+ }
+ }
+
+ public void warn(String msg) {
+ if (isWarnEnabled()) {
+ handle_0ArgsCall(Level.WARN, null, msg, null);
+ }
+ }
+
+ public void warn(String format, Object arg) {
+ if (isWarnEnabled()) {
+ handle_1ArgsCall(Level.WARN, null, format, arg);
+ }
+ }
+
+ public void warn(String format, Object arg1, Object arg2) {
+ if (isWarnEnabled()) {
+ handle2ArgsCall(Level.WARN, null, format, arg1, arg2);
+ }
+ }
+
+ public void warn(String format, Object... arguments) {
+ if (isWarnEnabled()) {
+ handleArgArrayCall(Level.WARN, null, format, arguments);
+ }
+ }
+
+ public void warn(String msg, Throwable t) {
+ if (isWarnEnabled()) {
+ handle_0ArgsCall(Level.WARN, null, msg, t);
+ }
+ }
+
+ public void warn(Marker marker, String msg) {
+ if (isWarnEnabled(marker)) {
+ handle_0ArgsCall(Level.WARN, marker, msg, null);
+ }
+ }
+
+ public void warn(Marker marker, String format, Object arg) {
+ if (isWarnEnabled(marker)) {
+ handle_1ArgsCall(Level.WARN, marker, format, arg);
+ }
+ }
+
+ public void warn(Marker marker, String format, Object arg1, Object arg2) {
+ if (isWarnEnabled(marker)) {
+ handle2ArgsCall(Level.WARN, marker, format, arg1, arg2);
+ }
+ }
+
+ public void warn(Marker marker, String format, Object... arguments) {
+ if (isWarnEnabled(marker)) {
+ handleArgArrayCall(Level.WARN, marker, format, arguments);
+ }
+ }
+
+ public void warn(Marker marker, String msg, Throwable t) {
+ if (isWarnEnabled(marker)) {
+ handle_0ArgsCall(Level.WARN, marker, msg, t);
+ }
+ }
+
+ public void error(String msg) {
+ if (isErrorEnabled()) {
+ handle_0ArgsCall(Level.ERROR, null, msg, null);
+ }
+ }
+
+ public void error(String format, Object arg) {
+ if (isErrorEnabled()) {
+ handle_1ArgsCall(Level.ERROR, null, format, arg);
+ }
+ }
+
+ public void error(String format, Object arg1, Object arg2) {
+ if (isErrorEnabled()) {
+ handle2ArgsCall(Level.ERROR, null, format, arg1, arg2);
+ }
+ }
+
+ public void error(String format, Object... arguments) {
+ if (isErrorEnabled()) {
+ handleArgArrayCall(Level.ERROR, null, format, arguments);
+ }
+ }
+
+ public void error(String msg, Throwable t) {
+ if (isErrorEnabled()) {
+ handle_0ArgsCall(Level.ERROR, null, msg, t);
+ }
+ }
+
+ public void error(Marker marker, String msg) {
+ if (isErrorEnabled(marker)) {
+ handle_0ArgsCall(Level.ERROR, marker, msg, null);
+ }
+ }
+
+ public void error(Marker marker, String format, Object arg) {
+ if (isErrorEnabled(marker)) {
+ handle_1ArgsCall(Level.ERROR, marker, format, arg);
+ }
+ }
+
+ public void error(Marker marker, String format, Object arg1, Object arg2) {
+ if (isErrorEnabled(marker)) {
+ handle2ArgsCall(Level.ERROR, marker, format, arg1, arg2);
+ }
+ }
+
+ public void error(Marker marker, String format, Object... arguments) {
+ if (isErrorEnabled(marker)) {
+ handleArgArrayCall(Level.ERROR, marker, format, arguments);
+ }
+ }
+
+ public void error(Marker marker, String msg, Throwable t) {
+ if (isErrorEnabled(marker)) {
+ handle_0ArgsCall(Level.ERROR, marker, msg, t);
+ }
+ }
+
+ private void handle_0ArgsCall(Level level, Marker marker, String msg, Throwable t) {
+ handleNormalizedLoggingCall(level, marker, msg, null, t);
+ }
+
+ private void handle_1ArgsCall(Level level, Marker marker, String msg, Object arg1) {
+ handleNormalizedLoggingCall(level, marker, msg, new Object[] { arg1 }, null);
+ }
+
+ private void handle2ArgsCall(Level level, Marker marker, String msg, Object arg1, Object arg2) {
+ if (arg2 instanceof Throwable) {
+ handleNormalizedLoggingCall(level, marker, msg, new Object[] { arg1 }, (Throwable) arg2);
+ } else {
+ handleNormalizedLoggingCall(level, marker, msg, new Object[] { arg1, arg2 }, null);
+ }
+ }
+
+ private void handleArgArrayCall(Level level, Marker marker, String msg, Object[] args) {
+ Throwable throwableCandidate = MessageFormatter.getThrowableCandidate(args);
+ if (throwableCandidate != null) {
+ Object[] trimmedCopy = MessageFormatter.trimmedCopy(args);
+ handleNormalizedLoggingCall(level, marker, msg, trimmedCopy, throwableCandidate);
+ } else {
+ handleNormalizedLoggingCall(level, marker, msg, args, null);
+ }
+ }
+
+ abstract protected String getFullyQualifiedCallerName();
+
+ /**
+ * Given various arguments passed as parameters, perform actual logging.
+ *
+ * <p>This method assumes that the separation of the args array into actual
+ * objects and a throwable has been already operated.
+ *
+ * @param level the SLF4J level for this event
+ * @param marker The marker to be used for this event, may be null.
+ * @param messagePattern The message pattern which will be parsed and formatted
+ * @param arguments the array of arguments to be formatted, may be null
+ * @param throwable The exception whose stack trace should be logged, may be null
+ */
+ abstract protected void handleNormalizedLoggingCall(Level level, Marker marker, String messagePattern, Object[] arguments, Throwable throwable);
+
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/BasicMDCAdapter.java b/slf4j-api/src/main/java/org/slf4j/helpers/BasicMDCAdapter.java
index 67ab9b5a..95cb904a 100644
--- a/slf4j-api/src/main/java/org/slf4j/helpers/BasicMDCAdapter.java
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/BasicMDCAdapter.java
@@ -27,45 +27,43 @@ package org.slf4j.helpers;
import org.slf4j.spi.MDCAdapter;
import java.util.*;
-import java.util.Map;
/**
* Basic MDC implementation, which can be used with logging systems that lack
* out-of-the-box MDC support.
- *
+ *
* This code was initially inspired by logback's LogbackMDCAdapter. However,
* LogbackMDCAdapter has evolved and is now considerably more sophisticated.
*
* @author Ceki Gulcu
* @author Maarten Bosteels
+ * @author Lukasz Cwik
*
* @since 1.5.0
*/
public class BasicMDCAdapter implements MDCAdapter {
- private InheritableThreadLocal<Map<String, String>> inheritableThreadLocal = new InheritableThreadLocal<Map<String, String>>();
+ private final ThreadLocalMapOfStacks threadLocalMapOfDeques = new ThreadLocalMapOfStacks();
- static boolean isJDK14() {
- try {
- String javaVersion = System.getProperty("java.version");
- return javaVersion.startsWith("1.4");
- } catch (SecurityException se) {
- // punt and assume JDK 1.5 or later
- return false;
+ private final InheritableThreadLocal<Map<String, String>> inheritableThreadLocalMap = new InheritableThreadLocal<Map<String, String>>() {
+ @Override
+ protected Map<String, String> childValue(Map<String, String> parentValue) {
+ if (parentValue == null) {
+ return null;
+ }
+ return new HashMap<>(parentValue);
}
- }
-
- static boolean IS_JDK14 = isJDK14();
+ };
/**
* Put a context value (the <code>val</code> parameter) as identified with
* the <code>key</code> parameter into the current thread's context map.
* Note that contrary to log4j, the <code>val</code> parameter can be null.
- *
+ *
* <p>
* If the current thread does not have a context map it is created as a side
* effect of this call.
- *
+ *
* @throws IllegalArgumentException
* in case the "key" parameter is null
*/
@@ -73,10 +71,10 @@ public class BasicMDCAdapter implements MDCAdapter {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
- Map<String, String> map = (Map<String, String>) inheritableThreadLocal.get();
+ Map<String, String> map = inheritableThreadLocalMap.get();
if (map == null) {
- map = Collections.<String, String> synchronizedMap(new HashMap<String, String>());
- inheritableThreadLocal.set(map);
+ map = new HashMap<>();
+ inheritableThreadLocalMap.set(map);
}
map.put(key, val);
}
@@ -85,19 +83,19 @@ public class BasicMDCAdapter implements MDCAdapter {
* Get the context identified by the <code>key</code> parameter.
*/
public String get(String key) {
- Map<String, String> Map = (Map<String, String>) inheritableThreadLocal.get();
- if ((Map != null) && (key != null)) {
- return (String) Map.get(key);
+ Map<String, String> map = inheritableThreadLocalMap.get();
+ if ((map != null) && (key != null)) {
+ return map.get(key);
} else {
return null;
}
}
/**
- * Remove the the context identified by the <code>key</code> parameter.
+ * Remove the context identified by the <code>key</code> parameter.
*/
public void remove(String key) {
- Map<String, String> map = (Map<String, String>) inheritableThreadLocal.get();
+ Map<String, String> map = inheritableThreadLocalMap.get();
if (map != null) {
map.remove(key);
}
@@ -107,27 +105,21 @@ public class BasicMDCAdapter implements MDCAdapter {
* Clear all entries in the MDC.
*/
public void clear() {
- Map<String, String> map = (Map<String, String>) inheritableThreadLocal.get();
+ Map<String, String> map = inheritableThreadLocalMap.get();
if (map != null) {
map.clear();
- // the InheritableThreadLocal.remove method was introduced in JDK 1.5
- // Thus, invoking clear() on previous JDK 1.4 will fail
- if (isJDK14()) {
- inheritableThreadLocal.set(null);
- } else {
- inheritableThreadLocal.remove();
- }
+ inheritableThreadLocalMap.remove();
}
}
/**
* Returns the keys in the MDC as a {@link Set} of {@link String}s The
* returned value can be null.
- *
+ *
* @return the keys in the MDC
*/
public Set<String> getKeys() {
- Map<String, String> map = (Map<String, String>) inheritableThreadLocal.get();
+ Map<String, String> map = inheritableThreadLocalMap.get();
if (map != null) {
return map.keySet();
} else {
@@ -136,26 +128,43 @@ public class BasicMDCAdapter implements MDCAdapter {
}
/**
- * Return a copy of the current thread's context map.
+ * Return a copy of the current thread's context map.
* Returned value may be null.
- *
+ *
*/
public Map<String, String> getCopyOfContextMap() {
- Map<String, String> oldMap = (Map<String, String>) inheritableThreadLocal.get();
+ Map<String, String> oldMap = inheritableThreadLocalMap.get();
if (oldMap != null) {
- Map<String, String> newMap = Collections.<String, String> synchronizedMap(new HashMap<String, String>());
- synchronized (oldMap) {
- newMap.putAll(oldMap);
- }
- return newMap;
+ return new HashMap<>(oldMap);
} else {
return null;
}
}
public void setContextMap(Map<String, String> contextMap) {
- Map<String, String> map = Collections.<String, String> synchronizedMap(new HashMap<String, String>(contextMap));
- inheritableThreadLocal.set(map);
+ Map<String, String> copy = null;
+ if (contextMap != null) {
+ copy = new HashMap<>(contextMap);
+ }
+ inheritableThreadLocalMap.set(copy);
+ }
+
+ @Override
+ public void pushByKey(String key, String value) {
+ threadLocalMapOfDeques.pushByKey(key, value);
}
+ @Override
+ public String popByKey(String key) {
+ return threadLocalMapOfDeques.popByKey(key);
+ }
+
+ @Override
+ public Deque<String> getCopyOfDequeByKey(String key) {
+ return threadLocalMapOfDeques.getCopyOfDequeByKey(key);
+ }
+ @Override
+ public void clearDequeByKey(String key) {
+ threadLocalMapOfDeques.clearDequeByKey(key);
+ }
}
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/BasicMarker.java b/slf4j-api/src/main/java/org/slf4j/helpers/BasicMarker.java
index b296d5d2..9e3a6aee 100755
--- a/slf4j-api/src/main/java/org/slf4j/helpers/BasicMarker.java
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/BasicMarker.java
@@ -24,10 +24,9 @@
*/
package org.slf4j.helpers;
-import java.util.Collections;
import java.util.Iterator;
import java.util.List;
-import java.util.Vector;
+import java.util.concurrent.CopyOnWriteArrayList;
import org.slf4j.Marker;
@@ -39,10 +38,9 @@ import org.slf4j.Marker;
*/
public class BasicMarker implements Marker {
- private static final long serialVersionUID = 1803952589649545191L;
-
+ private static final long serialVersionUID = -2849567615646933777L;
private final String name;
- private List<Marker> referenceList;
+ private final List<Marker> referenceList = new CopyOnWriteArrayList<>();
BasicMarker(String name) {
if (name == null) {
@@ -55,7 +53,7 @@ public class BasicMarker implements Marker {
return name;
}
- public synchronized void add(Marker reference) {
+ public void add(Marker reference) {
if (reference == null) {
throw new IllegalArgumentException("A null value cannot be added to a Marker as reference.");
}
@@ -65,49 +63,28 @@ public class BasicMarker implements Marker {
return;
} else if (reference.contains(this)) { // avoid recursion
- // a potential reference should not its future "parent" as a reference
+ // a potential reference should not hold its future "parent" as a reference
return;
} else {
- // let's add the reference
- if (referenceList == null) {
- referenceList = new Vector<Marker>();
- }
referenceList.add(reference);
}
-
}
- public synchronized boolean hasReferences() {
- return ((referenceList != null) && (referenceList.size() > 0));
+ public boolean hasReferences() {
+ return (referenceList.size() > 0);
}
+ @Deprecated
public boolean hasChildren() {
return hasReferences();
}
- public synchronized Iterator<Marker> iterator() {
- if (referenceList != null) {
- return referenceList.iterator();
- } else {
- List<Marker> emptyList = Collections.emptyList();
- return emptyList.iterator();
- }
+ public Iterator<Marker> iterator() {
+ return referenceList.iterator();
}
- public synchronized boolean remove(Marker referenceToRemove) {
- if (referenceList == null) {
- return false;
- }
-
- int size = referenceList.size();
- for (int i = 0; i < size; i++) {
- Marker m = (Marker) referenceList.get(i);
- if (referenceToRemove.equals(m)) {
- referenceList.remove(i);
- return true;
- }
- }
- return false;
+ public boolean remove(Marker referenceToRemove) {
+ return referenceList.remove(referenceToRemove);
}
public boolean contains(Marker other) {
@@ -120,8 +97,7 @@ public class BasicMarker implements Marker {
}
if (hasReferences()) {
- for (int i = 0; i < referenceList.size(); i++) {
- Marker ref = (Marker) referenceList.get(i);
+ for (Marker ref : referenceList) {
if (ref.contains(other)) {
return true;
}
@@ -143,8 +119,7 @@ public class BasicMarker implements Marker {
}
if (hasReferences()) {
- for (int i = 0; i < referenceList.size(); i++) {
- Marker ref = (Marker) referenceList.get(i);
+ for (Marker ref : referenceList) {
if (ref.contains(name)) {
return true;
}
@@ -153,9 +128,9 @@ public class BasicMarker implements Marker {
return false;
}
- private static String OPEN = "[ ";
- private static String CLOSE = " ]";
- private static String SEP = ", ";
+ private static final String OPEN = "[ ";
+ private static final String CLOSE = " ]";
+ private static final String SEP = ", ";
public boolean equals(Object obj) {
if (this == obj)
@@ -182,7 +157,7 @@ public class BasicMarker implements Marker {
StringBuilder sb = new StringBuilder(this.getName());
sb.append(' ').append(OPEN);
while (it.hasNext()) {
- reference = (Marker) it.next();
+ reference = it.next();
sb.append(reference.getName());
if (it.hasNext()) {
sb.append(SEP);
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/BasicMarkerFactory.java b/slf4j-api/src/main/java/org/slf4j/helpers/BasicMarkerFactory.java
index 5139bb66..06b0ec58 100644
--- a/slf4j-api/src/main/java/org/slf4j/helpers/BasicMarkerFactory.java
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/BasicMarkerFactory.java
@@ -41,7 +41,7 @@ import org.slf4j.Marker;
*/
public class BasicMarkerFactory implements IMarkerFactory {
- private final ConcurrentMap<String, Marker> markerMap = new ConcurrentHashMap<String, Marker>();
+ private final ConcurrentMap<String, Marker> markerMap = new ConcurrentHashMap<>();
/**
* Regular users should <em>not</em> create
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/CheckReturnValue.java b/slf4j-api/src/main/java/org/slf4j/helpers/CheckReturnValue.java
new file mode 100644
index 00000000..e1c9803a
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/CheckReturnValue.java
@@ -0,0 +1,30 @@
+package org.slf4j.helpers;
+
+
+import org.slf4j.Marker;
+
+import java.lang.annotation.*;
+
+/**
+ * <p>Used to annotate methods in the {@link org.slf4j.spi.LoggingEventBuilder} interface
+ * which return an instance of LoggingEventBuilder (usually as <code>this</code>). Such
+ * methods should be followed by one of the terminating <code>log()</code> methods returning
+ * <code>void</code>.</p>
+ * <p></p>
+ * <p>IntelliJ IDEA supports a check for annotations named as <code>CheckReturnValue</code>
+ * regardless of the containing package. For more information on this feature in IntelliJ IDEA,
+ * select File &#8594; Setting &#8594; Editor &#8594; Inspections and then Java &#8594; Probable Bugs &#8594;
+ * Result of method call ignored. </p>
+ * <p></p>
+ *
+ * <p>As for Eclipse, this feature has been requested in
+ * <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=572496">bug 572496</a></p>
+ *
+ * @author Ceki G&uuml;lc&uuml;
+ * @since 2.0.0-beta1
+ */
+@Documented
+@Target( { ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface CheckReturnValue {
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/FormattingTuple.java b/slf4j-api/src/main/java/org/slf4j/helpers/FormattingTuple.java
index 8d49ba45..a416e1d4 100644
--- a/slf4j-api/src/main/java/org/slf4j/helpers/FormattingTuple.java
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/FormattingTuple.java
@@ -33,9 +33,9 @@ public class FormattingTuple {
static public FormattingTuple NULL = new FormattingTuple(null);
- private String message;
- private Throwable throwable;
- private Object[] argArray;
+ private final String message;
+ private final Throwable throwable;
+ private final Object[] argArray;
public FormattingTuple(String message) {
this(message, null, null);
@@ -44,21 +44,7 @@ public class FormattingTuple {
public FormattingTuple(String message, Object[] argArray, Throwable throwable) {
this.message = message;
this.throwable = throwable;
- if (throwable == null) {
- this.argArray = argArray;
- } else {
- this.argArray = trimmedCopy(argArray);
- }
- }
-
- static Object[] trimmedCopy(Object[] argArray) {
- if (argArray == null || argArray.length == 0) {
- throw new IllegalStateException("non-sensical empty or null argument array");
- }
- final int trimemdLen = argArray.length - 1;
- Object[] trimmed = new Object[trimemdLen];
- System.arraycopy(argArray, 0, trimmed, 0, trimemdLen);
- return trimmed;
+ this.argArray = argArray;
}
public String getMessage() {
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/LegacyAbstractLogger.java b/slf4j-api/src/main/java/org/slf4j/helpers/LegacyAbstractLogger.java
new file mode 100644
index 00000000..dcc90009
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/LegacyAbstractLogger.java
@@ -0,0 +1,39 @@
+package org.slf4j.helpers;
+
+import org.slf4j.Marker;
+
+/**
+ * Provides minimal default implementations for {@link #isTraceEnabled(Marker)}, {@link #isDebugEnabled(Marker)} and other similar methods.
+ *
+ * @since 2.0
+ */
+abstract public class LegacyAbstractLogger extends AbstractLogger {
+
+ private static final long serialVersionUID = -7041884104854048950L;
+
+ @Override
+ public boolean isTraceEnabled(Marker marker) {
+ return isTraceEnabled();
+ }
+
+ @Override
+ public boolean isDebugEnabled(Marker marker) {
+ return isDebugEnabled();
+ }
+
+ @Override
+ public boolean isInfoEnabled(Marker marker) {
+ return isInfoEnabled();
+ }
+
+ @Override
+ public boolean isWarnEnabled(Marker marker) {
+ return isWarnEnabled();
+ }
+
+ @Override
+ public boolean isErrorEnabled(Marker marker) {
+ return isErrorEnabled();
+ }
+
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/MarkerIgnoringBase.java b/slf4j-api/src/main/java/org/slf4j/helpers/MarkerIgnoringBase.java
index dd765384..715cec75 100644
--- a/slf4j-api/src/main/java/org/slf4j/helpers/MarkerIgnoringBase.java
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/MarkerIgnoringBase.java
@@ -34,6 +34,7 @@ import org.slf4j.Marker;
* any marker data passed as argument.
*
* @author Ceki Gulcu
+ * @deprecated
*/
public abstract class MarkerIgnoringBase extends NamedLoggerBase implements Logger {
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/MessageFormatter.java b/slf4j-api/src/main/java/org/slf4j/helpers/MessageFormatter.java
index cbecbfc6..47f95daa 100755
--- a/slf4j-api/src/main/java/org/slf4j/helpers/MessageFormatter.java
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/MessageFormatter.java
@@ -33,14 +33,14 @@ import java.util.Map;
/**
* Formats messages according to very simple substitution rules. Substitutions
* can be made 1, 2 or more arguments.
- *
+ *
* <p>
* For example,
- *
+ *
* <pre>
* MessageFormatter.format(&quot;Hi {}.&quot;, &quot;there&quot;)
* </pre>
- *
+ *
* will return the string "Hi there.".
* <p>
* The {} pair is called the <em>formatting anchor</em>. It serves to designate
@@ -50,48 +50,48 @@ import java.util.Map;
* In case your message contains the '{' or the '}' character, you do not have
* to do anything special unless the '}' character immediately follows '{'. For
* example,
- *
+ *
* <pre>
* MessageFormatter.format(&quot;Set {1,2,3} is not equal to {}.&quot;, &quot;1,2&quot;);
* </pre>
- *
+ *
* will return the string "Set {1,2,3} is not equal to 1,2.".
- *
+ *
* <p>
* If for whatever reason you need to place the string "{}" in the message
* without its <em>formatting anchor</em> meaning, then you need to escape the
* '{' character with '\', that is the backslash character. Only the '{'
* character should be escaped. There is no need to escape the '}' character.
* For example,
- *
+ *
* <pre>
* MessageFormatter.format(&quot;Set \\{} is not equal to {}.&quot;, &quot;1,2&quot;);
* </pre>
- *
+ *
* will return the string "Set {} is not equal to 1,2.".
- *
+ *
* <p>
* The escaping behavior just described can be overridden by escaping the escape
* character '\'. Calling
- *
+ *
* <pre>
* MessageFormatter.format(&quot;File name is C:\\\\{}.&quot;, &quot;file.zip&quot;);
* </pre>
- *
+ *
* will return the string "File name is C:\file.zip".
- *
+ *
* <p>
- * The formatting conventions are different than those of {@link MessageFormat}
+ * The formatting conventions are different from those of {@link MessageFormat}
* which ships with the Java platform. This is justified by the fact that
* SLF4J's implementation is 10 times faster than that of {@link MessageFormat}.
* This local performance difference is both measurable and significant in the
* larger context of the complete logging processing chain.
- *
+ *
* <p>
* See also {@link #format(String, Object)},
* {@link #format(String, Object, Object)} and
* {@link #arrayFormat(String, Object[])} methods for more details.
- *
+ *
* @author Ceki G&uuml;lc&uuml;
* @author Joern Huxhorn
*/
@@ -106,17 +106,17 @@ final public class MessageFormatter {
* parameter.
* <p>
* For example,
- *
+ *
* <pre>
* MessageFormatter.format(&quot;Hi {}.&quot;, &quot;there&quot;);
* </pre>
- *
+ *
* will return the string "Hi there.".
* <p>
- *
+ *
* @param messagePattern
* The message pattern which will be parsed and formatted
- * @param argument
+ * @param arg
* The argument to be substituted in place of the formatting anchor
* @return The formatted message
*/
@@ -125,18 +125,18 @@ final public class MessageFormatter {
}
/**
- *
+ *
* Performs a two argument substitution for the 'messagePattern' passed as
* parameter.
* <p>
* For example,
- *
+ *
* <pre>
* MessageFormatter.format(&quot;Hi {}. My name is {}.&quot;, &quot;Alice&quot;, &quot;Bob&quot;);
* </pre>
- *
+ *
* will return the string "Hi Alice. My name is Bob.".
- *
+ *
* @param messagePattern
* The message pattern which will be parsed and formatted
* @param arg1
@@ -151,36 +151,34 @@ final public class MessageFormatter {
return arrayFormat(messagePattern, new Object[] { arg1, arg2 });
}
- static final Throwable getThrowableCandidate(Object[] argArray) {
- if (argArray == null || argArray.length == 0) {
- return null;
- }
-
- final Object lastEntry = argArray[argArray.length - 1];
- if (lastEntry instanceof Throwable) {
- return (Throwable) lastEntry;
+ final public static FormattingTuple arrayFormat(final String messagePattern, final Object[] argArray) {
+ Throwable throwableCandidate = MessageFormatter.getThrowableCandidate(argArray);
+ Object[] args = argArray;
+ if (throwableCandidate != null) {
+ args = MessageFormatter.trimmedCopy(argArray);
}
- return null;
+ return arrayFormat(messagePattern, args, throwableCandidate);
}
/**
- * Same principle as the {@link #format(String, Object)} and
- * {@link #format(String, Object, Object)} methods except that any number of
- * arguments can be passed in an array.
+ * Assumes that argArray only contains arguments with no throwable as last element.
*
* @param messagePattern
- * The message pattern which will be parsed and formatted
* @param argArray
- * An array of arguments to be substituted in place of formatting
- * anchors
- * @return The formatted message
*/
- final public static FormattingTuple arrayFormat(final String messagePattern, final Object[] argArray) {
+ final public static String basicArrayFormat(final String messagePattern, final Object[] argArray) {
+ FormattingTuple ft = arrayFormat(messagePattern, argArray, null);
+ return ft.getMessage();
+ }
- Throwable throwableCandidate = getThrowableCandidate(argArray);
+ public static String basicArrayFormat(NormalizedParameters np) {
+ return basicArrayFormat(np.getMessage(), np.getArguments());
+ }
+
+ final public static FormattingTuple arrayFormat(final String messagePattern, final Object[] argArray, Throwable throwable) {
if (messagePattern == null) {
- return new FormattingTuple(null, argArray, throwableCandidate);
+ return new FormattingTuple(null, argArray, throwable);
}
if (argArray == null) {
@@ -200,42 +198,38 @@ final public class MessageFormatter {
if (j == -1) {
// no more variables
if (i == 0) { // this is a simple string
- return new FormattingTuple(messagePattern, argArray, throwableCandidate);
+ return new FormattingTuple(messagePattern, argArray, throwable);
} else { // add the tail string which contains no variables and return
// the result.
- sbuf.append(messagePattern.substring(i, messagePattern.length()));
- return new FormattingTuple(sbuf.toString(), argArray, throwableCandidate);
+ sbuf.append(messagePattern, i, messagePattern.length());
+ return new FormattingTuple(sbuf.toString(), argArray, throwable);
}
} else {
if (isEscapedDelimeter(messagePattern, j)) {
if (!isDoubleEscaped(messagePattern, j)) {
L--; // DELIM_START was escaped, thus should not be incremented
- sbuf.append(messagePattern.substring(i, j - 1));
+ sbuf.append(messagePattern, i, j - 1);
sbuf.append(DELIM_START);
i = j + 1;
} else {
// The escape character preceding the delimiter start is
// itself escaped: "abc x:\\{}"
// we have to consume one backward slash
- sbuf.append(messagePattern.substring(i, j - 1));
- deeplyAppendParameter(sbuf, argArray[L], new HashMap<Object[], Object>());
+ sbuf.append(messagePattern, i, j - 1);
+ deeplyAppendParameter(sbuf, argArray[L], new HashMap<>());
i = j + 2;
}
} else {
// normal case
- sbuf.append(messagePattern.substring(i, j));
- deeplyAppendParameter(sbuf, argArray[L], new HashMap<Object[], Object>());
+ sbuf.append(messagePattern, i, j);
+ deeplyAppendParameter(sbuf, argArray[L], new HashMap<>());
i = j + 2;
}
}
}
// append the characters following the last {} pair.
- sbuf.append(messagePattern.substring(i, messagePattern.length()));
- if (L < argArray.length - 1) {
- return new FormattingTuple(sbuf.toString(), argArray, throwableCandidate);
- } else {
- return new FormattingTuple(sbuf.toString(), argArray, null);
- }
+ sbuf.append(messagePattern, i, messagePattern.length());
+ return new FormattingTuple(sbuf.toString(), argArray, throwable);
}
final static boolean isEscapedDelimeter(String messagePattern, int delimeterStartIndex) {
@@ -297,8 +291,7 @@ final public class MessageFormatter {
String oAsString = o.toString();
sbuf.append(oAsString);
} catch (Throwable t) {
- System.err.println("SLF4J: Failed toString() invocation on an object of type [" + o.getClass().getName() + "]");
- t.printStackTrace();
+ Reporter.error("Failed toString() invocation on an object of type [" + o.getClass().getName() + "]", t);
sbuf.append("[FAILED toString()]");
}
@@ -409,4 +402,29 @@ final public class MessageFormatter {
}
sbuf.append(']');
}
+
+ /**
+ * Helper method to determine if an {@link Object} array contains a {@link Throwable} as last element
+ *
+ * @param argArray
+ * The arguments off which we want to know if it contains a {@link Throwable} as last element
+ * @return if the last {@link Object} in argArray is a {@link Throwable} this method will return it,
+ * otherwise it returns null
+ */
+ public static Throwable getThrowableCandidate(final Object[] argArray) {
+ return NormalizedParameters.getThrowableCandidate(argArray);
+ }
+
+ /**
+ * Helper method to get all but the last element of an array
+ *
+ * @param argArray
+ * The arguments from which we want to remove the last element
+ *
+ * @return a copy of the array without the last element
+ */
+ public static Object[] trimmedCopy(final Object[] argArray) {
+ return NormalizedParameters.trimmedCopy(argArray);
+ }
+
}
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/NOPLogger.java b/slf4j-api/src/main/java/org/slf4j/helpers/NOPLogger.java
index 5e0909d8..4b9a3597 100644
--- a/slf4j-api/src/main/java/org/slf4j/helpers/NOPLogger.java
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/NOPLogger.java
@@ -25,14 +25,14 @@
package org.slf4j.helpers;
import org.slf4j.Logger;
-import org.slf4j.helpers.MarkerIgnoringBase;
+import org.slf4j.Marker;
/**
* A direct NOP (no operation) implementation of {@link Logger}.
*
* @author Ceki G&uuml;lc&uuml;
*/
-public class NOPLogger extends MarkerIgnoringBase {
+public class NOPLogger extends NamedLoggerBase implements Logger {
private static final long serialVersionUID = -517220405410904473L;
@@ -42,8 +42,9 @@ public class NOPLogger extends MarkerIgnoringBase {
public static final NOPLogger NOP_LOGGER = new NOPLogger();
/**
- * There is no point in creating multiple instances of NOPLOgger,
- * except by derived classes, hence the protected access for the constructor.
+ * There is no point in creating multiple instances of NOPLogger.
+ *
+ * The present constructor should be "private" but we are leaving it as "protected" for compatibility.
*/
protected NOPLogger() {
}
@@ -51,6 +52,7 @@ public class NOPLogger extends MarkerIgnoringBase {
/**
* Always returns the string value "NOP".
*/
+ @Override
public String getName() {
return "NOP";
}
@@ -59,31 +61,37 @@ public class NOPLogger extends MarkerIgnoringBase {
* Always returns false.
* @return always false
*/
+ @Override
final public boolean isTraceEnabled() {
return false;
}
/** A NOP implementation. */
+ @Override
final public void trace(String msg) {
// NOP
}
/** A NOP implementation. */
+ @Override
final public void trace(String format, Object arg) {
// NOP
}
/** A NOP implementation. */
+ @Override
public final void trace(String format, Object arg1, Object arg2) {
// NOP
}
/** A NOP implementation. */
+ @Override
public final void trace(String format, Object... argArray) {
// NOP
}
/** A NOP implementation. */
+ @Override
final public void trace(String msg, Throwable t) {
// NOP
}
@@ -107,12 +115,12 @@ public class NOPLogger extends MarkerIgnoringBase {
}
/** A NOP implementation. */
- public final void debug(String format, Object arg1, Object arg2) {
+ final public void debug(String format, Object arg1, Object arg2) {
// NOP
}
/** A NOP implementation. */
- public final void debug(String format, Object... argArray) {
+ final public void debug(String format, Object... argArray) {
// NOP
}
@@ -146,7 +154,7 @@ public class NOPLogger extends MarkerIgnoringBase {
}
/** A NOP implementation. */
- public final void info(String format, Object... argArray) {
+ final public void info(String format, Object... argArray) {
// NOP
}
@@ -179,7 +187,7 @@ public class NOPLogger extends MarkerIgnoringBase {
}
/** A NOP implementation. */
- public final void warn(String format, Object... argArray) {
+ final public void warn(String format, Object... argArray) {
// NOP
}
@@ -209,7 +217,7 @@ public class NOPLogger extends MarkerIgnoringBase {
}
/** A NOP implementation. */
- public final void error(String format, Object... argArray) {
+ final public void error(String format, Object... argArray) {
// NOP
}
@@ -217,4 +225,203 @@ public class NOPLogger extends MarkerIgnoringBase {
final public void error(String msg, Throwable t) {
// NOP
}
+
+ // ============================================================
+ // Added NOP methods since MarkerIgnoringBase is now deprecated
+ // ============================================================
+ /**
+ * Always returns false.
+ * @return always false
+ */
+ final public boolean isTraceEnabled(Marker marker) {
+ // NOP
+ return false;
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void trace(Marker marker, String msg) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void trace(Marker marker, String format, Object arg) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void trace(Marker marker, String format, Object arg1, Object arg2) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void trace(Marker marker, String format, Object... argArray) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void trace(Marker marker, String msg, Throwable t) {
+ // NOP
+ }
+
+ /**
+ * Always returns false.
+ * @return always false
+ */
+ final public boolean isDebugEnabled(Marker marker) {
+ return false;
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void debug(Marker marker, String msg) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void debug(Marker marker, String format, Object arg) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void debug(Marker marker, String format, Object arg1, Object arg2) {
+ // NOP
+ }
+
+ @Override
+ final public void debug(Marker marker, String format, Object... arguments) {
+ // NOP
+ }
+
+ @Override
+ final public void debug(Marker marker, String msg, Throwable t) {
+ // NOP
+ }
+
+ /**
+ * Always returns false.
+ * @return always false
+ */
+ @Override
+ public boolean isInfoEnabled(Marker marker) {
+ return false;
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void info(Marker marker, String msg) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void info(Marker marker, String format, Object arg) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void info(Marker marker, String format, Object arg1, Object arg2) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void info(Marker marker, String format, Object... arguments) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void info(Marker marker, String msg, Throwable t) {
+ // NOP
+ }
+
+ /**
+ * Always returns false.
+ * @return always false
+ */
+ @Override
+ final public boolean isWarnEnabled(Marker marker) {
+ return false;
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void warn(Marker marker, String msg) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void warn(Marker marker, String format, Object arg) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void warn(Marker marker, String format, Object arg1, Object arg2) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void warn(Marker marker, String format, Object... arguments) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void warn(Marker marker, String msg, Throwable t) {
+ // NOP
+ }
+
+ /**
+ * Always returns false.
+ * @return always false
+ */
+ @Override
+ final public boolean isErrorEnabled(Marker marker) {
+ return false;
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void error(Marker marker, String msg) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void error(Marker marker, String format, Object arg) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void error(Marker marker, String format, Object arg1, Object arg2) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void error(Marker marker, String format, Object... arguments) {
+ // NOP
+ }
+
+ /** A NOP implementation. */
+ @Override
+ final public void error(Marker marker, String msg, Throwable t) {
+ // NOP
+ }
+ // ===================================================================
+ // End of added NOP methods since MarkerIgnoringBase is now deprecated
+ // ===================================================================
+
}
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/NOPLoggerFactory.java b/slf4j-api/src/main/java/org/slf4j/helpers/NOPLoggerFactory.java
index b8a55dc9..7cc83914 100644
--- a/slf4j-api/src/main/java/org/slf4j/helpers/NOPLoggerFactory.java
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/NOPLoggerFactory.java
@@ -26,10 +26,9 @@ package org.slf4j.helpers;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
-import org.slf4j.helpers.NOPLogger;
/**
- * NOPLoggerFactory is an trivial implementation of {@link
+ * NOPLoggerFactory is a trivial implementation of {@link
* ILoggerFactory} which always returns the unique instance of
* NOPLogger.
*
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/NOPMDCAdapter.java b/slf4j-api/src/main/java/org/slf4j/helpers/NOPMDCAdapter.java
index 22f3ea45..7c34fca0 100644
--- a/slf4j-api/src/main/java/org/slf4j/helpers/NOPMDCAdapter.java
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/NOPMDCAdapter.java
@@ -24,6 +24,7 @@
*/
package org.slf4j.helpers;
+import java.util.Deque;
import java.util.Map;
import org.slf4j.spi.MDCAdapter;
@@ -60,4 +61,21 @@ public class NOPMDCAdapter implements MDCAdapter {
// NOP
}
+ @Override
+ public void pushByKey(String key, String value) {
+ }
+
+ @Override
+ public String popByKey(String key) {
+ return null;
+ }
+
+ @Override
+ public Deque<String> getCopyOfDequeByKey(String key) {
+ return null;
+ }
+
+ public void clearDequeByKey(String key) {
+ }
+
}
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/NOP_FallbackServiceProvider.java b/slf4j-api/src/main/java/org/slf4j/helpers/NOP_FallbackServiceProvider.java
new file mode 100755
index 00000000..87d287a6
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/NOP_FallbackServiceProvider.java
@@ -0,0 +1,47 @@
+package org.slf4j.helpers;
+
+import org.slf4j.ILoggerFactory;
+import org.slf4j.IMarkerFactory;
+import org.slf4j.spi.MDCAdapter;
+import org.slf4j.spi.SLF4JServiceProvider;
+
+public class NOP_FallbackServiceProvider implements SLF4JServiceProvider {
+
+ /**
+ * Declare the version of the SLF4J API this implementation is compiled
+ * against. The value of this field is modified with each major release.
+ */
+ // to avoid constant folding by the compiler, this field must *not* be final
+ public static String REQUESTED_API_VERSION = "2.0.99"; // !final
+
+ private final ILoggerFactory loggerFactory = new NOPLoggerFactory();
+ private final IMarkerFactory markerFactory = new BasicMarkerFactory();
+ private final MDCAdapter mdcAdapter = new NOPMDCAdapter();
+
+
+ @Override
+ public ILoggerFactory getLoggerFactory() {
+ return loggerFactory;
+ }
+
+ @Override
+ public IMarkerFactory getMarkerFactory() {
+ return markerFactory;
+ }
+
+
+ @Override
+ public MDCAdapter getMDCAdapter() {
+ return mdcAdapter;
+ }
+
+ @Override
+ public String getRequestedApiVersion() {
+ return REQUESTED_API_VERSION;
+ }
+
+ @Override
+ public void initialize() {
+ // already initialized
+ }
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/NamedLoggerBase.java b/slf4j-api/src/main/java/org/slf4j/helpers/NamedLoggerBase.java
index 184c3942..97b31837 100644
--- a/slf4j-api/src/main/java/org/slf4j/helpers/NamedLoggerBase.java
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/NamedLoggerBase.java
@@ -32,9 +32,10 @@ import org.slf4j.LoggerFactory;
/**
* Serves as base class for named logger implementation. More significantly, this
- * class establishes deserialization behavior. See @see #readResolve.
+ * class establishes deserialization behavior.
*
* @author Ceki Gulcu
+ * @see #readResolve
* @since 1.5.3
*/
abstract class NamedLoggerBase implements Logger, Serializable {
@@ -54,7 +55,7 @@ abstract class NamedLoggerBase implements Logger, Serializable {
*
* <p>
* This approach will work well if the desired ILoggerFactory is the one
- * references by LoggerFactory. However, if the user manages its logger hierarchy
+ * referenced by LoggerFactory. However, if the user manages its logger hierarchy
* through a different (non-static) mechanism, e.g. dependency injection, then
* this approach would be mostly counterproductive.
*
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/NormalizedParameters.java b/slf4j-api/src/main/java/org/slf4j/helpers/NormalizedParameters.java
new file mode 100644
index 00000000..ec278f46
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/NormalizedParameters.java
@@ -0,0 +1,116 @@
+package org.slf4j.helpers;
+
+import org.slf4j.event.LoggingEvent;
+
+/**
+ * Holds normalized call parameters.
+ *
+ * Includes utility methods such as {@link #normalize(String, Object[], Throwable)} to help the normalization of parameters.
+ *
+ * @author ceki
+ * @since 2.0
+ */
+public class NormalizedParameters {
+
+ final String message;
+ final Object[] arguments;
+ final Throwable throwable;
+
+ public NormalizedParameters(String message, Object[] arguments, Throwable throwable) {
+ this.message = message;
+ this.arguments = arguments;
+ this.throwable = throwable;
+ }
+
+ public NormalizedParameters(String message, Object[] arguments) {
+ this(message, arguments, null);
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public Object[] getArguments() {
+ return arguments;
+ }
+
+ public Throwable getThrowable() {
+ return throwable;
+ }
+
+ /**
+ * Helper method to determine if an {@link Object} array contains a
+ * {@link Throwable} as last element
+ *
+ * @param argArray The arguments off which we want to know if it contains a
+ * {@link Throwable} as last element
+ * @return if the last {@link Object} in argArray is a {@link Throwable} this
+ * method will return it, otherwise it returns null
+ */
+ public static Throwable getThrowableCandidate(final Object[] argArray) {
+ if (argArray == null || argArray.length == 0) {
+ return null;
+ }
+
+ final Object lastEntry = argArray[argArray.length - 1];
+ if (lastEntry instanceof Throwable) {
+ return (Throwable) lastEntry;
+ }
+
+ return null;
+ }
+
+ /**
+ * Helper method to get all but the last element of an array
+ *
+ * @param argArray The arguments from which we want to remove the last element
+ *
+ * @return a copy of the array without the last element
+ */
+ public static Object[] trimmedCopy(final Object[] argArray) {
+ if (argArray == null || argArray.length == 0) {
+ throw new IllegalStateException("non-sensical empty or null argument array");
+ }
+
+ final int trimmedLen = argArray.length - 1;
+
+ Object[] trimmed = new Object[trimmedLen];
+
+ if (trimmedLen > 0) {
+ System.arraycopy(argArray, 0, trimmed, 0, trimmedLen);
+ }
+
+ return trimmed;
+ }
+
+ /**
+ * This method serves to normalize logging call invocation parameters.
+ *
+ * More specifically, if a throwable argument is not supplied directly, it
+ * attempts to extract it from the argument array.
+ */
+ public static NormalizedParameters normalize(String msg, Object[] arguments, Throwable t) {
+
+ if (t != null) {
+ return new NormalizedParameters(msg, arguments, t);
+ }
+
+ if (arguments == null || arguments.length == 0) {
+ return new NormalizedParameters(msg, arguments, t);
+ }
+
+ Throwable throwableCandidate = NormalizedParameters.getThrowableCandidate(arguments);
+ if (throwableCandidate != null) {
+ Object[] trimmedArguments = MessageFormatter.trimmedCopy(arguments);
+ return new NormalizedParameters(msg, trimmedArguments, throwableCandidate);
+ } else {
+ return new NormalizedParameters(msg, arguments);
+ }
+
+ }
+
+ public static NormalizedParameters normalize(LoggingEvent event) {
+ return normalize(event.getMessage(), event.getArgumentArray(), event.getThrowable());
+ }
+
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/Reporter.java b/slf4j-api/src/main/java/org/slf4j/helpers/Reporter.java
new file mode 100644
index 00000000..2f269969
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/Reporter.java
@@ -0,0 +1,181 @@
+package org.slf4j.helpers;
+
+import java.io.PrintStream;
+
+/**
+ * An internally used class for reporting internal messages generated by SLF4J itself during initialization.
+ *
+ * <p>
+ * Internal reporting is performed by calling the {@link #info(String)}, {@link #warn(String)} (String)}
+ * {@link #error(String)} (String)} and {@link #error(String, Throwable)} methods.
+ * </p>
+ * <p>See {@link #SLF4J_INTERNAL_VERBOSITY_KEY} and {@link #SLF4J_INTERNAL_REPORT_STREAM_KEY} for
+ * configuration options.</p>
+ * <p>
+ * <p>
+ * Note that this system is independent of the logging back-end in use.
+ *
+ * @since 2.0.10
+ */
+public class Reporter {
+
+ /**
+ * this class is used internally by Reporter
+ */
+ private enum Level {
+ INFO(1), WARN(2), ERROR(3);
+
+ int levelInt;
+
+ private Level(int levelInt) {
+ this.levelInt = levelInt;
+ }
+
+ private int getLevelInt() {
+ return levelInt;
+ }
+ }
+
+ private enum TargetChoice {
+ Stderr, Stdout;
+ }
+
+ static final String SLF4J_INFO_PREFIX = "SLF4J(I): ";
+ static final String SLF4J_WARN_PREFIX = "SLF4J(W): ";
+ static final String SLF4J_ERROR_PREFIX = "SLF4J(E): ";
+
+
+ /**
+ * This system property controls the target for internal reports output by SLF4J.
+ * Recognized values for this key are "System.out", "stdout", "sysout", "System.err",
+ * "stderr" and "syserr".
+ *
+ * <p>By default, output is directed to "stderr".</p>
+ */
+ public static final String SLF4J_INTERNAL_REPORT_STREAM_KEY = "slf4j.internal.report.stream";
+ static private final String[] SYSOUT_KEYS = {"System.out", "stdout", "sysout"};
+
+ /**
+ * This system property controls the internal level of chattiness
+ * of SLF4J. Recognized settings are "INFO", "WARN" and "ERROR". The default value is "INFO".
+ */
+ public static final String SLF4J_INTERNAL_VERBOSITY_KEY = "slf4j.internal.verbosity";
+
+
+ static private final TargetChoice TARGET_CHOICE = initTargetChoice();
+
+ static private final Level INTERNAL_VERBOSITY = initVerbosity();
+
+ static private TargetChoice initTargetChoice() {
+ String reportStreamStr = System.getProperty(SLF4J_INTERNAL_REPORT_STREAM_KEY);
+
+ if(reportStreamStr == null || reportStreamStr.isEmpty()) {
+ return TargetChoice.Stderr;
+ }
+
+ for(String s : SYSOUT_KEYS) {
+ if(s.equalsIgnoreCase(reportStreamStr))
+ return TargetChoice.Stdout;
+ }
+ return TargetChoice.Stderr;
+ }
+
+
+ static private Level initVerbosity() {
+ String verbosityStr = System.getProperty(SLF4J_INTERNAL_VERBOSITY_KEY);
+
+ if(verbosityStr == null || verbosityStr.isEmpty()) {
+ return Level.INFO;
+ }
+
+ if(verbosityStr.equalsIgnoreCase("ERROR")) {
+ return Level.ERROR;
+ }
+
+
+ if(verbosityStr.equalsIgnoreCase("WARN")) {
+ return Level.WARN;
+ }
+
+ return Level.INFO;
+ }
+
+ static boolean isEnabledFor(Level level) {
+ return (level.levelInt >= INTERNAL_VERBOSITY.levelInt);
+ }
+
+ static private PrintStream getTarget() {
+ switch(TARGET_CHOICE) {
+ case Stdout:
+ return System.out;
+ case Stderr:
+ default:
+ return System.err;
+ }
+ }
+
+ /**
+ * Report an internal message of level INFO. Message text is prefixed with the string "SLF4J(I)", with
+ * (I) standing as a shorthand for INFO.
+ *
+ * <p>Messages of level INFO are be enabled when the {@link #SLF4J_INTERNAL_VERBOSITY_KEY} system property is
+ * set to "INFO" and disabled when set to "WARN" or "ERROR". By default, {@link #SLF4J_INTERNAL_VERBOSITY_KEY} is
+ * set to "INFO".</p>
+ *
+ * @param msg the message text
+ */
+ public static void info(String msg) {
+ if(isEnabledFor(Level.INFO)) {
+ getTarget().println(SLF4J_INFO_PREFIX + msg);
+ }
+ }
+
+
+ /**
+ * Report an internal message of level "WARN". Message text is prefixed with the string "SLF4J(W)", with
+ * (W) standing as a shorthand for WARN.
+ *
+ * <p>Messages of level WARN are be enabled when the {@link #SLF4J_INTERNAL_VERBOSITY_KEY} system property is
+ * set to "INFO" or "WARN" and disabled when set to "ERROR". By default, {@link #SLF4J_INTERNAL_VERBOSITY_KEY} is
+ * set to "INFO".</p>
+ *
+ * @param msg the message text
+ */
+ static final public void warn(String msg) {
+ if(isEnabledFor(Level.WARN)) {
+ getTarget().println(SLF4J_WARN_PREFIX + msg);
+ }
+ }
+
+
+ /**
+ * Report an internal message of level "ERROR accompanied by a {@link Throwable}.
+ * Message text is prefixed with the string "SLF4J(E)", with (E) standing as a shorthand for ERROR.
+ *
+ * <p>Messages of level ERROR are always enabled.
+ *
+ * @param msg the message text
+ * @param t a Throwable
+ */
+ static final public void error(String msg, Throwable t) {
+ // error cannot be disabled
+ getTarget().println(SLF4J_ERROR_PREFIX + msg);
+ getTarget().println(SLF4J_ERROR_PREFIX + "Reported exception:");
+ t.printStackTrace(getTarget());
+ }
+
+
+ /**
+ * Report an internal message of level "ERROR". Message text is prefixed with the string "SLF4J(E)", with
+ * (E) standing as a shorthand for ERROR.
+ *
+ * <p>Messages of level ERROR are always enabled.
+ *
+ * @param msg the message text
+ */
+
+ static final public void error(String msg) {
+ // error cannot be disabled
+ getTarget().println(SLF4J_ERROR_PREFIX + msg);
+ }
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteLogger.java b/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteLogger.java
index 1c37977e..b9e160d7 100644
--- a/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteLogger.java
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteLogger.java
@@ -24,196 +24,286 @@
*/
package org.slf4j.helpers;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Queue;
+
import org.slf4j.Logger;
import org.slf4j.Marker;
+import org.slf4j.event.EventRecordingLogger;
+import org.slf4j.event.Level;
+import org.slf4j.event.LoggingEvent;
+import org.slf4j.event.SubstituteLoggingEvent;
+import org.slf4j.spi.LoggingEventBuilder;
/**
* A logger implementation which logs via a delegate logger. By default, the delegate is a
- * {@link NOPLogger}. However, a different delegate can be set at anytime.
- * <p/>
- * See also the <a href="http://www.slf4j.org/codes.html#substituteLogger">relevant
+ * {@link NOPLogger}. However, a different delegate can be set at any time.
+ *
+ * <p>See also the <a href="http://www.slf4j.org/codes.html#substituteLogger">relevant
* error code</a> documentation.
*
* @author Chetan Mehrotra
+ * @author Ceki Gulcu
*/
public class SubstituteLogger implements Logger {
private final String name;
-
private volatile Logger _delegate;
+ private Boolean delegateEventAware;
+ private Method logMethodCache;
+ private EventRecordingLogger eventRecordingLogger;
+ private final Queue<SubstituteLoggingEvent> eventQueue;
+
+ public final boolean createdPostInitialization;
- public SubstituteLogger(String name) {
+ public SubstituteLogger(String name, Queue<SubstituteLoggingEvent> eventQueue, boolean createdPostInitialization) {
this.name = name;
+ this.eventQueue = eventQueue;
+ this.createdPostInitialization = createdPostInitialization;
}
+ @Override
public String getName() {
return name;
}
+
+ @Override
+ public LoggingEventBuilder makeLoggingEventBuilder(Level level) {
+ return delegate().makeLoggingEventBuilder(level);
+ }
+ @Override
+ public LoggingEventBuilder atLevel(Level level) {
+ return delegate().atLevel(level);
+ }
+
+ @Override
+ public boolean isEnabledForLevel(Level level) {
+ return delegate().isEnabledForLevel(level);
+ }
+
+ @Override
public boolean isTraceEnabled() {
return delegate().isTraceEnabled();
}
-
+
+ @Override
public void trace(String msg) {
delegate().trace(msg);
}
-
+
+ @Override
public void trace(String format, Object arg) {
delegate().trace(format, arg);
}
-
+
+ @Override
public void trace(String format, Object arg1, Object arg2) {
delegate().trace(format, arg1, arg2);
}
-
+
+ @Override
public void trace(String format, Object... arguments) {
delegate().trace(format, arguments);
- }
-
+ }
+
+ @Override
public void trace(String msg, Throwable t) {
delegate().trace(msg, t);
}
-
+
+ @Override
public boolean isTraceEnabled(Marker marker) {
return delegate().isTraceEnabled(marker);
}
-
+
+ @Override
public void trace(Marker marker, String msg) {
delegate().trace(marker, msg);
}
-
+
+ @Override
public void trace(Marker marker, String format, Object arg) {
delegate().trace(marker, format, arg);
}
-
+
+ @Override
public void trace(Marker marker, String format, Object arg1, Object arg2) {
delegate().trace(marker, format, arg1, arg2);
}
-
+ @Override
public void trace(Marker marker, String format, Object... arguments) {
delegate().trace(marker, format, arguments);
}
-
+ @Override
public void trace(Marker marker, String msg, Throwable t) {
delegate().trace(marker, msg, t);
}
-
+
+ @Override
+ public LoggingEventBuilder atTrace() {
+ return delegate().atTrace();
+ }
+
+ @Override
public boolean isDebugEnabled() {
return delegate().isDebugEnabled();
}
-
+
+ @Override
public void debug(String msg) {
delegate().debug(msg);
}
-
+
+ @Override
public void debug(String format, Object arg) {
delegate().debug(format, arg);
}
-
+
+ @Override
public void debug(String format, Object arg1, Object arg2) {
delegate().debug(format, arg1, arg2);
}
-
+
+ @Override
public void debug(String format, Object... arguments) {
delegate().debug(format, arguments);
}
-
+
+ @Override
public void debug(String msg, Throwable t) {
delegate().debug(msg, t);
}
-
+
+ @Override
public boolean isDebugEnabled(Marker marker) {
return delegate().isDebugEnabled(marker);
}
-
+
+ @Override
public void debug(Marker marker, String msg) {
delegate().debug(marker, msg);
}
-
+
+ @Override
public void debug(Marker marker, String format, Object arg) {
delegate().debug(marker, format, arg);
}
-
+
+ @Override
public void debug(Marker marker, String format, Object arg1, Object arg2) {
delegate().debug(marker, format, arg1, arg2);
}
-
+
+ @Override
public void debug(Marker marker, String format, Object... arguments) {
delegate().debug(marker, format, arguments);
}
-
+
+ @Override
public void debug(Marker marker, String msg, Throwable t) {
delegate().debug(marker, msg, t);
}
-
+
+ @Override
+ public LoggingEventBuilder atDebug() {
+ return delegate().atDebug();
+ }
+
+ @Override
public boolean isInfoEnabled() {
return delegate().isInfoEnabled();
}
+
+ @Override
public void info(String msg) {
delegate().info(msg);
}
-
+
+ @Override
public void info(String format, Object arg) {
delegate().info(format, arg);
}
-
+
+ @Override
public void info(String format, Object arg1, Object arg2) {
delegate().info(format, arg1, arg2);
}
-
+
+ @Override
public void info(String format, Object... arguments) {
delegate().info(format, arguments);
}
-
+
+ @Override
public void info(String msg, Throwable t) {
delegate().info(msg, t);
}
-
+
+ @Override
public boolean isInfoEnabled(Marker marker) {
return delegate().isInfoEnabled(marker);
}
-
+
+ @Override
public void info(Marker marker, String msg) {
delegate().info(marker, msg);
}
-
+
+ @Override
public void info(Marker marker, String format, Object arg) {
delegate().info(marker, format, arg);
}
-
+
+ @Override
public void info(Marker marker, String format, Object arg1, Object arg2) {
delegate().info(marker, format, arg1, arg2);
}
-
+
+ @Override
public void info(Marker marker, String format, Object... arguments) {
delegate().info(marker, format, arguments);
}
-
+
+ @Override
public void info(Marker marker, String msg, Throwable t) {
delegate().info(marker, msg, t);
}
+
+ @Override
+ public LoggingEventBuilder atInfo() {
+ return delegate().atInfo();
+ }
+
+ @Override
public boolean isWarnEnabled() {
return delegate().isWarnEnabled();
}
-
+
+ @Override
public void warn(String msg) {
delegate().warn(msg);
}
-
+
+ @Override
public void warn(String format, Object arg) {
delegate().warn(format, arg);
}
-
+
+ @Override
public void warn(String format, Object arg1, Object arg2) {
delegate().warn(format, arg1, arg2);
}
-
+
+ @Override
public void warn(String format, Object... arguments) {
delegate().warn(format, arguments);
}
-
+
+ @Override
public void warn(String msg, Throwable t) {
delegate().warn(msg, t);
}
@@ -221,76 +311,105 @@ public class SubstituteLogger implements Logger {
public boolean isWarnEnabled(Marker marker) {
return delegate().isWarnEnabled(marker);
}
-
+
+ @Override
public void warn(Marker marker, String msg) {
delegate().warn(marker, msg);
}
-
+
+ @Override
public void warn(Marker marker, String format, Object arg) {
delegate().warn(marker, format, arg);
}
-
+
+ @Override
public void warn(Marker marker, String format, Object arg1, Object arg2) {
delegate().warn(marker, format, arg1, arg2);
}
-
+
+ @Override
public void warn(Marker marker, String format, Object... arguments) {
delegate().warn(marker, format, arguments);
}
-
+
+ @Override
public void warn(Marker marker, String msg, Throwable t) {
delegate().warn(marker, msg, t);
}
+
+ @Override
+ public LoggingEventBuilder atWarn() {
+ return delegate().atWarn();
+ }
+
+
+ @Override
public boolean isErrorEnabled() {
return delegate().isErrorEnabled();
}
-
+
+ @Override
public void error(String msg) {
delegate().error(msg);
}
-
+
+ @Override
public void error(String format, Object arg) {
delegate().error(format, arg);
}
-
+
+ @Override
public void error(String format, Object arg1, Object arg2) {
delegate().error(format, arg1, arg2);
}
-
+
+ @Override
public void error(String format, Object... arguments) {
delegate().error(format, arguments);
}
-
+
+ @Override
public void error(String msg, Throwable t) {
delegate().error(msg, t);
}
-
+
+ @Override
public boolean isErrorEnabled(Marker marker) {
return delegate().isErrorEnabled(marker);
}
-
+
+ @Override
public void error(Marker marker, String msg) {
delegate().error(marker, msg);
}
-
+
+ @Override
public void error(Marker marker, String format, Object arg) {
delegate().error(marker, format, arg);
}
-
+
+ @Override
public void error(Marker marker, String format, Object arg1, Object arg2) {
delegate().error(marker, format, arg1, arg2);
}
-
+
+ @Override
public void error(Marker marker, String format, Object... arguments) {
delegate().error(marker, format, arguments);
}
-
+
+ @Override
public void error(Marker marker, String msg, Throwable t) {
delegate().error(marker, msg, t);
}
@Override
+ public LoggingEventBuilder atError() {
+ return delegate().atError();
+ }
+
+ @Override
public boolean equals(Object o) {
if (this == o)
return true;
@@ -314,8 +433,22 @@ public class SubstituteLogger implements Logger {
* Return the delegate logger instance if set. Otherwise, return a {@link NOPLogger}
* instance.
*/
- Logger delegate() {
- return _delegate != null ? _delegate : NOPLogger.NOP_LOGGER;
+ public Logger delegate() {
+ if (_delegate != null) {
+ return _delegate;
+ }
+ if (createdPostInitialization) {
+ return NOPLogger.NOP_LOGGER;
+ } else {
+ return getEventRecordingLogger();
+ }
+ }
+
+ private Logger getEventRecordingLogger() {
+ if (eventRecordingLogger == null) {
+ eventRecordingLogger = new EventRecordingLogger(this, eventQueue);
+ }
+ return eventRecordingLogger;
}
/**
@@ -325,4 +458,36 @@ public class SubstituteLogger implements Logger {
public void setDelegate(Logger delegate) {
this._delegate = delegate;
}
+
+ public boolean isDelegateEventAware() {
+ if (delegateEventAware != null)
+ return delegateEventAware;
+
+ try {
+ logMethodCache = _delegate.getClass().getMethod("log", LoggingEvent.class);
+ delegateEventAware = Boolean.TRUE;
+ } catch (NoSuchMethodException e) {
+ delegateEventAware = Boolean.FALSE;
+ }
+ return delegateEventAware;
+ }
+
+ public void log(LoggingEvent event) {
+ if (isDelegateEventAware()) {
+ try {
+ logMethodCache.invoke(_delegate, event);
+ } catch (IllegalAccessException e) {
+ } catch (IllegalArgumentException e) {
+ } catch (InvocationTargetException e) {
+ }
+ }
+ }
+
+ public boolean isDelegateNull() {
+ return _delegate == null;
+ }
+
+ public boolean isDelegateNOP() {
+ return _delegate instanceof NOPLogger;
+ }
}
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteLoggerFactory.java b/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteLoggerFactory.java
index 0697e39f..8a9d5dd3 100755
--- a/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteLoggerFactory.java
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteLoggerFactory.java
@@ -24,13 +24,15 @@
*/
package org.slf4j.helpers;
-import org.slf4j.ILoggerFactory;
-import org.slf4j.Logger;
-
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.slf4j.ILoggerFactory;
+import org.slf4j.Logger;
+import org.slf4j.event.SubstituteLoggingEvent;
/**
* SubstituteLoggerFactory manages instances of {@link SubstituteLogger}.
@@ -40,28 +42,39 @@ import java.util.concurrent.ConcurrentMap;
*/
public class SubstituteLoggerFactory implements ILoggerFactory {
- final ConcurrentMap<String, SubstituteLogger> loggers = new ConcurrentHashMap<String, SubstituteLogger>();
+ volatile boolean postInitialization = false;
- public Logger getLogger(String name) {
+ final Map<String, SubstituteLogger> loggers = new ConcurrentHashMap<>();
+
+ final LinkedBlockingQueue<SubstituteLoggingEvent> eventQueue = new LinkedBlockingQueue<>();
+
+ synchronized public Logger getLogger(String name) {
SubstituteLogger logger = loggers.get(name);
if (logger == null) {
- logger = new SubstituteLogger(name);
- SubstituteLogger oldLogger = loggers.putIfAbsent(name, logger);
- if (oldLogger != null)
- logger = oldLogger;
+ logger = new SubstituteLogger(name, eventQueue, postInitialization);
+ loggers.put(name, logger);
}
return logger;
}
public List<String> getLoggerNames() {
- return new ArrayList<String>(loggers.keySet());
+ return new ArrayList<>(loggers.keySet());
}
public List<SubstituteLogger> getLoggers() {
- return new ArrayList<SubstituteLogger>(loggers.values());
+ return new ArrayList<>(loggers.values());
+ }
+
+ public LinkedBlockingQueue<SubstituteLoggingEvent> getEventQueue() {
+ return eventQueue;
+ }
+
+ public void postInitialization() {
+ postInitialization = true;
}
public void clear() {
loggers.clear();
+ eventQueue.clear();
}
}
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteServiceProvider.java b/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteServiceProvider.java
new file mode 100755
index 00000000..2c59530d
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteServiceProvider.java
@@ -0,0 +1,41 @@
+package org.slf4j.helpers;
+
+import org.slf4j.ILoggerFactory;
+import org.slf4j.IMarkerFactory;
+import org.slf4j.spi.MDCAdapter;
+import org.slf4j.spi.SLF4JServiceProvider;
+
+public class SubstituteServiceProvider implements SLF4JServiceProvider {
+ private final SubstituteLoggerFactory loggerFactory = new SubstituteLoggerFactory();
+ private final IMarkerFactory markerFactory = new BasicMarkerFactory();
+ private final MDCAdapter mdcAdapter = new BasicMDCAdapter();
+
+ @Override
+ public ILoggerFactory getLoggerFactory() {
+ return loggerFactory;
+ }
+
+ public SubstituteLoggerFactory getSubstituteLoggerFactory() {
+ return loggerFactory;
+ }
+
+ @Override
+ public IMarkerFactory getMarkerFactory() {
+ return markerFactory;
+ }
+
+ @Override
+ public MDCAdapter getMDCAdapter() {
+ return mdcAdapter;
+ }
+
+ @Override
+ public String getRequestedApiVersion() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void initialize() {
+
+ }
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/ThreadLocalMapOfStacks.java b/slf4j-api/src/main/java/org/slf4j/helpers/ThreadLocalMapOfStacks.java
new file mode 100644
index 00000000..89ddee0e
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/ThreadLocalMapOfStacks.java
@@ -0,0 +1,89 @@
+package org.slf4j.helpers;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * A simple implementation of ThreadLocal backed Map containing values of type
+ * Deque<String>.
+ *
+ * @author Ceki Guuml;c&uuml;
+ * @since 2.0.0
+ */
+public class ThreadLocalMapOfStacks {
+
+ // BEWARE: Keys or values placed in a ThreadLocal should not be of a type/class
+ // not included in the JDK. See also https://jira.qos.ch/browse/LOGBACK-450
+
+ final ThreadLocal<Map<String, Deque<String>>> tlMapOfStacks = new ThreadLocal<>();
+
+ public void pushByKey(String key, String value) {
+ if (key == null)
+ return;
+
+ Map<String, Deque<String>> map = tlMapOfStacks.get();
+
+ if (map == null) {
+ map = new HashMap<>();
+ tlMapOfStacks.set(map);
+ }
+
+ Deque<String> deque = map.get(key);
+ if (deque == null) {
+ deque = new ArrayDeque<>();
+ }
+ deque.push(value);
+ map.put(key, deque);
+ }
+
+ public String popByKey(String key) {
+ if (key == null)
+ return null;
+
+ Map<String, Deque<String>> map = tlMapOfStacks.get();
+ if (map == null)
+ return null;
+ Deque<String> deque = map.get(key);
+ if (deque == null)
+ return null;
+ return deque.pop();
+ }
+
+ public Deque<String> getCopyOfDequeByKey(String key) {
+ if (key == null)
+ return null;
+
+ Map<String, Deque<String>> map = tlMapOfStacks.get();
+ if (map == null)
+ return null;
+ Deque<String> deque = map.get(key);
+ if (deque == null)
+ return null;
+
+ return new ArrayDeque<String>(deque);
+ }
+
+ /**
+ * Clear the deque(stack) referenced by 'key'.
+ *
+ * @param key identifies the stack
+ *
+ * @since 2.0.0
+ */
+ public void clearDequeByKey(String key) {
+ if (key == null)
+ return;
+
+ Map<String, Deque<String>> map = tlMapOfStacks.get();
+ if (map == null)
+ return;
+ Deque<String> deque = map.get(key);
+ if (deque == null)
+ return;
+ deque.clear();
+ }
+
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/Util.java b/slf4j-api/src/main/java/org/slf4j/helpers/Util.java
index 91a09837..98858315 100755
--- a/slf4j-api/src/main/java/org/slf4j/helpers/Util.java
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/Util.java
@@ -35,6 +35,27 @@ public final class Util {
private Util() {
}
+ public static String safeGetSystemProperty(String key) {
+ if (key == null)
+ throw new IllegalArgumentException("null input");
+
+ String result = null;
+ try {
+ result = System.getProperty(key);
+ } catch (java.lang.SecurityException sm) {
+ ; // ignore
+ }
+ return result;
+ }
+
+ public static boolean safeGetBooleanSystemProperty(String key) {
+ String value = safeGetSystemProperty(key);
+ if (value == null)
+ return false;
+ else
+ return value.equalsIgnoreCase("true");
+ }
+
/**
* In order to call {@link SecurityManager#getClassContext()}, which is a
* protected method, we add this wrapper which allows the method to be visible
@@ -46,7 +67,28 @@ public final class Util {
}
}
- private static final ClassContextSecurityManager SECURITY_MANAGER = new ClassContextSecurityManager();
+ private static ClassContextSecurityManager SECURITY_MANAGER;
+ private static boolean SECURITY_MANAGER_CREATION_ALREADY_ATTEMPTED = false;
+
+ private static ClassContextSecurityManager getSecurityManager() {
+ if (SECURITY_MANAGER != null)
+ return SECURITY_MANAGER;
+ else if (SECURITY_MANAGER_CREATION_ALREADY_ATTEMPTED)
+ return null;
+ else {
+ SECURITY_MANAGER = safeCreateSecurityManager();
+ SECURITY_MANAGER_CREATION_ALREADY_ATTEMPTED = true;
+ return SECURITY_MANAGER;
+ }
+ }
+
+ private static ClassContextSecurityManager safeCreateSecurityManager() {
+ try {
+ return new ClassContextSecurityManager();
+ } catch (java.lang.SecurityException sm) {
+ return null;
+ }
+ }
/**
* Returns the name of the class which called the invoking method.
@@ -54,7 +96,10 @@ public final class Util {
* @return the name of the class which called the invoking method.
*/
public static Class<?> getCallingClass() {
- Class<?>[] trace = SECURITY_MANAGER.getClassContext();
+ ClassContextSecurityManager securityManager = getSecurityManager();
+ if (securityManager == null)
+ return null;
+ Class<?>[] trace = securityManager.getClassContext();
String thisClassName = Util.class.getName();
// Advance until Util is found
@@ -72,13 +117,28 @@ public final class Util {
return trace[i + 2];
}
+ /**
+ * See {@link Reporter#error(String, Throwable)} class for alternative.
+ *
+ * @deprecated replaced by the {@link Reporter#error(String, Throwable)} method.
+ * @param msg message to print
+ * @param t throwable to print
+ */
static final public void report(String msg, Throwable t) {
System.err.println(msg);
System.err.println("Reported exception:");
t.printStackTrace();
}
+ /**
+ * See {@link Reporter} class for alternatives.
+ *
+ * @deprecated replaced by one of {@link Reporter#info(String)},
+ * {@link Reporter#warn(String)} or {@link Reporter#error(String)} methods.
+ * @param msg message to print
+ */
static final public void report(String msg) {
System.err.println("SLF4J: " + msg);
}
+
}
diff --git a/slf4j-api/src/main/java/org/slf4j/impl/StaticLoggerBinder.java b/slf4j-api/src/main/java/org/slf4j/impl/StaticLoggerBinder.java
deleted file mode 100644
index e6747e53..00000000
--- a/slf4j-api/src/main/java/org/slf4j/impl/StaticLoggerBinder.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/**
- * Copyright (c) 2004-2011 QOS.ch
- * All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-package org.slf4j.impl;
-
-import org.slf4j.ILoggerFactory;
-
-/**
- * The binding of {@link LoggerFactory} class with an actual instance of
- * {@link ILoggerFactory} is performed using information returned by this class.
- *
- * This class is meant to provide a dummy StaticLoggerBinder to the slf4j-api module.
- * Real implementations are found in each SLF4J binding project, e.g. slf4j-nop,
- * slf4j-log4j12 etc.
- *
- * @author Ceki G&uuml;lc&uuml;
- */
-public class StaticLoggerBinder {
-
- /**
- * The unique instance of this class.
- */
- private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
-
- /**
- * Return the singleton of this class.
- *
- * @return the StaticLoggerBinder singleton
- */
- public static final StaticLoggerBinder getSingleton() {
- return SINGLETON;
- }
-
- /**
- * Declare the version of the SLF4J API this implementation is compiled against.
- * The value of this field is usually modified with each release.
- */
- // to avoid constant folding by the compiler, this field must *not* be final
- public static String REQUESTED_API_VERSION = "1.6.99"; // !final
-
- private StaticLoggerBinder() {
- throw new UnsupportedOperationException("This code should have never made it into slf4j-api.jar");
- }
-
- public ILoggerFactory getLoggerFactory() {
- throw new UnsupportedOperationException("This code should never make it into slf4j-api.jar");
- }
-
- public String getLoggerFactoryClassStr() {
- throw new UnsupportedOperationException("This code should never make it into slf4j-api.jar");
- }
-}
diff --git a/slf4j-api/src/main/java/org/slf4j/impl/StaticMarkerBinder.java b/slf4j-api/src/main/java/org/slf4j/impl/StaticMarkerBinder.java
deleted file mode 100644
index 0823b827..00000000
--- a/slf4j-api/src/main/java/org/slf4j/impl/StaticMarkerBinder.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * Copyright (c) 2004-2011 QOS.ch
- * All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-package org.slf4j.impl;
-
-import org.slf4j.IMarkerFactory;
-import org.slf4j.MarkerFactory;
-import org.slf4j.helpers.BasicMarkerFactory;
-import org.slf4j.spi.MarkerFactoryBinder;
-
-/**
- *
- * The binding of {@link MarkerFactory} class with an actual instance of
- * {@link IMarkerFactory} is performed using information returned by this class.
- *
- * This class is meant to provide a *dummy* StaticMarkerBinder to the slf4j-api module.
- * Real implementations are found in each SLF4J binding project, e.g. slf4j-nop,
- * slf4j-simple, slf4j-log4j12 etc.
- *
- * @author Ceki G&uuml;lc&uuml;
- */
-public class StaticMarkerBinder implements MarkerFactoryBinder {
-
- /**
- * The unique instance of this class.
- */
- public static final StaticMarkerBinder SINGLETON = new StaticMarkerBinder();
-
- private StaticMarkerBinder() {
- throw new UnsupportedOperationException("This code should never make it into the jar");
- }
-
- /**
- * Currently this method always returns an instance of
- * {@link BasicMarkerFactory}.
- */
- public IMarkerFactory getMarkerFactory() {
- throw new UnsupportedOperationException("This code should never make it into the jar");
- }
-
- /**
- * Currently, this method returns the class name of
- * {@link BasicMarkerFactory}.
- */
- public String getMarkerFactoryClassStr() {
- throw new UnsupportedOperationException("This code should never make it into the jar");
- }
-
-}
diff --git a/slf4j-api/src/main/java/org/slf4j/impl/package.html b/slf4j-api/src/main/java/org/slf4j/impl/package.html
deleted file mode 100644
index 6b84bada..00000000
--- a/slf4j-api/src/main/java/org/slf4j/impl/package.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-
-
-<html>
- <head>
- <title></title>
- </head>
-
-
- <body>
-
- <p>Implementations of core logging interfaces defined in the {@link
- org.slf4j} package.</p>
-
- <hr/>
- </body>
-</html>
diff --git a/slf4j-api/src/main/java/org/slf4j/package.html b/slf4j-api/src/main/java/org/slf4j/package.html
index e50b3ee2..323bac21 100644
--- a/slf4j-api/src/main/java/org/slf4j/package.html
+++ b/slf4j-api/src/main/java/org/slf4j/package.html
@@ -11,6 +11,6 @@
<p>Core logging interfaces.</p>
- <hr/>
+ <hr>
</body>
</html>
diff --git a/slf4j-api/src/main/java/org/slf4j/spi/CallerBoundaryAware.java b/slf4j-api/src/main/java/org/slf4j/spi/CallerBoundaryAware.java
new file mode 100644
index 00000000..e1a2de95
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/spi/CallerBoundaryAware.java
@@ -0,0 +1,25 @@
+package org.slf4j.spi;
+
+import org.slf4j.event.LoggingEvent;
+
+/**
+ * Additional interface to {@link LoggingEventBuilder} and
+ * {@link org.slf4j.event.LoggingEvent LoggingEvent}.
+ *
+ * Implementations of {@link LoggingEventBuilder} and {@link LoggingEvent} may optionally
+ * implement {@link CallerBoundaryAware} in order to support caller info extraction.
+ *
+ * This interface is intended for use by logging backends or logging bridges.
+ *
+ * @author Ceki Gulcu
+ *
+ */
+public interface CallerBoundaryAware {
+
+ /**
+ * Add a fqcn (fully qualified class name) to this event, presumed to be the caller boundary.
+ *
+ * @param fqcn
+ */
+ void setCallerBoundary(String fqcn);
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/spi/DefaultLoggingEventBuilder.java b/slf4j-api/src/main/java/org/slf4j/spi/DefaultLoggingEventBuilder.java
new file mode 100755
index 00000000..66652b07
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/spi/DefaultLoggingEventBuilder.java
@@ -0,0 +1,246 @@
+/**
+ * Copyright (c) 2004-2022 QOS.ch
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+package org.slf4j.spi;
+
+import java.util.function.Supplier;
+
+import org.slf4j.Logger;
+import org.slf4j.Marker;
+import org.slf4j.event.DefaultLoggingEvent;
+import org.slf4j.event.KeyValuePair;
+import org.slf4j.event.Level;
+import org.slf4j.event.LoggingEvent;
+
+/**
+ * Default implementation of {@link LoggingEventBuilder}
+ */
+public class DefaultLoggingEventBuilder implements LoggingEventBuilder, CallerBoundaryAware {
+
+
+ // The caller boundary when the log() methods are invoked, is this class itself.
+
+ static String DLEB_FQCN = DefaultLoggingEventBuilder.class.getName();
+
+ protected DefaultLoggingEvent loggingEvent;
+ protected Logger logger;
+
+ public DefaultLoggingEventBuilder(Logger logger, Level level) {
+ this.logger = logger;
+ loggingEvent = new DefaultLoggingEvent(level, logger);
+ }
+
+ /**
+ * Add a marker to the current logging event being built.
+ *
+ * It is possible to add multiple markers to the same logging event.
+ *
+ * @param marker the marker to add
+ */
+ @Override
+ public LoggingEventBuilder addMarker(Marker marker) {
+ loggingEvent.addMarker(marker);
+ return this;
+ }
+
+ @Override
+ public LoggingEventBuilder setCause(Throwable t) {
+ loggingEvent.setThrowable(t);
+ return this;
+ }
+
+ @Override
+ public LoggingEventBuilder addArgument(Object p) {
+ loggingEvent.addArgument(p);
+ return this;
+ }
+
+ @Override
+ public LoggingEventBuilder addArgument(Supplier<?> objectSupplier) {
+ loggingEvent.addArgument(objectSupplier.get());
+ return this;
+ }
+
+ @Override
+ public void setCallerBoundary(String fqcn) {
+ loggingEvent.setCallerBoundary(fqcn);
+ }
+
+ @Override
+ public void log() {
+ log(loggingEvent);
+ }
+
+ @Override
+ public LoggingEventBuilder setMessage(String message) {
+ loggingEvent.setMessage(message);
+ return this;
+ }
+ @Override
+ public LoggingEventBuilder setMessage(Supplier<String> messageSupplier) {
+ loggingEvent.setMessage(messageSupplier.get());
+ return this;
+ }
+
+ @Override
+ public void log(String message) {
+ loggingEvent.setMessage(message);
+ log(loggingEvent);
+ }
+
+ @Override
+ public void log(String message, Object arg) {
+ loggingEvent.setMessage(message);
+ loggingEvent.addArgument(arg);
+ log(loggingEvent);
+ }
+
+ @Override
+ public void log(String message, Object arg0, Object arg1) {
+ loggingEvent.setMessage(message);
+ loggingEvent.addArgument(arg0);
+ loggingEvent.addArgument(arg1);
+ log(loggingEvent);
+ }
+
+ @Override
+ public void log(String message, Object... args) {
+ loggingEvent.setMessage(message);
+ loggingEvent.addArguments(args);
+
+ log(loggingEvent);
+ }
+
+ @Override
+ public void log(Supplier<String> messageSupplier) {
+ if (messageSupplier == null) {
+ log((String) null);
+ } else {
+ log(messageSupplier.get());
+ }
+ }
+
+ protected void log(LoggingEvent aLoggingEvent) {
+ setCallerBoundary(DLEB_FQCN);
+ if (logger instanceof LoggingEventAware) {
+ ((LoggingEventAware) logger).log(aLoggingEvent);
+ } else {
+ logViaPublicSLF4JLoggerAPI(aLoggingEvent);
+ }
+ }
+
+ private void logViaPublicSLF4JLoggerAPI(LoggingEvent aLoggingEvent) {
+ Object[] argArray = aLoggingEvent.getArgumentArray();
+ int argLen = argArray == null ? 0 : argArray.length;
+
+ Throwable t = aLoggingEvent.getThrowable();
+ int tLen = t == null ? 0 : 1;
+
+ String msg = aLoggingEvent.getMessage();
+
+ Object[] combinedArguments = new Object[argLen + tLen];
+
+ if (argArray != null) {
+ System.arraycopy(argArray, 0, combinedArguments, 0, argLen);
+ }
+ if (t != null) {
+ combinedArguments[argLen] = t;
+ }
+
+ msg = mergeMarkersAndKeyValuePairs(aLoggingEvent, msg);
+
+ switch (aLoggingEvent.getLevel()) {
+ case TRACE:
+ logger.trace(msg, combinedArguments);
+ break;
+ case DEBUG:
+ logger.debug(msg, combinedArguments);
+ break;
+ case INFO:
+ logger.info(msg, combinedArguments);
+ break;
+ case WARN:
+ logger.warn(msg, combinedArguments);
+ break;
+ case ERROR:
+ logger.error(msg, combinedArguments);
+ break;
+ }
+
+ }
+
+ /**
+ * Prepend markers and key-value pairs to the message.
+ *
+ * @param aLoggingEvent
+ * @param msg
+ * @return
+ */
+ private String mergeMarkersAndKeyValuePairs(LoggingEvent aLoggingEvent, String msg) {
+
+ StringBuilder sb = null;
+
+ if (aLoggingEvent.getMarkers() != null) {
+ sb = new StringBuilder();
+ for (Marker marker : aLoggingEvent.getMarkers()) {
+ sb.append(marker);
+ sb.append(' ');
+ }
+ }
+
+ if (aLoggingEvent.getKeyValuePairs() != null) {
+ if (sb == null) {
+ sb = new StringBuilder();
+ }
+ for (KeyValuePair kvp : aLoggingEvent.getKeyValuePairs()) {
+ sb.append(kvp.key);
+ sb.append('=');
+ sb.append(kvp.value);
+ sb.append(' ');
+ }
+ }
+
+ if (sb != null) {
+ sb.append(msg);
+ return sb.toString();
+ } else {
+ return msg;
+ }
+ }
+
+
+
+ @Override
+ public LoggingEventBuilder addKeyValue(String key, Object value) {
+ loggingEvent.addKeyValue(key, value);
+ return this;
+ }
+
+ @Override
+ public LoggingEventBuilder addKeyValue(String key, Supplier<Object> value) {
+ loggingEvent.addKeyValue(key, value.get());
+ return this;
+ }
+
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/spi/LocationAwareLogger.java b/slf4j-api/src/main/java/org/slf4j/spi/LocationAwareLogger.java
index 8e0a2070..d0d1a31b 100644
--- a/slf4j-api/src/main/java/org/slf4j/spi/LocationAwareLogger.java
+++ b/slf4j-api/src/main/java/org/slf4j/spi/LocationAwareLogger.java
@@ -34,11 +34,13 @@ import org.slf4j.Marker;
* which need to provide hints so that the underlying logging system can extract
* the correct location information (method name, line number).
*
- * @author Ceki Gulcu
+ * @author Ceki G&uuml;lc&uuml;
* @since 1.3
*/
public interface LocationAwareLogger extends Logger {
+ // these constants should be in EventConstants. However, in order to preserve binary backward compatibility
+ // we keep these constants here. {@link EventConstants} redefines these constants using the values below.
final public int TRACE_INT = 00;
final public int DEBUG_INT = 10;
final public int INFO_INT = 20;
@@ -49,9 +51,12 @@ public interface LocationAwareLogger extends Logger {
* Printing method with support for location information.
*
* @param marker The marker to be used for this event, may be null.
+ *
* @param fqcn The fully qualified class name of the <b>logger instance</b>,
* typically the logger class, logger bridge or a logger wrapper.
+ *
* @param level One of the level integers defined in this interface
+ *
* @param message The message for the log event
* @param t Throwable associated with the log event, may be null.
*/
diff --git a/slf4j-api/src/main/java/org/slf4j/spi/LoggerFactoryBinder.java b/slf4j-api/src/main/java/org/slf4j/spi/LoggerFactoryBinder.java
index c0c2ca05..169691f3 100644
--- a/slf4j-api/src/main/java/org/slf4j/spi/LoggerFactoryBinder.java
+++ b/slf4j-api/src/main/java/org/slf4j/spi/LoggerFactoryBinder.java
@@ -31,6 +31,7 @@ import org.slf4j.ILoggerFactory;
* class bind with the appropriate {@link ILoggerFactory} instance.
*
* @author Ceki G&uuml;lc&uuml;
+ * @deprecated
*/
public interface LoggerFactoryBinder {
@@ -47,9 +48,9 @@ public interface LoggerFactoryBinder {
* The String form of the {@link ILoggerFactory} object that this
* <code>LoggerFactoryBinder</code> instance is <em>intended</em> to return.
*
- * <p>This method allows the developer to intterogate this binder's intention
+ * <p>This method allows the developer to interrogate this binder's intention
* which may be different from the {@link ILoggerFactory} instance it is able to
- * yield in practice. The discrepency should only occur in case of errors.
+ * yield in practice. The discrepancy should only occur in case of errors.
*
* @return the class name of the intended {@link ILoggerFactory} instance
*/
diff --git a/slf4j-api/src/main/java/org/slf4j/spi/LoggingEventAware.java b/slf4j-api/src/main/java/org/slf4j/spi/LoggingEventAware.java
new file mode 100644
index 00000000..b833e40c
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/spi/LoggingEventAware.java
@@ -0,0 +1,23 @@
+package org.slf4j.spi;
+
+import org.slf4j.event.LoggingEvent;
+
+/**
+ * A logger capable of logging from org.slf4j.event.LoggingEvent implements this interface.
+ *
+ * <p>Please note that when the {@link #log(LoggingEvent)} method assumes that
+ * the event was filtered beforehand and no further filtering needs to occur by the method itself.
+ * <p>
+ *
+ * <p>Implementations of this interface <b>may</b> apply further filtering but they are not
+ * required to do so. In other words, {@link #log(LoggingEvent)} method is free to assume that
+ * the event was filtered beforehand and no further filtering needs to occur in the method itself.</p>
+ *
+ * See also https://jira.qos.ch/browse/SLF4J-575
+ *
+ * @author Ceki Gulcu
+ * @since 2.0.0
+ */
+public interface LoggingEventAware {
+ void log(LoggingEvent event);
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/spi/LoggingEventBuilder.java b/slf4j-api/src/main/java/org/slf4j/spi/LoggingEventBuilder.java
new file mode 100755
index 00000000..55e24cfb
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/spi/LoggingEventBuilder.java
@@ -0,0 +1,169 @@
+/**
+ * Copyright (c) 2004-2021 QOS.ch
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+package org.slf4j.spi;
+
+import java.util.function.Supplier;
+
+import org.slf4j.Marker;
+
+import org.slf4j.helpers.CheckReturnValue;
+
+/**
+ * This is the main interface in slf4j's fluent API for creating
+ * {@link org.slf4j.event.LoggingEvent logging events}.
+ *
+ * @author Ceki G&uuml;lc&uuml;
+ * @since 2.0.0
+ */
+public interface LoggingEventBuilder {
+
+ /**
+ * Set the cause for the logging event being built.
+ * @param cause a throwable
+ * @return a LoggingEventBuilder, usually <b>this</b>.
+ */
+ @CheckReturnValue
+ LoggingEventBuilder setCause(Throwable cause);
+
+ /**
+ * A {@link Marker marker} to the event being built.
+ *
+ * @param marker a Marker instance to add.
+ * @return a LoggingEventBuilder, usually <b>this</b>.
+ */
+ @CheckReturnValue
+ LoggingEventBuilder addMarker(Marker marker);
+
+ /**
+ * Add an argument to the event being built.
+ *
+ * @param p an Object to add.
+ * @return a LoggingEventBuilder, usually <b>this</b>.
+ */
+ @CheckReturnValue
+ LoggingEventBuilder addArgument(Object p);
+
+ /**
+ * Add an argument supplier to the event being built.
+ *
+ * @param objectSupplier an Object supplier to add.
+ * @return a LoggingEventBuilder, usually <b>this</b>.
+ */
+ @CheckReturnValue
+ LoggingEventBuilder addArgument(Supplier<?> objectSupplier);
+
+
+ /**
+ * Add a {@link org.slf4j.event.KeyValuePair key value pair} to the event being built.
+ *
+ * @param key the key of the key value pair.
+ * @param value the value of the key value pair.
+ * @return a LoggingEventBuilder, usually <b>this</b>.
+ */
+ @CheckReturnValue
+ LoggingEventBuilder addKeyValue(String key, Object value);
+
+ /**
+ * Add a {@link org.slf4j.event.KeyValuePair key value pair} to the event being built.
+ *
+ * @param key the key of the key value pair.
+ * @param valueSupplier a supplier of a value for the key value pair.
+ * @return a LoggingEventBuilder, usually <b>this</b>.
+ */
+ @CheckReturnValue
+ LoggingEventBuilder addKeyValue(String key, Supplier<Object> valueSupplier);
+
+ /**
+ * Sets the message of the logging event.
+ *
+ * @since 2.0.0-beta0
+ */
+ @CheckReturnValue
+ LoggingEventBuilder setMessage(String message);
+
+ /**
+ * Sets the message of the event via a message supplier.
+ *
+ * @param messageSupplier supplies a String to be used as the message for the event
+ * @since 2.0.0-beta0
+ */
+ @CheckReturnValue
+ LoggingEventBuilder setMessage(Supplier<String> messageSupplier);
+
+ /**
+ * After the logging event is built, performs actual logging. This method must be called
+ * for logging to occur.
+ *
+ * If the call to {@link #log()} is omitted, a {@link org.slf4j.event.LoggingEvent LoggingEvent}
+ * will be built but no logging will occur.
+ *
+ * @since 2.0.0-beta0
+ */
+ void log();
+
+ /**
+ * Equivalent to calling {@link #setMessage(String)} followed by {@link #log()};
+ *
+ * @param message the message to log
+ */
+ void log(String message);
+
+ /**
+ * Equivalent to calling {@link #setMessage(String)} followed by {@link #addArgument(Object)}}
+ * and then {@link #log()}
+ *
+ * @param message the message to log
+ * @param arg an argument to be used with the message to log
+ */
+ void log(String message, Object arg);
+
+ /**
+ * Equivalent to calling {@link #setMessage(String)} followed by two calls to
+ * {@link #addArgument(Object)} and then {@link #log()}
+ *
+ * @param message the message to log
+ * @param arg0 first argument to be used with the message to log
+ * @param arg1 second argument to be used with the message to log
+ */
+ void log(String message, Object arg0, Object arg1);
+
+
+ /**
+ * Equivalent to calling {@link #setMessage(String)} followed by zero or more calls to
+ * {@link #addArgument(Object)} (depending on the size of args array) and then {@link #log()}
+ *
+ * @param message the message to log
+ * @param args a list (actually an array) of arguments to be used with the message to log
+ */
+ void log(String message, Object... args);
+
+ /**
+ * Equivalent to calling {@link #setMessage(Supplier)} followed by {@link #log()}
+ *
+ * @param messageSupplier a Supplier returning a message of type String
+ */
+ void log(Supplier<String> messageSupplier);
+
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/spi/MDCAdapter.java b/slf4j-api/src/main/java/org/slf4j/spi/MDCAdapter.java
index 2e637cbe..924849d1 100644
--- a/slf4j-api/src/main/java/org/slf4j/spi/MDCAdapter.java
+++ b/slf4j-api/src/main/java/org/slf4j/spi/MDCAdapter.java
@@ -24,6 +24,7 @@
*/
package org.slf4j.spi;
+import java.util.Deque;
import java.util.Map;
/**
@@ -38,7 +39,7 @@ public interface MDCAdapter {
/**
* Put a context value (the <code>val</code> parameter) as identified with
* the <code>key</code> parameter into the current thread's context map.
- * The <code>key</code> parameter cannot be null. The code>val</code> parameter
+ * The <code>key</code> parameter cannot be null. The <code>val</code> parameter
* can be null only if the underlying implementation supports it.
*
* <p>If the current thread does not have a context map it is created as a side
@@ -55,7 +56,7 @@ public interface MDCAdapter {
public String get(String key);
/**
- * Remove the the context identified by the <code>key</code> parameter.
+ * Remove the context identified by the <code>key</code> parameter.
* The <code>key</code> parameter cannot be null.
*
* <p>
@@ -83,9 +84,50 @@ public interface MDCAdapter {
* map and then copying the map passed as parameter. The context map
* parameter must only contain keys and values of type String.
*
+ * Implementations must support null valued map passed as parameter.
+ *
* @param contextMap must contain only keys and values of type String
*
* @since 1.5.1
*/
public void setContextMap(Map<String, String> contextMap);
+
+ /**
+ * Push a value into the deque(stack) referenced by 'key'.
+ *
+ * @param key identifies the appropriate stack
+ * @param value the value to push into the stack
+ * @since 2.0.0
+ */
+ public void pushByKey(String key, String value);
+
+ /**
+ * Pop the stack referenced by 'key' and return the value possibly null.
+ *
+ * @param key identifies the deque(stack)
+ * @return the value just popped. May be null/
+ * @since 2.0.0
+ */
+ public String popByKey(String key);
+
+ /**
+ * Returns a copy of the deque(stack) referenced by 'key'. May be null.
+ *
+ * @param key identifies the stack
+ * @return copy of stack referenced by 'key'. May be null.
+ *
+ * @since 2.0.0
+ */
+ public Deque<String> getCopyOfDequeByKey(String key);
+
+
+ /**
+ * Clear the deque(stack) referenced by 'key'.
+ *
+ * @param key identifies the stack
+ *
+ * @since 2.0.0
+ */
+ public void clearDequeByKey(String key);
+
}
diff --git a/slf4j-api/src/main/java/org/slf4j/spi/MarkerFactoryBinder.java b/slf4j-api/src/main/java/org/slf4j/spi/MarkerFactoryBinder.java
index a71140c9..0502ffad 100644
--- a/slf4j-api/src/main/java/org/slf4j/spi/MarkerFactoryBinder.java
+++ b/slf4j-api/src/main/java/org/slf4j/spi/MarkerFactoryBinder.java
@@ -31,6 +31,7 @@ import org.slf4j.IMarkerFactory;
* class bind with the appropriate {@link IMarkerFactory} instance.
*
* @author Ceki G&uuml;lc&uuml;
+ * @deprecated
*/
public interface MarkerFactoryBinder {
@@ -47,9 +48,9 @@ public interface MarkerFactoryBinder {
* The String form of the {@link IMarkerFactory} object that this
* <code>MarkerFactoryBinder</code> instance is <em>intended</em> to return.
*
- * <p>This method allows the developer to intterogate this binder's intention
+ * <p>This method allows the developer to interrogate this binder's intention
* which may be different from the {@link IMarkerFactory} instance it is able to
- * return. Such a discrepency should only occur in case of errors.
+ * return. Such a discrepancy should only occur in case of errors.
*
* @return the class name of the intended {@link IMarkerFactory} instance
*/
diff --git a/slf4j-api/src/main/java/org/slf4j/spi/NOPLoggingEventBuilder.java b/slf4j-api/src/main/java/org/slf4j/spi/NOPLoggingEventBuilder.java
new file mode 100755
index 00000000..e356b47f
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/spi/NOPLoggingEventBuilder.java
@@ -0,0 +1,100 @@
+package org.slf4j.spi;
+
+import java.util.function.Supplier;
+
+import org.slf4j.Marker;
+import org.slf4j.event.Level;
+
+/**
+ * <p>A no-operation implementation of {@link LoggingEventBuilder}.</p>
+ *
+ * <p>As the name indicates, the method in this class do nothing, except when a return value is expected
+ * in which case a singleton, i.e. the unique instance of this class is returned.
+ * </p
+ *
+ * @author Ceki G&uuml;lc&uuml;
+ * @since 2.0.0
+ *
+ */
+public class NOPLoggingEventBuilder implements LoggingEventBuilder {
+
+ static final NOPLoggingEventBuilder SINGLETON = new NOPLoggingEventBuilder();
+
+ private NOPLoggingEventBuilder() {
+ }
+
+ /**
+ * <p>Returns the singleton instance of this class.
+ * Used by {@link org.slf4j.Logger#makeLoggingEventBuilder(Level) makeLoggingEventBuilder(Level)}.</p>
+ *
+ * @return the singleton instance of this class
+ */
+ public static LoggingEventBuilder singleton() {
+ return SINGLETON;
+ }
+
+ @Override
+ public LoggingEventBuilder addMarker(Marker marker) {
+ return singleton();
+ }
+
+ @Override
+ public LoggingEventBuilder addArgument(Object p) {
+ return singleton();
+ }
+
+ @Override
+ public LoggingEventBuilder addArgument(Supplier<?> objectSupplier) {
+ return singleton();
+ }
+
+ @Override
+ public LoggingEventBuilder addKeyValue(String key, Object value) {
+ return singleton();
+ }
+
+ @Override
+ public LoggingEventBuilder addKeyValue(String key, Supplier<Object> value) {
+ return singleton();
+ }
+
+ @Override
+ public LoggingEventBuilder setCause(Throwable cause) {
+ return singleton();
+ }
+
+ @Override
+ public void log() {
+ }
+
+ @Override
+ public LoggingEventBuilder setMessage(String message) {
+ return this;
+ }
+ @Override
+ public LoggingEventBuilder setMessage(Supplier<String> messageSupplier) {
+ return this;
+ }
+
+ @Override
+ public void log(String message) {
+ }
+
+ @Override
+ public void log(Supplier<String> messageSupplier) {
+ }
+
+ @Override
+ public void log(String message, Object arg) {
+ }
+
+ @Override
+ public void log(String message, Object arg0, Object arg1) {
+ }
+
+ @Override
+ public void log(String message, Object... args) {
+
+ }
+
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/spi/SLF4JServiceProvider.java b/slf4j-api/src/main/java/org/slf4j/spi/SLF4JServiceProvider.java
new file mode 100755
index 00000000..83e08741
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/spi/SLF4JServiceProvider.java
@@ -0,0 +1,60 @@
+package org.slf4j.spi;
+
+import org.slf4j.ILoggerFactory;
+import org.slf4j.IMarkerFactory;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+
+/**
+ * This interface based on {@link java.util.ServiceLoader} paradigm.
+ *
+ * <p>It replaces the old static-binding mechanism used in SLF4J versions 1.0.x to 1.7.x.
+ *
+ * @author Ceki G&uml;lc&uml;
+ * @since 1.8
+ */
+public interface SLF4JServiceProvider {
+
+ /**
+ * Return the instance of {@link ILoggerFactory} that
+ * {@link org.slf4j.LoggerFactory} class should bind to.
+ *
+ * @return instance of {@link ILoggerFactory}
+ */
+ public ILoggerFactory getLoggerFactory();
+
+ /**
+ * Return the instance of {@link IMarkerFactory} that
+ * {@link org.slf4j.MarkerFactory} class should bind to.
+ *
+ * @return instance of {@link IMarkerFactory}
+ */
+ public IMarkerFactory getMarkerFactory();
+
+ /**
+ * Return the instance of {@link MDCAdapter} that
+ * {@link MDC} should bind to.
+ *
+ * @return instance of {@link MDCAdapter}
+ */
+ public MDCAdapter getMDCAdapter();
+
+ /**
+ * Return the maximum API version for SLF4J that the logging
+ * implementation supports.
+ *
+ * <p>For example: {@code "2.0.1"}.
+ *
+ * @return the string API version.
+ */
+ public String getRequestedApiVersion();
+
+ /**
+ * Initialize the logging back-end.
+ *
+ * <p><b>WARNING:</b> This method is intended to be called once by
+ * {@link LoggerFactory} class and from nowhere else.
+ *
+ */
+ public void initialize();
+}