问题来源
本周在实际项目中发现无法自定义的log4j-dev配置的error日志级别文件无法生效,项目启动后仍然采用默认的info级别日志进行打印。之所以自定义名称,是为了减少隔离不同环境的日志级别,比如开发dev环境使用debug、info级别,而线上环境使用error级别。如果是采用默认的文件名称log4j.properties那么是可以生效的,但是在部署到不同环境时需要手动进行更改,容易遗忘。
于是在同事的协助下花时间研究了一波log4j的源码,最终完美解决
解决过程
我的log4j-dev.properties文件放置在resources目录下,springmvc环境(非springboot)
1、在web.xml引入Log4jConfigListener的监听器.
<context-param> <param-name>log4jConfigLocation</param-name> <param-value>classpath:log4j-dev.properties</param-value> </context-param> <context-param> <param-name>log4jRefreshInterval</param-name> <param-value>6000</param-value> </context-param> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener>
2、删除原来的log4j.properties文件,以及各个子模块下的log4j.properties文件。必须要删除所有的默认名称文件。或者保留默认文件,但是默认文件里的配置项的key必须同自定义配置文件log4j-dev.properties一致,value可以不同。
3、对第2点的解释Log4jConfigListener源码发现,如果存在默认文件会读取到默认文件的所以配置项,然后读取自定义文件的配置项,然后进行覆盖掉默认的配置项。如果没有默认配置文件,则直接读取classpath:log4j-dev.properties文件。
主要源码心得
Log4j容器初始化过程时序图
web环境log4jweb初始化过程时序图:
主要类:
- Logger——日志行为类,debug,info等日志记录行为,
- Category——Logger的父类
- LoggerRepository——日志容器,是管理Logger的容器,内部维护一个Hashtable,储存所有的Logger
- Hierarchy是Log4J中默认对LoggerRepository的实现类,它用于表达其内部的Logger是以层次结构存储的。在对LoggerRepository接口的实现中,getLogger()方法是其最核心的实现,因而首先从这个方法开始。
- LoggerManager——日志管理,日志框架的入口,由他开始启动日志系统,加载配置,初始化根日志
- Appender——日志输出器
- Layout——日志形式
- Level——日志级别
- rootLogger——根日志,每个Logger都有父级Logger,是层级关系,如果没有,则根日志就是父级,根日志是最后节点