【从零单排】详解 Log4j + Slf4j 等日志框架(番外)
日志框架的上,下两篇把大部分的内容都涵盖了,有些额外的小知识点(但是同样十分重要),放到这里来说。
知识点1:Log4j Properties File 配置详解
关于Logger
一般来说,一定(最好)要配一个 rootLogger ,告诉 log4j 默认情况下的 logger 的配置情况。
# 第一个参数是日志级别,后面可以跟多个 logger
log4j.rootLogger = INFO, stdout, testlog1, testlog2
另外,还可以在 package / class 级别,设置 logger 。
# 设置一个名为 testlog 的 logger ,只输出 com.my.app 下的内容
log4j.logger.com.my.app = INFO, testlog
# 设置该 logger 不再输出到 root logger 中
log4j.additivity.com.my.app = false
# 设置 rootLogger/logger 的日志级别,且只针对 com.my.app 下的内容
log4j.logger.com.my.app = INFO
这两招配合使用,可以控制日志灵活地输出。
举个例子,我改了某个类,新加了很多 DEBUG log,跑 Integration Test 的时候会输出大量 DEBUG 用于分析。但是我不想污染主 log 文件。咋办?
改动如下:main 是原来的配置,不去动它,新增 temp 部分,这样,所有 com.my.app.Log4jApp
的 DEBUG log 会单独输出到一个文件,不会污染主 log 文件。(测完了把新增部分删除即可)
# main
log = D:/codebase/LogTestSystem/logdir
log4j.rootLogger = INFO, testlog
log4j.appender.testlog=org.apache.log4j.FileAppender
log4j.appender.testlog.File=${log}/log.out
log4j.appender.testlog.layout=org.apache.log4j.PatternLayout
log4j.appender.testlog.layout.conversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] - [%c] %m%n
# temp
log4j.logger.com.my.app.Log4jApp = Debug, testlogNew
log4j.additivity.com.my.app.Log4jApp = false
log4j.appender.testlogNew=org.apache.log4j.FileAppender
log4j.appender.testlogNew.File=${log}/log3.out
log4j.appender.testlogNew.layout=org.apache.log4j.PatternLayout
log4j.appender.testlogNew.layout.conversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] - [%c] %m%n
关于Layout
格式如下:
%c 输出日志信息所属的类的全名
%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy-MM-dd HH:mm:ss },输出类似:2002-10-18- 22:10:28
%l 输出日志事件的发生位置,即输出日志信息的语句处于它所在的类的第几行
%m 输出代码中指定的信息,如log(message)中的message
%n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”
%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL。如果是调用debug()输出的,则为DEBUG,依此类推
%r 输出自应用启动到输出该日志信息所耗费的毫秒数
%t 输出产生该日志事件的线程名
一个常见的配置如下:
log4j.appender.testlog.layout.conversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] - [%c] %m%n
输出如下:
2021-04-24 15:16:21 [ main:0 ] - [ INFO ] - [Log4jModule2] hello world
(详细见参考1,参考3)
知识点2:对 Log4j Properties File 文件重命名
实际使用中,为了区分不同环境,可能的 log4j.properties 文件集合如下:
log4j_alice_personal.properties
log4j_stg.properties
log4j_uat.properties
log4j_prd.properties
那么怎么使之生效呢?可以在启动参数中做如下配置:
java -Dlog4j.configuration=file:/some_path/log4j_stg.properties MyApp
(详细见参考4)
知识点3:Apache Commons Logging 的兼容性问题
如果项目中或依赖的包中使用了JCL,那么需要注意兼容性。
一般最容易报的错是 JCL 无法正确地根据配置文件创建对应的 Logger 。
99%的情况,这是由于pom.xml
和xxx.properties
/xxx.xml
冲突导致的。
所以,我们需要理解 JCL 是如何创建 Logger 的,我简单总结如下:
- 优先选用环境变量
org.apache.commons.logging.Log
/org.apache.commons.logging.LogFactory
指定的 Logger - 其次是 Log4j
- 然后是 JDK Logging
- 最后是 Simple Log
第一步中,这个环境变量,一种方式是通过 commons-logging.properties
配置,如下:
org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
org.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger
org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
另外,也有可能,在某些 jar 包中,会自己设置。比如,log4j-jcl:2.11.1
中,显示地设置了
org.apache.common.logging.LogFactory = org.apache.logging.log4j.jcl.LogFactoryImpl
这就导致了,只要我引用了log4j-jcl:2.11.1
这个包,JCL 一定会初始化一个 Log4j2 的 Logger !如果找不到就报错。(如果你问我是咋知道的?踩坑踩出来的呗...)
(详细见参考2)
参考
- 参考1:Log4j.properties配置详解 https://www.jianshu.com/p/ccafda45bcea
- 参考2:Apache Commons Logging 官网 https://commons.apache.org/proper/commons-logging/guide.html
- 参考3:Oracle Doc https://docs.oracle.com/cd/E29578_01/webhelp/cas_webcrawler/src/cwcg_config_log4j_file.html
- 参考4:Stack Overflow QA https://stackoverflow.com/questions/34575481/how-can-i-change-the-name-and-location-of-log4j-properties