zoukankan      html  css  js  c++  java
  • logger(一)、springBoot的日志源码查看(LogBack + slf4j)——对接slf4j

    查看源码的建议:

    1、源代码的类虽然很多,但是一般是有一个清晰的结构的(当然有些项目确实结构混乱,东一撮西一撮的,那没关系,不读就是了)。所以在深入到具体的细节之前,应该先把整个代码的结构梳理清楚,再一块一块地往下读。梳理清楚了脉络和结构,可以直接跳过不想深究的细节,只要知道它是干什么的就行了,当用到的时候再来看

    2、源代码的类虽然很多,但是一般会有一个入口。比如hibernate的SessionFactory,spring的ApplicationContext,logback的LoggerFactory。从入口进去,一点点地读,必要的时候使用debug功能跟着走一走,相信会清晰很多

    以前我就是进了误区,哎

    slf4j最关键的是2个接口,和一个入口类。搞清楚了这3个,对slf4j就会比较清楚了。

    最关键的2个接口,分别是Logger和ILoggerFactory。

    最关键的入口类,是LoggerFactory

    所有的具体实现框架,一定会实现Logger接口和ILoggerFactory接口。前者实际记录日志,后者用来提供Logger,重要性不言自明。而LoggerFactory类,则是前面说过的入口类。

    private final static Logger LOGGER = LoggerFactory.getLogger(RequestController.class);

    不管实现框架是什么,要获取Logger对象,都是通过这个类的getLogger()方法,所以这个类也非常重要。具体实现框架和slf4j的对接,就是通过这个类

     如图,每个日志框架都需要实现ILoggerFactory接口,来说明自己是怎么提供Logger的。像log4j、logback能够提供父子层级关系的Logger,就是在ILoggerFactory的实现类里实现的。同时,它们也需要实现Logger接口,以完成记录日志。为啥log4j和logback可以一个Logger对应多个Appender?这就要去分析它们的Logger实现类。

    slf4j自带的NOPLoggerFactory,实现了ILoggerFactory,其getLogger()方法很简单,就是返回一个NOPLogger

    public class NOPLoggerFactory implements ILoggerFactory {
    
        public NOPLoggerFactory() {
            // nothing to do
        }
    
        public Logger getLogger(String name) {
            return NOPLogger.NOP_LOGGER;
        }
    
    }

    NOPLogger实现了Logger接口,它就更简单,如同它的名字一样:什么都不做

    public class NOPLogger extends MarkerIgnoringBase {
    
        private static final long serialVersionUID = -517220405410904473L;
    
        /**
         * The unique instance of NOPLogger.
         */
        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.
         */
        protected NOPLogger() {
        }
    
        /**
         * Always returns the string value "NOP".
         */
        public String getName() {
            return "NOP";
        }
    
        /**
         * Always returns false.
         * @return always false
         */
        final public boolean isTraceEnabled() {
            return false;
        }
    
        /** A NOP implementation. */
        final public void trace(String msg) {
            // NOP
        }
    
        /** A NOP implementation.  */
        final public void trace(String format, Object arg) {
            // NOP
        }
    
        /** A NOP implementation.  */
        public final void trace(String format, Object arg1, Object arg2) {
            // NOP
        }
    
        /** A NOP implementation.  */
        public final void trace(String format, Object... argArray) {
            // NOP
        }
    
        /** A NOP implementation. */
        final public void trace(String msg, Throwable t) {
            // NOP
        }
    
        /**
         * Always returns false.
         * @return always false
         */
        final public boolean isDebugEnabled() {
            return false;
        }
    
        /** A NOP implementation. */
        final public void debug(String msg) {
            // NOP
        }
    
        /** A NOP implementation.  */
        final public void debug(String format, Object arg) {
            // NOP
        }
    
        /** A NOP implementation.  */
        public final void debug(String format, Object arg1, Object arg2) {
            // NOP
        }
    
        /** A NOP implementation.  */
        public final void debug(String format, Object... argArray) {
            // NOP
        }
    
        /** A NOP implementation. */
        final public void debug(String msg, Throwable t) {
            // NOP
        }
    
        /**
         * Always returns false.
         * @return always false
         */
        final public boolean isInfoEnabled() {
            // NOP
            return false;
        }
    
        /** A NOP implementation. */
        final public void info(String msg) {
            // NOP
        }
    
        /** A NOP implementation. */
        final public void info(String format, Object arg1) {
            // NOP
        }
    
        /** A NOP implementation. */
        final public void info(String format, Object arg1, Object arg2) {
            // NOP
        }
    
        /** A NOP implementation.  */
        public final void info(String format, Object... argArray) {
            // NOP
        }
    
        /** A NOP implementation. */
        final public void info(String msg, Throwable t) {
            // NOP
        }
    
        /**
         * Always returns false.
         * @return always false
         */
        final public boolean isWarnEnabled() {
            return false;
        }
    
        /** A NOP implementation. */
        final public void warn(String msg) {
            // NOP
        }
    
        /** A NOP implementation. */
        final public void warn(String format, Object arg1) {
            // NOP
        }
    
        /** A NOP implementation. */
        final public void warn(String format, Object arg1, Object arg2) {
            // NOP
        }
    
        /** A NOP implementation.  */
        public final void warn(String format, Object... argArray) {
            // NOP
        }
    
        /** A NOP implementation. */
        final public void warn(String msg, Throwable t) {
            // NOP
        }
    
        /** A NOP implementation. */
        final public boolean isErrorEnabled() {
            return false;
        }
    
        /** A NOP implementation. */
        final public void error(String msg) {
            // NOP
        }
    
        /** A NOP implementation. */
        final public void error(String format, Object arg1) {
            // NOP
        }
    
        /** A NOP implementation. */
        final public void error(String format, Object arg1, Object arg2) {
            // NOP
        }
    
        /** A NOP implementation.  */
        public final void error(String format, Object... argArray) {
            // NOP
        }
    
        /** A NOP implementation. */
        final public void error(String msg, Throwable t) {
            // NOP
        }
    }
    NOPLogger

    下面就是关键的LoggerFactory类了,这个入口类是怎么提供Logger的呢?

    首先它调用一个public的getILoggerFactory()方法,获得一个ILoggerFactory的实现类,然后委托这个实现类提供一个Logger实现类。就是这样把获取实际Logger的工作,委托给具体的日志框架上,比如log4j、logback、common-logging等

       /**
         * 返回正在使用的 {@link ILoggerFactory} 实例。
         * ILoggerFactory 实例在编译时与此类绑定。
         * @return 正在使用的 ILoggerFactory 实例
         */
        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");
        }

    为了获得ILoggerFactory的实现类,它首先要初始化,它会委托NOPLoggerFactory来提供Logger,最终结果就是一个没实现的NOPLogger。如果初始化成功了,就是和日志框架对接成功,就委托日志框架的ILoggerFactory来提供Logger的实现。

        private final static void performInitialization() {
            bind();
            if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
                versionSanityCheck();
            }
        }

    然后是关键的bind()方法,绑定实现框架。最后再检查一下版本完整性。这段代码提到了一个最关键的StaticLoggerBinder类,检查是否有这个类存在,检查是否有这个是否是Android 应用,以及这个类有没有getSingleton()方法,如果有,就视为绑定成功(起码 self4j的实现导入了)。其实这个类还必须有getLoggerFactory()方法,否则虽然绑定成功,但是到了运行期,一样会抛出NoSuchMethodException。我认为这里是slf4j设计不好的地方,应该在bind()方法里,就检查一下StaticLoggerBinder有没有实现getLoggerFactory()方法。

        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);
            } 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);
            } finally {
                postBindCleanUp();
            }
        }

    这个StaticLoggerBinder类,就是具体实现框架和slf4j框架对接的接口。

     这就介绍完了logback是怎么和slf4j对接的。不止是logback,任何日志框架,一定都是通过自己的StaticLoggerBinder类,来和slf4j对接。这个类的实现,在不同的框架中不同,比如后面会说到,在logback中,这个类被设计为或者简单的返回一个默认的LoggerContext(LoggerContext是ILoggerFactory在logback中的实现),或者通过ContextSelector(logback特有的)来选择一个LoggerContext并返回。

    大家可能会有一个疑问:在LoggerFactory类的bind()方法里,依赖了StaticLoggerBinder类,但是slf4j框架里又没有这个类,那么框架一开始是怎么编译通过并发布成jar包的呢?

    作者一开始的时候,是以类似图里的包结构组织代码的。为了编译通过,作者写了一个StaticLoggerBinder类

    private final ILoggerFactory loggerFactory;  
        
      private StaticLoggerBinder() {  
        loggerFactory = new NOPLoggerFactory();  
      }  
        
      public ILoggerFactory getLoggerFactory() {  
        return loggerFactory;  
      } 

    然后,作者把org.slf4j、org.slf4j.helpers、org.slf4j.spi这3个package发布为slf4j-api.jar。把org.slf4j.impl发布为slf4j-nop.jar

    用户实际使用的时候,必须要引入slf4j-api.jar和具体实现框架,比如log4j.jar,以及对接用的slf4j-log4j.jar,不需要引入slf4j-nop.jar

    博客来源:

    由于使用的版本较高,本文稍有不同,新版本中也见到很多新的编程思想

     
    读logback源码系列文章(一)——对接slf4j
     
    读logback源码系列文章(二)——提供ILoggerFactory
     
    读logback源码系列文章(三)——创建Logger
     
    读logback源码系列文章(四)——记录日志
     
    读logback源码系列文章(五)——Appender
     
    读logback源码系列文章(六)——ContextInitializer
     
    读logback源码系列文章(七)——配置的实际工作类Action
     
    读logback源码系列文章(八)——记录日志的实际工作类Encoder
  • 相关阅读:
    github 访问速度慢 的解决+个人理解
    phpstorm 编辑器进行自动的注释添加
    输入框只能输入数字(最好用)
    数组去重(根据对象属性去重)
    mac安装Homebrew和 tree
    vue中 .sync 的作用
    打包 vue 组件为 webcomponent
    Android开发之SharedPreferences
    Android开发之SharedPreferences扩展以及File
    Android 开发之SQLite基础
  • 原文地址:https://www.cnblogs.com/lzghyh/p/14880309.html
Copyright © 2011-2022 走看看