分析spring4和spring5日志中的不同

  日志在工作中起到关键作用,我们经常使用它来打印关键信息,方便分析,或者是输出错误信息,用于bug排查,spring中同样使用了日志进行信息的输出,但是spring4和spring5之间的日志又有些不同,接下来我们就进行一些分析。

1. 各种日志技术简述:  

    log4j,jul,jcl,log4j2,slf4j

    我们先把他们展示出来,以免引用错误。

  1.1 log4j

   使用log4j需要引入log4j的配置文件log4j.properties,内容简配一下:

log4j.rootLogger = info,stdout
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 = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

  pom文件中只引入log4j所需要的jar包

  

  程序也写的简单一点,输出日志就行:

  

   结果:

  

  日志是输出的。

 1.2JUL

  JUL 是jdk自带的,所以pom文件中不需要引入任何包,直接撸代码:

  

   结果:

  

   日志输出格式我们不再做改变,记住log4j和jul的日志输出格式,后面有用到。

 1.3:JCL

  pom中引入所需要的包:

       

 

 

   撸代码:

  

  结果:

  

   呜呼,这个日志输出是不是很眼熟呀,长得很像上面JUL打印出来的日志,难道他们之间有什么不可描述的事情?所以最帅的我准备打开JCL的源码一探究竟。

  从上面代码中的LogFactory的getLog方法下手,进去:

  

  木得意思,就一行代码,那就再深一点,进入getInstance()中,

  

   第一行代码看着像是拿到了log,不过老哥我已经帮大家踩过坑了,debug走了一遍,第一行代码执行后,Log 依然是null,所以我们继续往下走,进入到

this.newInstance()方法中,有点长,就酱紫:

protected Log newInstance(String name) throws LogConfigurationException {
        Log instance = null;

        try {
            Object[] params;
            if (this.logConstructor == null) {
                instance = this.discoverLogImplementation(name);
            } else {
                params = new Object[]{name};
                instance = (Log)this.logConstructor.newInstance(params);
            }

            if (this.logMethod != null) {
                params = new Object[]{this};
                this.logMethod.invoke(instance, params);
            }

            return instance;
        } catch (LogConfigurationException var5) {
            throw var5;
        } catch (InvocationTargetException var6) {
            Throwable c = var6.getTargetException();
            if (c != null) {
                throw new LogConfigurationException(c);
            } else {
                throw new LogConfigurationException(var6);
            }
        } catch (Throwable var7) {
            throw new LogConfigurationException(var7);
        }
    }

  这段代码差一点就比我的长处还长呢,所以我又帮大家debug了一遍,在第一个if判断 之后执行的这个方法 this.discoverLogImplementation(name);就是我们要找的,再深入,在

discoverLogImplementation()这个方法中,我找到了这段代码:

  

  result属性就是我们需要返回的Log,循环条件中有一个classesToDiscover,并且还把他的值传给了this.createLogFromClass()方法,点击看看他是什么,

String[] classesToDiscover = new String[]{"org.apache.commons.logging.impl.Log4JLogger", 
                          "org.apache.commons.logging.impl.Jdk14Logger",
                          "org.apache.commons.logging.impl.Jdk13LumberjackLogger",
                          "org.apache.commons.logging.impl.SimpleLog"};

  这个数组里面找到了 org.apache.commons.logging.impl.Jdk14Logger,这不就是我们的JUL 吗?再想到刚刚的for循环,我们很容易理解,JCL就是循环这个数组,一次获取Log,知道Log 不为null为止,为了验证我们的猜想,

我们可以引入log4j的jar包和配置文件,其他代码不做修改,这样按照数组顺序,应该打印出来log4j的日志。

      

 

果然,在有log4j的情况下,JCL按照刚刚那个数组,顺序加载log,知道Log不为null。

  由此可以看出,JCL起到的是一个中间商赚差价的作用,在有log4j的时候使用log4j,否则使用JUL

2 Sring4 的日志体系

  上面说了那么多,只是介绍了一部分日志技术,接下来我们开始分别分析spring4和spring5的日志体系。

  2.1 构建spring4项目

  采用java+注解的方式快速构建,pom中只引入spring-context包

  

  运行下面的代码,可以看到有日志输出

  

 

   

 

   找到打印日志的地方,debug模式下,查看输出日志的Log是什么log

  

 

  可以看出是jdk14Logger,这个在JCL中说过,这个指的是JUL,也就是说在默认spring日志体系下,采用的是JUL,

  接下来,我们按照之前的方法引入log4j,debug运行上面的程序,再次查看日志类型

  

 

  额,这次在增加log4j jar包和配置文件的情况下,spring4有使用了log4j,这么像JCL呢,木错,让我们在idea中打开spring4的日志依赖结构:

  

  common-logging 这不就是JCL使用到的包吗,可以看出,Spring4使用的是原生的JCL,所以在有log4j的时候使用log4j打印日志,没有的时候使用JUL打印日志。

3.Spring5日志体系

  线上依赖结构图:

  

 

   答题结构没变,只是原来common-logging ,换成了spring-jcl,看名字就知道是spring自造的包,jcl,更是标注了,它使用的是JCL日志体系。

  所以还是看源码吧。

  按照之前的经验,我们只用debug找到spring内部一个Log,看看他的产生方式和类型。这次我给大家找了AbstractApplicationContext里面找到产生Log的地方

  

 

  进入这个方法的getLog()中,一直深入,不要怜惜spring,找到LogAdapter中的createLog()方法

  

 

   可以看出来spring5中对日志的生产,不在像原生JCL中那样使用一个数组,然后进行循环产生,这里用到的是Switch case,这个关键字段LogApi又是在哪一部分赋值的呢?看图

  

 

   Duang ,没错是在静态代码块中赋的值,为了验证,我们准备用其中提到的log4j2验证(注意:log4j不行,因为这里的switch没有log4j选项),首先我们准备log4j2的配置文件

  

<Configuration status="WARN">

<Appenders>

<Console name="Console" target="SYSTEM_OUT">

<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>

</Console>

</Appenders>

<Loggers>

<Root level="debug">

<AppenderRef ref="Console"/>

</Root>

</Loggers>

</Configuration>

  然后准备pom

  

   代码还是这一行,直接运行:

           

  结果有日志打印出来了

  

 

   所以,在spring5中,依然使用的是JCL,但是不是原生的,是经过改造的JCL,默认使用的是JUL,而原生JCL中默认使用的是log4j.

 

  好了,就酱紫,求轻拍,大家中秋快乐。

 

 

  

  

 

 

 

  

 

  

 

 

      

      

      

posted @ 2019-09-12 16:31  矜持的折返跑  阅读(4018)  评论(0编辑  收藏  举报