Spring源码日志分析

1.jdk自带的java util logging,即jul

日志输出格式:

从源码可以看到jul的Logger是个具体的类


2.apache的commons-logging,即jcl

输出结果,没有任何配置

从表面看起来是使用的jul,实际上是什么样的,查看源码Log是一个接口,而不是具体的实现类



看LogFactory.getLog源码,实际上是有一个数组,里面放了4种日志打印方式


然后通过反射获取数组里的对象

  private Log createLogFromClass(String logAdapterClassName, String logCategory, boolean affectState) throws LogConfigurationException {
        if (isDiagnosticsEnabled()) {
            this.logDiagnostic("Attempting to instantiate '" + logAdapterClassName + "'");
        }

        Object[] params = new Object[]{logCategory};
        Log logAdapter = null;
        Constructor constructor = null;
        Class logAdapterClass = null;
        ClassLoader currentCL = this.getBaseClassLoader();

        while(true) {
            this.logDiagnostic("Trying to load '" + logAdapterClassName + "' from classloader " + LogFactory.objectId(currentCL));

            String resourceName;
            try {
                if (isDiagnosticsEnabled()) {
                    resourceName = logAdapterClassName.replace('.', '/') + ".class";
                    URL url;
                    if (currentCL != null) {
                        url = currentCL.getResource(resourceName);
                    } else {
                        url = ClassLoader.getSystemResource(resourceName + ".class");
                    }

                    if (url == null) {
                        this.logDiagnostic("Class '" + logAdapterClassName + "' [" + resourceName + "] cannot be found.");
                    } else {
                        this.logDiagnostic("Class '" + logAdapterClassName + "' was found at '" + url + "'");
                    }
                }

                Class c;
                try {
                    c = Class.forName(logAdapterClassName, true, currentCL);
                } catch (ClassNotFoundException var15) {
                    String msg = var15.getMessage();
                    this.logDiagnostic("The log adapter '" + logAdapterClassName + "' is not available via classloader " + LogFactory.objectId(currentCL) + ": " + msg.trim());

                    try {
                        c = Class.forName(logAdapterClassName);
                    } catch (ClassNotFoundException var14) {
                        msg = var14.getMessage();
                        this.logDiagnostic("The log adapter '" + logAdapterClassName + "' is not available via the LogFactoryImpl class classloader: " + msg.trim());
                        break;
                    }
                }

                constructor = c.getConstructor(this.logConstructorSignature);
                Object o = constructor.newInstance(params);
                if (o instanceof Log) {
                    logAdapterClass = c;
                    logAdapter = (Log)o;
                    break;
                }

                this.handleFlawedHierarchy(currentCL, c);
            } catch (NoClassDefFoundError var16) {
                resourceName = var16.getMessage();
                this.logDiagnostic("The log adapter '" + logAdapterClassName + "' is missing dependencies when loaded via classloader " + LogFactory.objectId(currentCL) + ": " + resourceName.trim());
                break;
            } catch (ExceptionInInitializerError var17) {
                resourceName = var17.getMessage();
                this.logDiagnostic("The log adapter '" + logAdapterClassName + "' is unable to initialize itself when loaded via classloader " + LogFactory.objectId(currentCL) + ": " + resourceName.trim());
                break;
            } catch (LogConfigurationException var18) {
                throw var18;
            } catch (Throwable var19) {
                LogFactory.handleThrowable(var19);
                this.handleFlawedDiscovery(logAdapterClassName, currentCL, var19);
            }

            if (currentCL == null) {
                break;
            }

            currentCL = this.getParentClassLoader(currentCL);
        }

        if (logAdapterClass != null && affectState) {
            this.logClassName = logAdapterClassName;
            this.logConstructor = constructor;

            try {
                this.logMethod = logAdapterClass.getMethod("setLogFactory", this.logMethodSignature);
                this.logDiagnostic("Found method setLogFactory(LogFactory) in '" + logAdapterClassName + "'");
            } catch (Throwable var13) {
                LogFactory.handleThrowable(var13);
                this.logMethod = null;
                this.logDiagnostic("[INFO] '" + logAdapterClassName + "' from classloader " + LogFactory.objectId(currentCL) + " does not declare optional method " + "setLogFactory(LogFactory)");
            }

            this.logDiagnostic("Log adapter '" + logAdapterClassName + "' from classloader " + LogFactory.objectId(logAdapterClass.getClassLoader()) + " has been selected for use.");
        }

        return logAdapter;
    }

当能找到log4j的情况,则使用log4j打印,加入log4j的jar包和log4j.properties

log4j.rootLogger=INFO,stdout,logfile
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n

log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=D:/test.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n

再输出结果,则使用的时loj4j打印

根据源码分析出:使用jcl打印日志的时,当项目中有log4j的jar包和配置的时候,优先使用log4j,没有log4j的时候使用jul(jdk14或jdk13),jdk13目前也没用了,最后是Simple Log(目前基本已不使用)

jcl的缺陷,扩展性差,只有数组里提供的四种日志类型,spring5.0已下则依赖的是apache的commons-logging


而spring5.0以上在不再使用apache原生的commons-logging,而是自己扩展了jcl,apache原生的commons-logging已停止更新不建议使用


3.spring的jcl

来源于spring的jcl的jar包

spring5以上支持的日志顺序log4j2->slf4j_lal(1.3版本及以后)->slf4j(1.3版本之前)->默认jul,当匹配不到时使用默认的jul


可以发现spring5是不支持log4j,支持log4j2的,那为什么在我们的项目中会看到可以使用log4j呢?一种情况使用的是spring5已下的版本,如果是spring5是因为我们使用的slf4j然后通过绑定器实现使用log4j打印日志。

增加slf4j的jar包slf4j-api和slf4j与log4j的的绑定器slf4j-log4j12(log4j 1.x的版本)

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

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

发现可以正常使用log4j的方式打印


去掉绑定器slf4j-log4j12,则报错



以上分析可知:从日志扩展角度和移植行,建议使用slf4j接口,它为每个日志的实现类都对应了一个绑定器,换日志打印时比较方便,扩展性好,不建议在代码中直接使日志具体实现类。如log4j,jul,也不建议使用apache原生jcl,已停止更新且扩展性差

还有一个问题是slf4j log4j死循环的问题:
项目加入slf4j-log4j12的jar包,又加入log4j-over-slf4j的jar包,会导致死循环

究其本质的原因是: log4j代理给slf4j --> slf4j绑定到log4j,所以循环于此,所以会报错

猜你喜欢

转载自blog.csdn.net/dhj199181/article/details/108987807
今日推荐