转载-Java-日志-java日志体系分析
原文 java日志体系分析-CSDN博客 ------ https://blog.csdn.net/wang0907/article/details/118675549
写在前面
Java中日志框架种类繁多,我也是最近在学习mybatis过程中对日志的使用产生了困惑,所以这里个人做下总结,希望能帮助到和我有一样困惑的朋友,下面我们就开始吧!
1:java日志的发展历史
Java中的第一个日志框架是apache提出来的log4j
,如下:
后来呢,sun公司认为自己作为java的开发主体,日志肯定是需要自己来给出方案的,为了打压log4j,于是在jdk4中制定了JULjava util logging
,在rt.jar
的java.util.logging包中,如下:
与此同时,又有一些其他的日志框架也被提了出来,比如simplelog,此时java日志体系如下图:
但是众多的毫无关系的日志框架,势必在我们的项目中造成混乱,比如项目中使用的是log4j,依赖的库A使用的是JUL,依赖的库B使用的是simplelog,这样一个项目中各种日志框架混杂,项目会越来越不好维护,越来越混乱,此时这个问题就亟需解决。这个时候,apache社区提出了commons-logging即JCL
的日志抽象来适配各种日志框架,此时java日志体系就变成下图:
之后呢,log4j的作者提出了一个对标JCL的日志抽象适配接口SLF4J
(simple logging facade for Java),不仅如此还直接开发一个基于SLF4J的日志框架实现logback
,与此同时呢,又继续维护并升级了log4j,形成了其升级版本log4j2
,此时Java日志体系变为下图红色框为本部分新介绍的
:
这个图也是目前java日志体系发展的现状了。
2:各种日志的使用实例
2.1:log4j
- pom
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </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,学霸"); } }
我们运行,输出如下:
注意提示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")
我们是进行了配置的,因此会成功生成日志文件,如下:
要消除警告我们可以有如下的两种方式,第一种是定义一个名称为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
在运行查看:
此时警告就消失了,文件生成如下:
但是呢,这种配置方式有个问题就是,项目中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
然后运行:
各种警告也都消失了,生成日志文件如下:
截止到这里我们其实并没有引入日志门面,但是在项目中一般都是面向日志门面接口来编写日志的,这里我们引入日志门面slf4j,修改如下:
- 修改pom
<dependencies> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!-- 因为log4j不是slf4j日志门面接口的亲儿子,所以需要该依赖执行进一步的转换适配(logback是slf4j的亲儿子,就不需要该依赖) --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> </dependencies>
- 修改测试代码
org.slf4j.Logger saveUserLog = org.slf4j.LoggerFactory.getLogger("saveUserLog"); saveUserLog.info("slf4j info ..."); saveUserLog.error("slf4j error ...");
注意使用了slf4j的接口API,内部会动态的查找实现类,这里最终查找到的就是log4j,运行:
2.2:logback
本例直接结合slf4j日志门面接口一起使用,logback作为slf4j的亲儿子,使用起来更加无缝衔接。
- pom
<dependencies> <!-- 日志门面依赖 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <!-- logback依赖 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-access</artifactId> <version>1.2.3</version> </dependency> </dependencies>
- logback.xml
<?xml version="1.0" encoding="UTF-8"?> <configuration status="WARN" monitorInterval="30"> <!-- 名称为STDOUT1,输出到控制台的的appender --> <appender name="STDOUT1" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern> </encoder> </appender> <!-- 定义统配日志名称的root配置,日志级别为debug,使用的appender是STDOUT1--> <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
此时只配置了输出到控制台,接下来增加输出到文件中。
红框中的为增加内容,这里再贴下,方便看到的朋友测试:
<appender name="FILE1" class="ch.qos.logback.core.FileAppender"> <file>D:\\test\\testslf4jlogbck\\testlogback.log</file> <append>true</append> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <appender-ref ref="FILE1"/>
运行查看生成的文件:
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
<?xml version="1.0" encoding="UTF-8"?> <configuration status="WARN" monitorInterval="30"> <!--先定义所有的appender --> <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> <!--只有定义了logger并引入的appender,appender才会生效 --> <loggers> <!--过滤掉spring和mybatis的一些无用的DEBUG信息 --> <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."); } }
运行:
2.4:JCL(java common logging) + Log4j
本部分使用JCLjava common logging
作为日志门面,使用log4j作为日志实现,具体如下。
- pom
<dependencies> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </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."); } }
运行:
写在后面
参考文章列表:
https://zhuanlan.zhihu.com/p/64353891
https://www.cnblogs.com/warking/p/5710303.html
写在前面
Java中日志框架种类繁多,我也是最近在学习mybatis过程中对日志的使用产生了困惑,所以这里个人做下总结,希望能帮助到和我有一样困惑的朋友,下面我们就开始吧!
1:java日志的发展历史
Java中的第一个日志框架是apache提出来的log4j
,如下:
后来呢,sun公司认为自己作为java的开发主体,日志肯定是需要自己来给出方案的,为了打压log4j,于是在jdk4中制定了JULjava util logging
,在rt.jar
的java.util.logging包中,如下:
与此同时,又有一些其他的日志框架也被提了出来,比如simplelog,此时java日志体系如下图:
但是众多的毫无关系的日志框架,势必在我们的项目中造成混乱,比如项目中使用的是log4j,依赖的库A使用的是JUL,依赖的库B使用的是simplelog,这样一个项目中各种日志框架混杂,项目会越来越不好维护,越来越混乱,此时这个问题就亟需解决。这个时候,apache社区提出了commons-logging即JCL的日志抽象来适配各种日志框架,此时java日志体系就变成下图:
之后呢,log4j的作者提出了一个对标JCL的日志抽象适配接口SLF4J(simple logging facade for Java),不仅如此还直接开发一个基于SLF4J的日志框架实现logback,与此同时呢,又继续维护并升级了log4j,形成了其升级版本log4j2,此时Java日志体系变为下图红色框为本部分新介绍的:
这个图也是目前java日志体系发展的现状了。
2:各种日志的使用实例
2.1:log4j
2.2:logback
2.3:log4j2
2.4:JCL(java common logging) + Log4j