java日志体系分析

写在前面

Java中日志框架种类繁多,我也是最近在学习mybatis过程中对日志的使用产生了困惑,所以这里个人做下总结,希望能帮助到和我有一样困惑的朋友,下面我们就开始吧!

1:java日志的发展历史

Java中的第一个日志框架是apache提出来的log4j,如下:
java日志体系分析_第1张图片

后来呢,sun公司认为自己作为java的开发主体,日志肯定是需要自己来给出方案的,为了打压log4j,于是在jdk4中制定了JULjava util logging,在rt.jar的java.util.logging包中,如下:
java日志体系分析_第2张图片
与此同时,又有一些其他的日志框架也被提了出来,比如simplelog,此时java日志体系如下图:
java日志体系分析_第3张图片
但是众多的毫无关系的日志框架,势必在我们的项目中造成混乱,比如项目中使用的是log4j,依赖的库A使用的是JUL,依赖的库B使用的是simplelog,这样一个项目中各种日志框架混杂,项目会越来越不好维护,越来越混乱,此时这个问题就亟需解决。这个时候,apache社区提出了commons-logging即JCL的日志抽象来适配各种日志框架,此时java日志体系就变成下图:
java日志体系分析_第4张图片
之后呢,log4j的作者提出了一个对标JCL的日志抽象适配接口SLF4J(simple logging facade for Java),不仅如此还直接开发一个基于SLF4J的日志框架实现logback,与此同时呢,又继续维护并升级了log4j,形成了其升级版本log4j2,此时Java日志体系变为下图红色框为本部分新介绍的
java日志体系分析_第5张图片
这个图也是目前java日志体系发展的现状了。

2:各种日志的使用实例

2.1:log4j

  • pom
<dependency>
    <groupId>log4jgroupId>
    <artifactId>log4jartifactId>
    <version>1.2.17version>
dependency>
  • log4j.properties
#使某个功能的日志单独输出到指定的日志文件
log4j.logger.saveUserLog=INFO,saveUserLog
#该配置就是让job的日志只输出到自己指定的日志文件中,表示Logger不会在父Logger的appender里输出,默认为true。
log4j.additivity.saveUserLog=false
log4j.appender.saveUserLog=org.apache.log4j.DailyRollingFileAppender
log4j.appender.saveUserLog.File=d:\\test\\testlog4j\\saveUserLog.log
log4j.appender.saveUserLog.DatePattern=yyyy-MM-dd
log4j.appender.saveUserLog.Append=true
log4j.appender.saveUserLog.layout=org.apache.log4j.PatternLayout
log4j.appender.saveUserLog.layout.ConversionPattern=%m%n
#log4j.appender.error.encoding=UTF-8

注意,在该配置中只配置了名称为saveUserLog日志的配置。

  • 测试代码
public class TestLog4j {

    public static void main(String[] args) {
        // 使用默认的log4j.rootLogger=info,file,error的配置,即输出日志到名称为file,error的日志配置中
        final Logger logger = Logger.getLogger(TestLog4j.class);
        /*
        #使某个功能的日志单独输出到指定的日志文件
        log4j.logger.saveUserLog=INFO,saveUserLog
        #该配置就是让job的日志只输出到自己指定的日志文件中,表示Logger不会在父Logger的appender里输出,默认为true。
        log4j.additivity.saveUserLog=false
        log4j.appender.saveUserLog=org.apache.log4j.DailyRollingFileAppender
        log4j.appender.saveUserLog.File=d:\\test\\testlog4j\\saveUserLog.log
        log4j.appender.saveUserLog.DatePattern=yyyy-MM-dd
        log4j.appender.saveUserLog.Append=true
        log4j.appender.saveUserLog.layout=org.apache.log4j.PatternLayout
        log4j.appender.saveUserLog.layout.ConversionPattern=%m%n
         */
        // 日志名称saveUserLog进行了单独的配置,因此不会使用log4j.rootLogger=info,file,error的配置
        final Logger saveUserLog = Logger.getLogger("saveUserLog");
        if (logger.isDebugEnabled()) {
            logger.debug("debug");
        }
        logger.info("info");
        logger.error("error");
        saveUserLog.info("张三,男,26岁,北京大学,2018-05-19,学霸");
    }
}

我们运行,输出如下:
java日志体系分析_第6张图片
注意提示log4j:WARN No appenders could be found for logger (dongshi.daddy.TestLog4j).,对应的代码是final Logger logger = Logger.getLogger(TestLog4j.class),但是代码final Logger saveUserLog = Logger.getLogger("saveUserLog")我们是进行了配置的,因此会成功生成日志文件,如下:
java日志体系分析_第7张图片
要消除警告我们可以有如下的两种方式,第一种是定义一个名称为dongshi.daddy.TestLog4j的appener,如下:

# 定义名字为dongshi.daddy.TestLog4j的logger
log4j.logger.dongshi.daddy.TestLog4j=INFO,dongshi.daddy.TestLog4j
#该配置就是让job的日志只输出到自己指定的日志文件中,表示Logger不会在父Logger的appender里输出,默认为true。
log4j.additivity.dongshi.daddy.TestLog4j=false
log4j.appender.dongshi.daddy.TestLog4j=org.apache.log4j.DailyRollingFileAppender
log4j.appender.dongshi.daddy.TestLog4j.File=d:\\test\\testlog4j\\dongshi.daddy.TestLog4j.log
log4j.appender.dongshi.daddy.TestLog4j.DatePattern=yyyy-MM-dd
log4j.appender.dongshi.daddy.TestLog4j.Append=true
log4j.appender.dongshi.daddy.TestLog4j.layout=org.apache.log4j.PatternLayout
log4j.appender.dongshi.daddy.TestLog4j.layout.ConversionPattern=%m%n

在运行查看:
在这里插入图片描述
此时警告就消失了,文件生成如下:
java日志体系分析_第8张图片
但是呢,这种配置方式有个问题就是,项目中logger的名称有很多很多,几乎一个类就有一个,如果每个都这样配置,那就太麻烦了,因此,可以采用第二种方式,即配置rootLogger,这是一种统配appener的配置方式,首先定义默认的日志级别,logger的名称,然后配置每个logger名称的详细配置,首先配置如下:

log4j.rootLogger=info,myfile,myerror

代表,默认的日志级别是info,默认的日志名字是myfile,myerror,然后分别配置日志的文件位置,输出格式等信息:


#配置error日志信息输出目的地Appender
#定义名为error的输出端是每天产生一个日志文件
log4j.appender.myfile=org.apache.log4j.DailyRollingFileAppender
#指定日志信息的最低输出级别位ERROR,默认为DEBUG。
log4j.appender.myfile.Threshold=ERROR
#myfile/log4j/error.log文件中
log4j.appender.myfile.File=d:\\test\\testlog4j\\myfile.log
#指定按月来滚动日志文件
log4j.appender.myfile.DatePattern=yyyy-MM
#配置日志信息的格式(布局)Layout是可以灵活地指定布局模式
log4j.appender.myfile.layout=org.apache.log4j.PatternLayout
#格式化日志,Log4j采用类似C语言中的printf函数的打印格式格式化日志信息
log4j.appender.myfile.layout.ConversionPattern=[%d{yyyy-MM-ddHH:mm:ss}][%-5p][jpm-%c{1}-%M(%L)]-%m%n
#指定输出信息的编码
log4j.appender.myfile.encoding=UTF-8

#配置error日志信息输出目的地Appender
#定义名为error的输出端是每天产生一个日志文件
log4j.appender.myerror=org.apache.log4j.DailyRollingFileAppender
#指定日志信息的最低输出级别位ERROR,默认为DEBUG。
log4j.appender.myerror.Threshold=ERROR
#myfile/log4j/error.log文件中
log4j.appender.myerror.File=d:\\test\\testlog4j\\myerror.log
#指定按月来滚动日志文件
log4j.appender.myerror.DatePattern=yyyy-MM
#配置日志信息的格式(布局)Layout是可以灵活地指定布局模式
log4j.appender.myerror.layout=org.apache.log4j.PatternLayout
#格式化日志,Log4j采用类似C语言中的printf函数的打印格式格式化日志信息
log4j.appender.myerror.layout.ConversionPattern=[%d{yyyy-MM-ddHH:mm:ss}][%-5p][jpm-%c{1}-%M(%L)]-%m%n
#指定输出信息的编码
log4j.appender.myerror.encoding=UTF-8

然后运行:
在这里插入图片描述
各种警告也都消失了,生成日志文件如下:
java日志体系分析_第9张图片
截止到这里我们其实并没有引入日志门面,但是在项目中一般都是面向日志门面接口来编写日志的,这里我们引入日志门面slf4j,修改如下:

  • 修改pom
<dependencies>
   <dependency>
       <groupId>log4jgroupId>
       <artifactId>log4jartifactId>
       <version>1.2.17version>
   dependency>
   
   <dependency>
       <groupId>org.slf4jgroupId>
       <artifactId>slf4j-log4j12artifactId>
       <version>1.7.25version>
   dependency>
   <dependency>
       <groupId>org.slf4jgroupId>
       <artifactId>slf4j-apiartifactId>
       <version>1.7.25version>
   dependency>
dependencies>
  • 修改测试代码
org.slf4j.Logger saveUserLog = org.slf4j.LoggerFactory.getLogger("saveUserLog");
saveUserLog.info("slf4j info ...");
saveUserLog.error("slf4j error ...");

注意使用了slf4j的接口API,内部会动态的查找实现类,这里最终查找到的就是log4j,运行:
java日志体系分析_第10张图片

2.2:logback

本例直接结合slf4j日志门面接口一起使用,logback作为slf4j的亲儿子,使用起来更加无缝衔接。

  • pom
<dependencies>
    
    <dependency>
        <groupId>org.slf4jgroupId>
        <artifactId>slf4j-apiartifactId>
        <version>1.7.25version>
    dependency>

    
    <dependency>
        <groupId>ch.qos.logbackgroupId>
        <artifactId>logback-coreartifactId>
        <version>1.2.3version>
    dependency>
    <dependency>
        <groupId>ch.qos.logbackgroupId>
        <artifactId>logback-classicartifactId>
        <version>1.2.3version>
    dependency>
    <dependency>
        <groupId>ch.qos.logbackgroupId>
        <artifactId>logback-accessartifactId>
        <version>1.2.3version>
    dependency>
dependencies>
  • logback.xml

<configuration status="WARN" monitorInterval="30">
    
    <appender name="STDOUT1" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %npattern>
        encoder>
    appender>

    
    <root level="DEBUG">
        <appender-ref ref="STDOUT1"/>
    root>
configuration>
  • 测试代码
public class TestLogback {

    public static void main(String[] args) {
        final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger(TestLogback.class);
        LOGGER.debug("print debug log.");
        LOGGER.info("print info log.");
        LOGGER.error("print error log.");
    }
}

运行:

157  [main] DEBUG dongshi.daddy.TestLogback - print debug log. 
160  [main] INFO  dongshi.daddy.TestLogback - print info log. 
160  [main] ERROR dongshi.daddy.TestLogback - print error log. 

Process finished with exit code 0

此时只配置了输出到控制台,接下来增加输出到文件中。
java日志体系分析_第11张图片
红框中的为增加内容,这里再贴下,方便看到的朋友测试:

<appender name="FILE1" class="ch.qos.logback.core.FileAppender">
     <file>D:\\test\\testslf4jlogbck\\testlogback.logfile>
     <append>trueappend>
     <encoder>
       <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%npattern>
     encoder>
appender>

<appender-ref ref="FILE1"/>

运行查看生成的文件:
java日志体系分析_第12张图片

2.3:log4j2

log4j2是log4j的升级版本,同时支持日志门面JCL(Java commons logging),slf4j(simple logging facade for Java),下面看下使用。

一直以为log4j2是有一个专门的xxx.xxx.log4j2的pom,但其实log4j2就是log4j的2.x版本,汗!!!

  • pom
<dependencies>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.8.2</version>
    </dependency>

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.8.2</version>
    </dependency>
</dependencies>
  • 配置文件log4j2.xml

<configuration status="WARN" monitorInterval="30">
    
    <appenders>
        
        <console name="Console" target="SYSTEM_OUT">
            
            <PatternLayout
                    pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %logger{36} - %msg%n" />
        console>

        
        <File name="log" fileName="D:\\test\\testlog4j2\\log.log" append="true">
            <PatternLayout
                    pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %logger{36} - %msg%n" />
        File>
    appenders>

    
    <loggers>
        
        <logger name="org.springframework" level="INFO">logger>
        <logger name="org.mybatis" level="INFO">logger>
        <root level="INFO">
            
            <appender-ref ref="Console" />
            
            <appender-ref ref="log" />
        root>
    loggers>

configuration>
  • 测试代码
public class TestLog4j2 {

    public static void main(String[] args) {
        final Logger LOGGER = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
        LOGGER.debug("TestLog4j2 debug log.");
        LOGGER.info("TestLog4j2 info log.");
        LOGGER.error("TestLog4j2 error log.");
    }

}

运行:在这里插入图片描述
java日志体系分析_第13张图片

2.4:JCL(java common logging) + Log4j

本部分使用JCLjava common logging作为日志门面,使用log4j作为日志实现,具体如下。

  • pom
<dependencies>
    <dependency>
        <groupId>commons-logginggroupId>
        <artifactId>commons-loggingartifactId>
        <version>1.2version>
    dependency>

    <dependency>
        <groupId>log4jgroupId>
        <artifactId>log4jartifactId>
        <version>1.2.17version>
    dependency>
dependencies>
  • common-logging.properties
    设置实现类,不过没有该文件应该也是可以的,但是一定要有log4j.properties文件。
org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
  • log4j.properties
log4j.rootLogger=debug,myfile,myerror,myconsole

log4j.appender.myconsole=org.apache.log4j.ConsoleAppender
#指定日志信息的最低输出级别位ERROR,默认为DEBUG。
log4j.appender.myconsole.Threshold=DEBUG
#配置日志信息的格式(布局)Layout是可以灵活地指定布局模式
log4j.appender.myconsole.layout=org.apache.log4j.PatternLayout
#格式化日志,Log4j采用类似C语言中的printf函数的打印格式格式化日志信息
log4j.appender.myconsole.layout.ConversionPattern=[%d{yyyy-MM-ddHH:mm:ss}][%-5p][jpm-%c{1}-%M(%L)]-%m%n
#指定输出信息的编码
log4j.appender.myconsole.encoding=UTF-8

#配置error日志信息输出目的地Appender
#定义名为error的输出端是每天产生一个日志文件
log4j.appender.myfile=org.apache.log4j.DailyRollingFileAppender
#指定日志信息的最低输出级别位ERROR,默认为DEBUG。
log4j.appender.myfile.Threshold=INFO
#myfile/log4j/error.log文件中
log4j.appender.myfile.File=d:\\test\\TestJclAndLog4j\\myfile.log
#指定按月来滚动日志文件
log4j.appender.myfile.DatePattern=yyyy-MM
#配置日志信息的格式(布局)Layout是可以灵活地指定布局模式
log4j.appender.myfile.layout=org.apache.log4j.PatternLayout
#格式化日志,Log4j采用类似C语言中的printf函数的打印格式格式化日志信息
log4j.appender.myfile.layout.ConversionPattern=[%d{yyyy-MM-ddHH:mm:ss}][%-5p][jpm-%c{1}-%M(%L)]-%m%n
#指定输出信息的编码
log4j.appender.myfile.encoding=UTF-8

#配置error日志信息输出目的地Appender
#定义名为error的输出端是每天产生一个日志文件
log4j.appender.myerror=org.apache.log4j.DailyRollingFileAppender
#指定日志信息的最低输出级别位ERROR,默认为DEBUG。
log4j.appender.myerror.Threshold=ERROR
#myfile/log4j/error.log文件中
log4j.appender.myerror.File=d:\\test\\TestJclAndLog4j\\myerror.log
#指定按月来滚动日志文件
log4j.appender.myerror.DatePattern=yyyy-MM
#配置日志信息的格式(布局)Layout是可以灵活地指定布局模式
log4j.appender.myerror.layout=org.apache.log4j.PatternLayout
#格式化日志,Log4j采用类似C语言中的printf函数的打印格式格式化日志信息
log4j.appender.myerror.layout.ConversionPattern=[%d{yyyy-MM-ddHH:mm:ss}][%-5p][jpm-%c{1}-%M(%L)]-%m%n
#指定输出信息的编码
log4j.appender.myerror.encoding=UTF-8
  • 测试代码
public class TestJclAndLog4j {

    public static void main(String[] args) {
        final org.apache.commons.logging.Log LOGGER = org.apache.commons.logging.LogFactory.getLog(TestJclAndLog4j.class);
        LOGGER.debug("TestJclAndLog4j debug log.");
        LOGGER.info("TestJclAndLog4j info log.");
        LOGGER.error("TestJclAndLog4j error log.");
    }
}

运行:
java日志体系分析_第14张图片
java日志体系分析_第15张图片

写在后面

参考文章列表:
https://zhuanlan.zhihu.com/p/64353891
https://www.cnblogs.com/warking/p/5710303.html

你可能感兴趣的:(杂,java,日志)