如何在运行时以编程方式添加Log4J2附加器?

55

是否可以按照XML配置规范以编程方式添加Log4J2附加器?

我计划在log4j2.xml中定义所有内容,然后像这样根据情况选择附加器(不会编译):

if (arg[0].equals("log") ) {
    Logger.getLogger("loggerNameFromXMLConfig").addAppender("appenderNameFromXMLConfig");
} else {
    //...
}

8
这并不是完全重复的问题,因为它是一个关于log4j2的问题。链接到的问题涉及log4j1(其甚至没有暴露与log4j2相同的方法)和slf4j。 - Brian Johnson
2
我不确定关闭这个问题的人知道它实际上是关于什么的。 - Colin Hebert
目前,作为一种解决方法,我放弃了log4j2并迁移到Log4J(“Log4J1”),它允许从XML配置动态构建。 - Brian Johnson
哦,我忘记在这个上面发表评论了。所以,log4j2背后的大思想基本上是您不能(也不应该)以编程方式设置附加程序。因此,您无法通过设计来执行此操作。 - Colin Hebert
1
阅读 http://logging.apache.org/log4j/2.x/manual/appenders.html#RoutingAppender 上的 RoutingAppender。可能是因为 Log4j2 想避免声明式和编程式配置混合,不确定原因,但 RoutingAppender 解决方案似乎更清晰。 - Samarth Seksaria
5个回答

41

有很多请求支持更好的Log4j 2程序配置。对不起,花费了这么长时间才推出。从Log4j 2.4开始,API被添加到log4j-core以便于程序化配置

新的ConfigurationBuilder API允许用户构建组件定义。使用此API,无需直接处理实际配置对象(如LoggerConfig和FileAppender),因为这需要了解Log4j在底层是如何工作的。组件定义添加到ConfigurationBuilder中,一旦收集了所有定义,所有实际配置对象(如Loggers和Appenders)都将被构建。它感觉有点像XML配置语法,只是你写的是Java代码。

请注意,新的ConfigurationBuilder API允许用户代码创建一个新的配置或完全替换现有的配置。如果您的用例不同,并且希望在Log4j启动后以编程方式修改(而不是替换)现有配置,则需要处理实际配置对象。在这种情况下,请参阅手册中的在初始化后编程修改当前配置部分。


9
我注意到,我一直在寻找这个答案,并且它指向文档,但是文档中的示例现在使用了已弃用的方法,并且实际上从未为我工作过。我不得不添加类似以下内容才能让它工作: context.getRootLogger().addAppender(configuration.getAppender(appender.getName())); 你也不能直接传递 appender,因为那样也行不通... - user2163960
2
@RemkoPopma 现在是log4j 2.12版本。显然需要一个更清晰的API来停止和清除以编程方式创建的附加器和记录器。 - Gaurav
23
我在考虑放弃使用log4j。他们把它复杂化到了荒谬的地步。你只需要2个小时就可以编写自己的日志框架,但研究这个垃圾需要2周时间。 - jurez
@jurez 我同意你的看法!我花了两天时间研究如何添加 appender.... 以前只需要 logger.addAppender(appender); 就可以了... - Amerousful
@Amerousful,现在如何添加一个appender?救命! - user3804769
显示剩余2条评论

31

编辑:对于最新版本的log4j2,请查看此处:https://dev59.com/vmUp5IYBdhLWcg3wNFar#33472893

我觉得他们不想让你这样做,但这对我起作用:

if (arg[0].equals("log") ) {
  org.apache.logging.log4j.Logger logger
    = org.apache.logging.log4j.LogManager.getLogger("loggerNameFromXMLConfig");
  org.apache.logging.log4j.core.Logger coreLogger
    = (org.apache.logging.log4j.core.Logger)logger;
  org.apache.logging.log4j.core.LoggerContext context
    = (org.apache.logging.log4j.core.LoggerContext)coreLogger.getContext();
  org.apache.logging.log4j.core.config.BaseConfiguration configuration
    = (org.apache.logging.log4j.core.config.BaseConfiguration)context.getConfiguration();

  coreLogger.addAppender(configuration.getAppender("appenderNameFromXMLConfig"));
} else {
  //...
}

2
那很漂亮! :) - Brian Johnson
谢谢 - 我用它来做这个 - https://codingcraftsman.wordpress.com/2015/04/28/log4j2-mocking-with-mockito-and-junit/ - Ashley Frieze
你使用 org.apache.logging.log4j.core.Logger 进行转换,真是救了我的一天!谢谢!! - user2081279
这是对我非常完美的解决方案: https://www.dontpanicblog.co.uk/2018/04/29/test-log4j2-with-junit/ - Skystrider

17
正如我之前所提到的,我无法让https://logging.apache.org/log4j/2.x/manual/customconfig.html#AddingToCurrent按照我期望的方式工作(我的appender无法将消息路由到它),但我最终找到了一种适合我的模式 - 允许我在运行时添加一个appender,并且实际上将日志消息路由到该appender。 编辑 我从这里删除了一堆令人困惑的代码,它并没有做任何事情...
    LoggerContext lc = (LoggerContext) LogManager.getContext(false);
    FileAppender fa = FileAppender.newBuilder().withName("mylogger").withAppend(false).withFileName(new File(outputDirectory, "ConsoleOutput.txt").toString())
            .withLayout(PatternLayout.newBuilder().withPattern("%-5p %d  [%t] %C{2} (%F:%L) - %m%n").build())
            .setConfiguration(lc.getConfiguration()).build();
    fa.start();
    lc.getConfiguration().addAppender(fa);
    lc.getRootLogger().addAppender(lc.getConfiguration().getAppender(fa.getName()));
    lc.updateLoggers();

对我来说一个关键的点,是直接调用addAppender并传入你的 appender 是不起作用的,但是通过名称请求你的 appender 可以。虽然这没有意义...但既然它有效了,我已经厌倦了在本应如此简单的事情上浪费时间。


2
非常感谢您的回答!官方文档现在甚至无法与log4j 2.14的接口匹配。这应该写进官方文档中! - David Georg Reichelt

3
在Log4j2中的结构
        ---"Config"---
                Appenders
                        Appender(0)
                            Console
                        Appender(1)
                            File
                    LoggerConfigs
                        -- LoggerConfig(0) 
                        -- LoggerConfig(1)
                        -- LoggerConfig(2)

        ----"LoggerConfig"----
                - AppenderRefs
                    -- AppenderRef(0)
                        -- Name Console
                        -- Level : DEBUG
                - Appenders
                    -- Appender(0)
                        -- Name Console
                        -- Level : DEBUG
                - Level -- ALL

loggerConfig.getAppenders() --> 将返回“Config”中的Appenders。对我来说,这是一个错误。

loggerConfig.getAppenderRefs() --> 正常工作!


我喜欢使用类似yaml的记法 :) - Sridhar Sarnobat

2

addLoggerAppender方法的文档说明指出,它旨在用于单元测试。 - Gaurav

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接