log4j2 dynamic file name based on command line argument

一个人想着一个人 提交于 2020-01-30 11:31:09

问题


We are using a fairly simple log4j2.xml configuration file to log to stdout. However there are cases where we want to change this configuration programmatically after the start of the application to use a log file that is handed over on the command line.

For this I followed the suggestion on the log4j2 homepage and wrote the following method

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);
}

We get the following output

<?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>

and then the log message

ERROR Attempted to append to non-started appender File

After that logging continues to be output to stdout and the desired log file stays empty.

Anyone having an idea what I am doing wrong?


回答1:


You don't need programmatic configuration to do what you want, and I would strongly discourage you from using it since that would make your code depend on the log4j2 implementation rather than its public interface.

To change the file dynamically at runtime you can use the RoutingAppender together with a lookup. See the log4j2 FAQ page for details.

Here is a sample log4j2 configuration:

<?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>

Here is some sample Java code to generate some logs:

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");
    }
}

When the above code is executed without passing a program argument, 2 logs are generated each with 1 entry. The default.log contains the first log entry and the specialFile.log contains the second. If you pass a program argument it will be used as the log file name in which case no entry would appear in default.log - as illustrated by the second log where we simulate passing a single argument by creating the new String array.

Hope this helps!




回答2:


try this

    /**
     * 
     * @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");
    }

Why my answer is downvoted?



来源:https://stackoverflow.com/questions/58058227/log4j2-dynamic-file-name-based-on-command-line-argument

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!