【问题标题】:log4j2 dynamic file name based on command line argumentlog4j2 基于命令行参数的动态文件名
【发布时间】:2020-01-23 06:36:06
【问题描述】:

我们使用一个相当简单的 log4j2.xml 配置文件来记录到标准输出。但是,在某些情况下,我们希望在应用程序启动后以编程方式更改此配置,以使用在命令行上传递的日志文件。

为此我按照log4j2主页上的建议,写了如下方法

static void divertLogging(String logFile, Level level) {
    ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();

    AppenderComponentBuilder appenderBuilder 
        = builder.newAppender("File", "FILE").addAttribute("fileName", logFile).addAttribute("append", "false");

    appenderBuilder.add(builder.newLayout("PatternLayout")
        .addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable"));

    builder.add(appenderBuilder);
    builder.add(builder.newRootLogger(level).add(builder.newAppenderRef("File")));

    try {
        builder.writeXmlConfiguration(System.out);
    } catch (IOException e) {
        throw new ApplicationException(e);
    }

    BuiltConfiguration configuration = builder.build();

    Configurator.initialize(configuration);
    ((LoggerContext)LogManager.getContext(false)).updateLoggers(configuration);
}

我们得到以下输出

<?xml version="1.0" ?>
<Configuration>
    <Appenders>
        <FILE name="File" fileName="test.log" append="false">
            <PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable"/>
        </FILE>
    </Appenders>
    <Loggers>
        <Root level="INFO">
            <AppenderRef ref="File"/>
        </Root>
    </Loggers>
</Configuration>

然后是日志消息

ERROR Attempted to append to non-started appender File

之后,日志继续输出到标准输出,所需的日志文件保持为空。

有人知道我做错了什么吗?

【问题讨论】:

  • 根据this需要调用start()
  • 我在寻找解决方案时也看到了该帖子。我的印象是 log4j2 负责启动附加程序;特别是,因为我只是在构建构建器,而不是对象本身。但是,在上述方法的最后一行之前添加一行configuration.getAppender("logFile").start(); 似乎解决了问题...
  • ...但仅适用于同一类中的下一条日志语句。其他日志语句再次输出到标准输出...... :-(

标签: java log4j2


【解决方案1】:

你不需要编程配置来做你想做的事,我强烈建议你不要使用它,因为这会使你的代码依赖于 log4j2 实现而不是它的公共接口。

要在运行时动态更改文件,您可以使用RoutingAppenderlookup。详情请见log4j2 FAQ page

这里是一个示例 log4j2 配置:

<?xml version="1.0" ?>
<Configuration>
    <Appenders>
        <Routing name="myAppender">
            <Routes pattern="$${main:0}">
                <!-- This route is chosen if there is no value for main argument 0 -->
                <Route key="$${main:0}">
                    <File
                        fileName="logs/default.log"
                        name="myAppender-default">
                        <PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable"/>
                    </File>
                </Route>
                <!-- This route is chosen if there is a value for main argument 0 -->
                <Route>
                    <File
                        fileName="logs/${main:0}.log"
                        name="myAppender-${main:0}">
                        <PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable"/>
                    </File>
                </Route>
            </Routes>
        </Routing>
    </Appenders>
    <Loggers>
        <Root level="INFO">
            <AppenderRef ref="myAppender"/>
        </Root>
    </Loggers>
</Configuration>

这里是一些用于生成日志的示例 Java 代码:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.lookup.MainMapLookup;

public class SomeClass {

    private static final Logger LOG = LogManager.getLogger();   

    public static void main(String[] args){
        MainMapLookup.setMainArguments(args);

        LOG.info("This should appear in default.log");

        args = new String[]{"specialFile"};
        MainMapLookup.setMainArguments(args);
        LOG.info("This should appear in specialFile.log");
    }
}

在不传递程序参数的情况下执行上述代码时,会生成 2 个日志,每个日志有 1 个条目。 default.log 包含第一个日志条目,而 specialFile.log 包含第二个。如果您传递程序参数,它将用作日志文件名,在这种情况下,default.log 中不会出现任何条目 - 如第二个日志所示,我们通过创建新的 String 数组来模拟传递单个参数。

希望这会有所帮助!

【讨论】:

    【解决方案2】:

    试试这个

        /**
         * 
         * @param logType 0 = console, 1 = file
         * @param logFile
         * @param level
         */
        private void divertLogging(int logType, String logFile, Level level) {
            ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
    
            AppenderComponentBuilder appenderBuilder;
    
            if (logType == 0)
                appenderBuilder = builder.newAppender("Stdout", "CONSOLE").addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT);
            else
                appenderBuilder = builder.newAppender("File", "FILE").addAttribute("fileName", logFile).addAttribute("append", "false");
    
            appenderBuilder.add(builder.newLayout("PatternLayout")
                .addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable"));
    
            builder.add(appenderBuilder);
            if (logType == 1)
                builder.add(builder.newRootLogger(level).add(builder.newAppenderRef("File")));
    
            try {
                builder.writeXmlConfiguration(System.out);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            BuiltConfiguration configuration = builder.build();
    
            Configurator.initialize(configuration);
            ((LoggerContext)LogManager.getContext(false)).updateLoggers(configuration);
            if (logType == 1)
                configuration.getAppender("File").start();
        }
    
        public static void main(String[] args) {
            Log4j2Test test = new Log4j2Test();
            test.divertLogging(0, null, Level.ALL);
            logger.error("Log to console 1");
            test.divertLogging(1, "C:\\Java\\test\\output\\test.log", Level.ALL);
            logger.error("Log to file 2");
            test.divertLogging(0, null, Level.ALL);
            logger.error("Log to console 3");
            test.divertLogging(1, "C:\\Java\\test\\output\\test.log", Level.ALL);
            logger.error("Log to file 4");
        }
    

    为什么我的回答被否决了?

    【讨论】:

    • 除了 xml 输出之外,我看到以下内容:14:09:39.756 [main] ERROR LogTest - Log to console 1 14:09:40.072 [main] ERROR LogTest - Log to console 3 在 C:\Java\test\output\test.log 我看到 2019-09-23 14:09:40,062 [main] ERROR: Log to file 2 2019-09-23 14:09:40,080 [main] ERROR: Log to file 4
    • 是的,这是我在main方法中的错误日志,在console/stdout和日志文件之间进行切换,调用divertLogging方法进行切换
    • 那么我是否理解正确:一旦基于 log4j2.xml 文件的配置处于活动状态,就无法以编程方式覆盖它?
    • 如果您仍想使用 log4j2.xml 进行基本配置,那么我认为您应该在 divertLogging 方法中手动加载文件。只需将 logType 设为 0 即可读取文件,可以参考here。我也会编辑我的答案
    • 自己尝试过,并将log4j2.xml的手动加载(如您提供的链接所示)放在我的启动类的静态初始化程序中。不工作:-(
    猜你喜欢
    • 2018-03-12
    • 2012-07-12
    • 2013-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-17
    • 2018-05-11
    相关资源
    最近更新 更多