• logger(一)slf4j简介及其实现原理


    一、slf4j简介

    slf4j(Simple logging facade for Java)是对所有日志框架制定的一种规范、标准、接口,并不是一个框架的具体的实现,因为接口并不能独立使用,需要和具体的日志框架实现配合使用

    slf4j是门面模式的典型应用,外部与一个子系统的通信必须通过一个统一的外观对象进行,使得子系统更易于使用。用一张图来表示门面模式的结构为

    日志实现(log4j、logback、log4j2)

    • log4j是apache实现的一个开源日志组件
    • logback同样是由log4j的作者设计完成的,拥有更好的特性,用来取代log4j的一个日志框架,是slf4j的原生实现
    • log4j2是log4j 1.x和logback的改进版,据说采用了一些新技术(无锁异步、等等),使得日志的吞吐量、性能比log4j 1.x提高10倍,并解决了一些死锁的bug,而且配置更加简单灵活。

    为什么要使用slf4j

    • SLF4J提供了统一的记录日志的接口,对不同日志系统的具体实现进行了抽象化,只要按照其提供的方法记录即可,最终日志的格式、记录级别、输出方式等通过绑定具体的日志系统来实现。在项目中使用了SLF4J记录日志,并且绑定了log4j,则日志会以log4j的风格输出;后期需要改为以logback的风格输出日志,只需要将jar包log4j替换成logback即可,不用修改项目中的代码。
    • SLF4J支持{}作为占位符,等价于C语言中的%s,而不必再进行字符串的拼接,效率有显著的提升

    maven jar包

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.21</version>
    </dependency>

    如下图所示,slf4j不能单独使用,比如要使用logback实现,需要导入logback的jar包

    <dependency> 
        <groupId>org.slf4j</groupId> 
        <artifactId>slf4j-api</artifactId> 
        <version>1.7.21</version> 
    </dependency> 
    <dependency> 
        <groupId>ch.qos.logback</groupId> 
        <artifactId>logback-core</artifactId> 
        <version>1.1.7</version> 
    </dependency> 
    <dependency> 
        <groupId>ch.qos.logback</groupId> 
        <artifactId>logback-classic</artifactId> 
        <version>1.1.7</version> 
    </dependency> 

    比如要使用log4j或者 jdk自带的logging,需要使用桥接包如slf4j-jdk14.jar包

    二、实现原理

    slf4j只是一个日志标准,并不是日志系统的具体实现。slf4j只提做两件事情:Logger类用来打日志,LoggerFactory类用来获取Logger;

    1. 提供日志接口 (Logger.class)
    2. 提供获取具体日志对象的方法 (LoggerFactory.class)

    Logger.class

    public interface Logger {
    
        /**
         * Case insensitive String constant used to retrieve the name of the root logger.
         *
         * @since 1.3
         */
        final public String ROOT_LOGGER_NAME = "ROOT";
    
        /**
         * Return the name of this <code>Logger</code> instance.
         * @return name of this logger instance 
         */
        public String getName();
    
        /**
         * Is the logger instance enabled for the TRACE level?
         *
         * @return True if this Logger is enabled for the TRACE level,
         *         false otherwise.
         * @since 1.4
         */
        public boolean isTraceEnabled();
    
        /**
         * Log a message at the TRACE level.
         *
         * @param msg the message string to be logged
         * @since 1.4
         */
        public void trace(String msg);
    
        /**
         * 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>
         *
         * @param format the format string
         * @param arg    the argument
         * @since 1.4
         */
        public void trace(String format, Object arg);
    
        /**
         * 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>
         *
         * @param format the format string
         * @param arg1   the first argument
         * @param arg2   the second argument
         * @since 1.4
         */
        public void trace(String format, Object arg1, Object arg2);
    
        /**
         * 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>
         *
         * @param format    the format string
         * @param arguments a list of 3 or more arguments
         * @since 1.4
         */
        public void trace(String format, Object... arguments);
    
        /**
         * Log an exception (throwable) at the TRACE level with an
         * accompanying message.
         *
         * @param msg the message accompanying the exception
         * @param t   the exception (throwable) to log
         * @since 1.4
         */
        public void trace(String msg, Throwable t);
    
        /**
         * Similar to {@link #isTraceEnabled()} method except that the
         * marker data is also taken into account.
         *
         * @param marker The marker data to take into consideration
         * @return True if this Logger is enabled for the TRACE level,
         *         false otherwise.
         *         
         * @since 1.4
         */
        public boolean isTraceEnabled(Marker marker);
    
        /**
         * Log a message with the specific Marker at the TRACE level.
         *
         * @param marker the marker data specific to this log statement
         * @param msg    the message string to be logged
         * @since 1.4
         */
        public void trace(Marker marker, String msg);
    
        /**
         * This method is similar to {@link #trace(String, Object)} method except that the
         * marker data is also taken into consideration.
         *
         * @param marker the marker data specific to this log statement
         * @param format the format string
         * @param arg    the argument
         * @since 1.4
         */
        public void trace(Marker marker, String format, Object arg);
    
        /**
         * This method is similar to {@link #trace(String, Object, Object)}
         * method except that the marker data is also taken into
         * consideration.
         *
         * @param marker the marker data specific to this log statement
         * @param format the format string
         * @param arg1   the first argument
         * @param arg2   the second argument
         * @since 1.4
         */
        public void trace(Marker marker, String format, Object arg1, Object arg2);
    
        /**
         * This method is similar to {@link #trace(String, Object...)}
         * method except that the marker data is also taken into
         * consideration.
         *
         * @param marker   the marker data specific to this log statement
         * @param format   the format string
         * @param argArray an array of arguments
         * @since 1.4
         */
        public void trace(Marker marker, String format, Object... argArray);
    
        /**
         * This method is similar to {@link #trace(String, Throwable)} method except that the
         * marker data is also taken into consideration.
         *
         * @param marker the marker data specific to this log statement
         * @param msg    the message accompanying the exception
         * @param t      the exception (throwable) to log
         * @since 1.4
         */
        public void trace(Marker marker, String msg, Throwable t);
    
        /**
         * Is the logger instance enabled for the DEBUG level?
         *
         * @return True if this Logger is enabled for the DEBUG level,
         *         false otherwise.
         */
        public boolean isDebugEnabled();
    
        /**
         * Log a message at the DEBUG level.
         *
         * @param msg the message string to be logged
         */
        public void debug(String msg);
    
        /**
         * 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>
         *
         * @param format the format string
         * @param arg    the argument
         */
        public void debug(String format, Object arg);
    
        /**
         * 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>
         *
         * @param format the format string
         * @param arg1   the first argument
         * @param arg2   the second argument
         */
        public void debug(String format, Object arg1, Object arg2);
    
        /**
         * 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>
         *
         * @param format    the format string
         * @param arguments a list of 3 or more arguments
         */
        public void debug(String format, Object... arguments);
    
        /**
         * Log an exception (throwable) at the DEBUG level with an
         * accompanying message.
         *
         * @param msg the message accompanying the exception
         * @param t   the exception (throwable) to log
         */
        public void debug(String msg, Throwable t);
    
        /**
         * Similar to {@link #isDebugEnabled()} method except that the
         * marker data is also taken into account.
         *
         * @param marker The marker data to take into consideration
         * @return True if this Logger is enabled for the DEBUG level,
         *         false otherwise. 
         */
        public boolean isDebugEnabled(Marker marker);
    
        /**
         * Log a message with the specific Marker at the DEBUG level.
         *
         * @param marker the marker data specific to this log statement
         * @param msg    the message string to be logged
         */
        public void debug(Marker marker, String msg);
    
        /**
         * This method is similar to {@link #debug(String, Object)} method except that the
         * marker data is also taken into consideration.
         *
         * @param marker the marker data specific to this log statement
         * @param format the format string
         * @param arg    the argument
         */
        public void debug(Marker marker, String format, Object arg);
    
        /**
         * This method is similar to {@link #debug(String, Object, Object)}
         * method except that the marker data is also taken into
         * consideration.
         *
         * @param marker the marker data specific to this log statement
         * @param format the format string
         * @param arg1   the first argument
         * @param arg2   the second argument
         */
        public void debug(Marker marker, String format, Object arg1, Object arg2);
    
        /**
         * This method is similar to {@link #debug(String, Object...)}
         * method except that the marker data is also taken into
         * consideration.
         *
         * @param marker    the marker data specific to this log statement
         * @param format    the format string
         * @param arguments a list of 3 or more arguments
         */
        public void debug(Marker marker, String format, Object... arguments);
    
        /**
         * This method is similar to {@link #debug(String, Throwable)} method except that the
         * marker data is also taken into consideration.
         *
         * @param marker the marker data specific to this log statement
         * @param msg    the message accompanying the exception
         * @param t      the exception (throwable) to log
         */
        public void debug(Marker marker, String msg, Throwable t);
    
        /**
         * Is the logger instance enabled for the INFO level?
         *
         * @return True if this Logger is enabled for the INFO level,
         *         false otherwise.
         */
        public boolean isInfoEnabled();
    
        /**
         * Log a message at the INFO level.
         *
         * @param msg the message string to be logged
         */
        public void info(String msg);
    
        /**
         * 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>
         *
         * @param format the format string
         * @param arg    the argument
         */
        public void info(String format, Object arg);
    
        /**
         * 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>
         *
         * @param format the format string
         * @param arg1   the first argument
         * @param arg2   the second argument
         */
        public void info(String format, Object arg1, Object arg2);
    
        /**
         * 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>
         *
         * @param format    the format string
         * @param arguments a list of 3 or more arguments
         */
        public void info(String format, Object... arguments);
    
        /**
         * Log an exception (throwable) at the INFO level with an
         * accompanying message.
         *
         * @param msg the message accompanying the exception
         * @param t   the exception (throwable) to log
         */
        public void info(String msg, Throwable t);
    
        /**
         * Similar to {@link #isInfoEnabled()} method except that the marker
         * 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 
         */
        public boolean isInfoEnabled(Marker marker);
    
        /**
         * Log a message with the specific Marker at the INFO level.
         *
         * @param marker The marker specific to this log statement
         * @param msg    the message string to be logged
         */
        public void info(Marker marker, String msg);
    
        /**
         * This method is similar to {@link #info(String, Object)} method except that the
         * marker data is also taken into consideration.
         *
         * @param marker the marker data specific to this log statement
         * @param format the format string
         * @param arg    the argument
         */
        public void info(Marker marker, String format, Object arg);
    
        /**
         * This method is similar to {@link #info(String, Object, Object)}
         * method except that the marker data is also taken into
         * consideration.
         *
         * @param marker the marker data specific to this log statement
         * @param format the format string
         * @param arg1   the first argument
         * @param arg2   the second argument
         */
        public void info(Marker marker, String format, Object arg1, Object arg2);
    
        /**
         * This method is similar to {@link #info(String, Object...)}
         * method except that the marker data is also taken into
         * consideration.
         *
         * @param marker    the marker data specific to this log statement
         * @param format    the format string
         * @param arguments a list of 3 or more arguments
         */
        public void info(Marker marker, String format, Object... arguments);
    
        /**
         * This method is similar to {@link #info(String, Throwable)} method
         * except that the marker data is also taken into consideration.
         *
         * @param marker the marker data for this log statement
         * @param msg    the message accompanying the exception
         * @param t      the exception (throwable) to log
         */
        public void info(Marker marker, String msg, Throwable t);
    
        /**
         * Is the logger instance enabled for the WARN level?
         *
         * @return True if this Logger is enabled for the WARN level,
         *         false otherwise.
         */
        public boolean isWarnEnabled();
    
        /**
         * Log a message at the WARN level.
         *
         * @param msg the message string to be logged
         */
        public void warn(String msg);
    
        /**
         * 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>
         *
         * @param format the format string
         * @param arg    the argument
         */
        public void warn(String format, Object arg);
    
        /**
         * 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>
         *
         * @param format    the format string
         * @param arguments a list of 3 or more arguments
         */
        public void warn(String format, Object... arguments);
    
        /**
         * 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>
         *
         * @param format the format string
         * @param arg1   the first argument
         * @param arg2   the second argument
         */
        public void warn(String format, Object arg1, Object arg2);
    
        /**
         * Log an exception (throwable) at the WARN level with an
         * accompanying message.
         *
         * @param msg the message accompanying the exception
         * @param t   the exception (throwable) to log
         */
        public void warn(String msg, Throwable t);
    
        /**
         * Similar to {@link #isWarnEnabled()} method except that the marker
         * data is also taken into consideration.
         *
         * @param marker The marker data to take into consideration
         * @return True if this Logger is enabled for the WARN level,
         *         false otherwise.
         */
        public boolean isWarnEnabled(Marker marker);
    
        /**
         * Log a message with the specific Marker at the WARN level.
         *
         * @param marker The marker specific to this log statement
         * @param msg    the message string to be logged
         */
        public void warn(Marker marker, String msg);
    
        /**
         * This method is similar to {@link #warn(String, Object)} method except that the
         * marker data is also taken into consideration.
         *
         * @param marker the marker data specific to this log statement
         * @param format the format string
         * @param arg    the argument
         */
        public void warn(Marker marker, String format, Object arg);
    
        /**
         * This method is similar to {@link #warn(String, Object, Object)}
         * method except that the marker data is also taken into
         * consideration.
         *
         * @param marker the marker data specific to this log statement
         * @param format the format string
         * @param arg1   the first argument
         * @param arg2   the second argument
         */
        public void warn(Marker marker, String format, Object arg1, Object arg2);
    
        /**
         * This method is similar to {@link #warn(String, Object...)}
         * method except that the marker data is also taken into
         * consideration.
         *
         * @param marker    the marker data specific to this log statement
         * @param format    the format string
         * @param arguments a list of 3 or more arguments
         */
        public void warn(Marker marker, String format, Object... arguments);
    
        /**
         * This method is similar to {@link #warn(String, Throwable)} method
         * except that the marker data is also taken into consideration.
         *
         * @param marker the marker data for this log statement
         * @param msg    the message accompanying the exception
         * @param t      the exception (throwable) to log
         */
        public void warn(Marker marker, String msg, Throwable t);
    
        /**
         * Is the logger instance enabled for the ERROR level?
         *
         * @return True if this Logger is enabled for the ERROR level,
         *         false otherwise.
         */
        public boolean isErrorEnabled();
    
        /**
         * Log a message at the ERROR level.
         *
         * @param msg the message string to be logged
         */
        public void error(String msg);
    
        /**
         * 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>
         *
         * @param format the format string
         * @param arg    the argument
         */
        public void error(String format, Object arg);
    
        /**
         * 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>
         *
         * @param format the format string
         * @param arg1   the first argument
         * @param arg2   the second argument
         */
        public void error(String format, Object arg1, Object arg2);
    
        /**
         * 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>
         *
         * @param format    the format string
         * @param arguments a list of 3 or more arguments
         */
        public void error(String format, Object... arguments);
    
        /**
         * Log an exception (throwable) at the ERROR level with an
         * accompanying message.
         *
         * @param msg the message accompanying the exception
         * @param t   the exception (throwable) to log
         */
        public void error(String msg, Throwable t);
    
        /**
         * Similar to {@link #isErrorEnabled()} method except that the
         * marker data is also taken into consideration.
         *
         * @param marker The marker data to take into consideration
         * @return True if this Logger is enabled for the ERROR level,
         *         false otherwise.
         */
        public boolean isErrorEnabled(Marker marker);
    
        /**
         * Log a message with the specific Marker at the ERROR level.
         *
         * @param marker The marker specific to this log statement
         * @param msg    the message string to be logged
         */
        public void error(Marker marker, String msg);
    
        /**
         * This method is similar to {@link #error(String, Object)} method except that the
         * marker data is also taken into consideration.
         *
         * @param marker the marker data specific to this log statement
         * @param format the format string
         * @param arg    the argument
         */
        public void error(Marker marker, String format, Object arg);
    
        /**
         * This method is similar to {@link #error(String, Object, Object)}
         * method except that the marker data is also taken into
         * consideration.
         *
         * @param marker the marker data specific to this log statement
         * @param format the format string
         * @param arg1   the first argument
         * @param arg2   the second argument
         */
        public void error(Marker marker, String format, Object arg1, Object arg2);
    
        /**
         * This method is similar to {@link #error(String, Object...)}
         * method except that the marker data is also taken into
         * consideration.
         *
         * @param marker    the marker data specific to this log statement
         * @param format    the format string
         * @param arguments a list of 3 or more arguments
         */
        public void error(Marker marker, String format, Object... arguments);
    
        /**
         * This method is similar to {@link #error(String, Throwable)}
         * method except that the marker data is also taken into
         * consideration.
         *
         * @param marker the marker data specific to this log statement
         * @param msg    the message accompanying the exception
         * @param t      the exception (throwable) to log
         */
        public void error(Marker marker, String msg, Throwable t);
    
    }
    View Code

    LoggerFactory.class

    public final class LoggerFactory {
    
        static final String CODES_PREFIX = "http://www.slf4j.org/codes.html";
    
        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 in failed state. Original exception was thrown EARLIER. See also " + UNSUCCESSFUL_INIT_URL;
    
        static final int UNINITIALIZED = 0;
        static final int ONGOING_INITIALIZATION = 1;
        static final int FAILED_INITIALIZATION = 2;
        static final int SUCCESSFUL_INITIALIZATION = 3;
        static final int NOP_FALLBACK_INITIALIZATION = 4;
    
        static volatile int INITIALIZATION_STATE = UNINITIALIZED;
        static final SubstituteLoggerFactory SUBST_FACTORY = new SubstituteLoggerFactory();
        static final NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory();
    
        // Support for detecting mismatched logger names.
        static final String DETECT_LOGGER_NAME_MISMATCH_PROPERTY = "slf4j.detectLoggerNameMismatch";
        static final String JAVA_VENDOR_PROPERTY = "java.vendor.url";
    
        static boolean DETECT_LOGGER_NAME_MISMATCH = Util.safeGetBooleanSystemProperty(DETECT_LOGGER_NAME_MISMATCH_PROPERTY);
    
        /**
         * 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.
         */
        static private final String[] API_COMPATIBILITY_LIST = new String[] { "1.6", "1.7" };
    
        // private constructor prevents instantiation
        private LoggerFactory() {
        }
    
        /**
         * Force LoggerFactory to consider itself uninitialized.
         * <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/>
         * You are strongly discouraged from calling this method in production code.
         */
        static void reset() {
            INITIALIZATION_STATE = UNINITIALIZED;
        }
    
        private final static void performInitialization() {
            bind();
            if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
                versionSanityCheck();
            }
        }
    
        private static boolean messageContainsOrgSlf4jImplStaticLoggerBinder(String msg) {
            if (msg == null)
                return false;
            if (msg.contains("org/slf4j/impl/StaticLoggerBinder"))
                return true;
            if (msg.contains("org.slf4j.impl.StaticLoggerBinder"))
                return true;
            return false;
        }
    
        private final static void bind() {
            try {
                Set<URL> staticLoggerBinderPathSet = null;
                // skip check under android, see also
                // http://jira.qos.ch/browse/SLF4J-328
                if (!isAndroid()) {
                    staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
                    reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
                }
                // the next line does the binding
                StaticLoggerBinder.getSingleton();
                INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
                reportActualBinding(staticLoggerBinderPathSet);
                fixSubstituteLoggers();
                replayEvents();
                // release all resources in SUBST_FACTORY
                SUBST_FACTORY.clear();
            } 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.");
                } else {
                    failedBinding(ncde);
                    throw ncde;
                }
            } catch (java.lang.NoSuchMethodError nsme) {
                String msg = nsme.getMessage();
                if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
                    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.");
                }
                throw nsme;
            } catch (Exception e) {
                failedBinding(e);
                throw new IllegalStateException("Unexpected initialization failure", e);
            }
        }
    
        private static void fixSubstituteLoggers() {
            synchronized (SUBST_FACTORY) {
                SUBST_FACTORY.postInitialization();
                for (SubstituteLogger substLogger : SUBST_FACTORY.getLoggers()) {
                    Logger logger = getLogger(substLogger.getName());
                    substLogger.setDelegate(logger);
                }
            }
    
        }
    
        static void failedBinding(Throwable t) {
            INITIALIZATION_STATE = FAILED_INITIALIZATION;
            Util.report("Failed to instantiate SLF4J LoggerFactory", t);
        }
    
        private static void replayEvents() {
            final LinkedBlockingQueue<SubstituteLoggingEvent> queue = SUBST_FACTORY.getEventQueue();
            final int queueSize = queue.size();
            int count = 0;
            final int maxDrain = 128;
            List<SubstituteLoggingEvent> eventList = new ArrayList<SubstituteLoggingEvent>(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()) {
                substLogger.log(event);
            } else {
                Util.report(loggerName);
            }
        }
    
        private static void emitSubstitutionWarning() {
            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);
        }
    
        private static void emitReplayWarning(int eventCount) {
            Util.report("A number (" + eventCount + ") of logging calls during the initialization phase have been intercepted and are");
            Util.report("now being replayed. These are subject to the filtering rules of the underlying logging system.");
            Util.report("See also " + REPLAY_URL);
        }
    
        private final static void versionSanityCheck() {
            try {
                String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
    
                boolean match = false;
                for (String aAPI_COMPATIBILITY_LIST : API_COMPATIBILITY_LIST) {
                    if (requested.startsWith(aAPI_COMPATIBILITY_LIST)) {
                        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";
    
        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>();
            try {
                ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
                Enumeration<URL> paths;
                if (loggerFactoryClassLoader == null) {
                    paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
                } else {
                    paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
                }
                while (paths.hasMoreElements()) {
                    URL path = paths.nextElement();
                    staticLoggerBinderPathSet.add(path);
                }
            } catch (IOException ioe) {
                Util.report("Error getting resources from path", ioe);
            }
            return staticLoggerBinderPathSet;
        }
    
        private static boolean isAmbiguousStaticLoggerBinderPathSet(Set<URL> binderPathSet) {
            return binderPathSet.size() > 1;
        }
    
        /**
         * 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> binderPathSet) {
            if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
                Util.report("Class path contains multiple SLF4J bindings.");
                for (URL path : binderPathSet) {
                    Util.report("Found binding in [" + path + "]");
                }
                Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
            }
        }
    
        private static boolean isAndroid() {
            String vendor = Util.safeGetSystemProperty(JAVA_VENDOR_PROPERTY);
            if (vendor == null)
                return false;
            return vendor.toLowerCase().contains("android");
        }
    
        private static void reportActualBinding(Set<URL> binderPathSet) {
            // binderPathSet can be null under Android
            if (binderPathSet != null && isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
                Util.report("Actual binding is of type [" + StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr() + "]");
            }
        }
    
        /**
         * Return a logger named according to the name parameter using the
         * statically bound {@link ILoggerFactory} instance.
         * 
         * @param name
         *            The name of the logger.
         * @return logger
         */
        public static Logger getLogger(String name) {
            ILoggerFactory iLoggerFactory = getILoggerFactory();
            return iLoggerFactory.getLogger(name);
        }
    
        /**
         * Return a logger named corresponding to the class passed as parameter,
         * using the statically bound {@link ILoggerFactory} instance.
         * 
         * <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
         * @return logger
         * 
         * 
         * @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 (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
                    Util.report(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");
                }
            }
            return logger;
        }
    
        private static boolean nonMatchingClasses(Class<?> clazz, Class<?> autoComputedCallingClass) {
            return !autoComputedCallingClass.isAssignableFrom(clazz);
        }
    
        /**
         * Return the {@link ILoggerFactory} instance in use.
         * <p/>
         * <p/>
         * ILoggerFactory instance is bound with this class at compile time.
         * 
         * @return the ILoggerFactory instance in use
         */
        public static ILoggerFactory getILoggerFactory() {
            if (INITIALIZATION_STATE == UNINITIALIZED) {
                synchronized (LoggerFactory.class) {
                    if (INITIALIZATION_STATE == UNINITIALIZED) {
                        INITIALIZATION_STATE = ONGOING_INITIALIZATION;
                        performInitialization();
                    }
                }
            }
            switch (INITIALIZATION_STATE) {
            case SUCCESSFUL_INITIALIZATION:
                return StaticLoggerBinder.getSingleton().getLoggerFactory();
            case NOP_FALLBACK_INITIALIZATION:
                return NOP_FALLBACK_FACTORY;
            case FAILED_INITIALIZATION:
                throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
            case ONGOING_INITIALIZATION:
                // support re-entrant behavior.
                // See also http://jira.qos.ch/browse/SLF4J-97
                return SUBST_FACTORY;
            }
            throw new IllegalStateException("Unreachable code");
        }
    }
    View Code

    这里看下获取getLogger函数的源码,我们发现LoggerFactory.getLogger()首先获取一个ILoggerFactory接口,然后使用该接口获取具体的Logger。获取ILoggerFactory的时候用到了一个StaticLoggerBinder类,仔细研究我们会发现StaticLoggerBinder这个类并不是slf4j-api这个包中的类,而是实现日志框架里的类,所有的实现框架都需要定义该类,提供具体的Logger实现。

    接下来具体分析日志实现框架 logback和log4j2。

  • 相关阅读:
    关于区间 $mex$ 的几种做法
    Selenium+TestNG+Maven+Jenkins+SVN(转载)
    Selenium+Java的TestNG测试报告优化
    使用Log4J收集日志
    Selenium+TestNG+Maven(2)
    自动化测试用例设计实例
    自动化测试用例的编写规则
    selenium 总结篇,常见方法和页面元素的操作
    Selenium Webdriver元素定位的八种常用方式(转载)
    selenium+java利用AutoIT实现文件上传
  • 原文地址:https://www.cnblogs.com/pjfmeng/p/11272068.html
Copyright © 2020-2023  润新知