Log4j2编程式创建LoggerConfig时AppenderRef参数作用咨询
Log4j2 动态创建LoggerConfig时AppenderRef参数的必要性疑问
问题描述
当前使用Log4j2 v2.17.2版本,需要在运行时通过编程方式动态添加Logger与Appender。参考Log4j2官方自定义配置文档中「初始化后编程式修改当前配置」章节的指引创建LoggerConfig,参考示例代码如下:
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); final Configuration config = ctx.getConfiguration(); final Layout layout = PatternLayout.createDefaultLayout(config); Appender appender = FileAppender.createAppender("target/test.log", "false", "false", "File", "true", "false", "false", "4000", layout, null, "false", null, config); appender.start(); config.addAppender(appender); AppenderRef ref = AppenderRef.createAppenderRef("File", null, null); AppenderRef[] refs = new AppenderRef[] {ref}; LoggerConfig loggerConfig = LoggerConfig.createLogger("false", "info", "org.apache.logging.log4j", "true", refs, null, config, null ); loggerConfig.addAppender(appender, null, null); config.addLogger("org.apache.logging.log4j", loggerConfig); ctx.updateLoggers();
实际开发中已经改用Builder构造器方式创建LoggerConfig,测试时发现两个和文档描述有差异的现象:
- 调用
LoggerConfig.createLogger()时,即使给refs(用于传入Appender引用的入参)传入空数组,新建的LoggerConfig依然可以正常完成日志输出 - 静态配置文件中编写LoggerConfig时,
<Logger>标签内必须配置Appender引用,配置逻辑和LoggerConfig对象属性一一对应,看起来是必填项;但编程式实现时,直接调用LoggerConfig.addAppender()方法绑定Appender也能正常运行
想了解这两种写法的差异原因,以及官方指引要求创建LoggerConfig时传入AppenderRef数组的根本原因。
回答
这个差异本质是Log4j2配置加载解析阶段和运行时动态修改阶段的逻辑设计区别,两种写法本身没有功能冲突:
AppenderRef数组的原生定位是配置解析阶段的依赖声明
不管是XML/JSON/Properties这类静态配置,还是编程式初始化LoggerConfig,传入AppenderRef数组的逻辑和静态配置规则完全对齐:它本质是延迟绑定的引用占位符。配置解析流程启动时,各个配置节点的加载顺序是不固定的,解析到LoggerConfig节点时,它依赖的Appender很可能还没完成初始化、没注册到全局Configuration中,此时根本拿不到实际的Appender实例,只能先把需要绑定的Appender名称以AppenderRef的形式存下来,等整个配置文件所有节点都解析完成、所有Appender都注册就绪后,框架再统一遍历所有LoggerConfig的AppenderRef,把引用替换成实际的Appender实例完成绑定。
官方示例里传入AppenderRef数组,只是完全复现了静态配置的标准加载流程,不是强制的语法要求。- 手动调用
addAppender()是直接跳过引用解析的运行时操作
做动态配置修改时,代码执行顺序完全由开发者控制:一般都是先创建Appender、启动Appender、把Appender注册到全局Configuration之后,再创建对应的LoggerConfig,此时已经拿到了初始化完成的Appender实例,直接调用addAppender()绑定属于直接操作运行时实例,完全绕过了"先存引用名、后续统一解析"的流程,所以哪怕初始化LoggerConfig时给refs传空数组,也不会影响最终的日志输出。 - 静态配置中AppenderRef看起来是必填的原因
静态配置加载是全自动的无序解析流程,解析Logger节点时,它依赖的Appender节点大概率还没被加载,框架没有办法提前拿到Appender实例做绑定,只能通过配置里写的AppenderRef声明依赖关系,否则框架不知道这个Logger需要绑定哪些Appender,自然就要求必须配置。
编程式动态修改时可以完全控制执行顺序,只要最终能把已注册的Appender实例绑定到LoggerConfig上,不管是初始化时传AppenderRef等框架自动解析,还是拿到实例后手动调用addAppender绑定,最终效果完全一致。
补充注意:如果创建LoggerConfig时,要绑定的Appender还没完成初始化、没注册到全局Configuration,那就必须传入对应的AppenderRef数组,否则后续框架做配置解析时找不到对应引用,会打印错误日志,也无法完成Appender绑定。
内容的提问来源于stack exchange,提问作者Noob Saibot
相关产品推荐
相关产品推荐

