简体   繁体   中英

Configure FileAppender after Log4j2 Initialization

I'm having trouble configuring a new FileAppender for my log4j2 logger. The problem is that I only know the path of the file I should be appending my log after the application starts, so I tried following these instructions here for modifying the original configuration after log4j2 is initialized.

I've read a lot of answers regarding similar issues on stack overflow but most of them are for earlier log4j versions, and won't work as they have now implemented that configuration oriented plugin into log4j itself.

The app successfuly creates the log file but it won't write any content to it . I've put that test log message on ERROR level on purpose so I could verify it wasn't any issue related to the log level threshold or something.

I'm kind of new to log4j2 configuration and feeling a bit lost at this point.

My log4j2.xml file (very basic) looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
    <Properties>
        <Property name="PATTERN">%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n</Property>
    </Properties>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
             <PatternLayout pattern="${PATTERN}"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="debug">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>

Also, here's the method that should be adding that new FileAppender :

public static void initLogFile(String path, Level level) {
    final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
    final Configuration config = ctx.getConfiguration();
    Layout layout = PatternLayout.createDefaultLayout(config);
    Appender appender = FileAppender.createAppender(path, "false", "true",
            "RollingFile", "true", "false", "false", "8000", null, null,
            "false", null, config);
    appender.start();
    config.addAppender(appender);
    AppenderRef ref = AppenderRef.createAppenderRef("RollingFile", null,
            null);
    AppenderRef[] refs = new AppenderRef[] { ref };
    LoggerConfig loggerConfig = LoggerConfig.createLogger("false", level,
            "org.apache.logging.log4j", "true", refs, null, config, null);
    loggerConfig.addAppender(appender, null, null);
    config.addLogger("org.apache.logging.log4j", loggerConfig);
    ctx.updateLoggers();
    System.out.println("Logger initialized");
}

And my Main method, which the very first thing it does is try to change the logger config to write into the file I want:

static final Logger log = LogManager.getLogger(Main.class.getName());
  public static void main( String[] args )
    {
        // Init
        System.out.println("Initializing logger");
        Utils.initLogFile("C:/Users/Jorge/Desktop/logtest/test.log", Level.DEBUG);

        log.error("test error message");

        ...
 }

What do you mean that you only know the path after the application starts? No matter what, you should always be able to use a Lookup to determine the file path. If the path is specified as one of the arguments to the application you should be able to use the main arguments lookup.

To be clear, your configuration should something like:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Properties>
    <Property name="PATTERN">%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n</Property>
</Properties>
<Appenders>
    <Console name="Console" target="SYSTEM_OUT">
         <PatternLayout pattern="${PATTERN}"/>
    </Console>
    <File name="file" fileName="${main:--logFile}">
         <PatternLayout pattern="${PATTERN}"/>
    </File> 
</Appenders>
<Loggers>
    <Logger name="org.apache.logging.log4j" level="${main:--level}">
       <AppenderRef ref="file"/>
    </Logger>
    <Root level="debug">
        <AppenderRef ref="Console" />
    </Root>
</Loggers>

Note that I have configured a File appender. Your sample code is creating a File appender but is naming it "RollingFile" for some reason.

I'm accepting this answer because its related strictly to the question's title. If you're checking this post for help you should take a moment to read @rgoers answer as it might be what you're looking for.

So here's how I finally did it. Configuring an existing empty new logger into my log4j2.xml file (as I didn't want to create a new one on runtime) which I would modify on runtime.

The issue with my original code was the logger's name. If you take a look at the original post I was creating a new appender named "org.apache.logging.log4j".

On my main class when I retrieved the logger with which I was going to log, I was getting the default's logger for the package in which my main class is ( com.jorge.myapp.business ). Thus, this logger was receiving all the log requests and the new one I created ( org.apache.logging.log4j ) wasn't receiving any. That's why it wouldn't log anything to the file I inteded it to, because all logging requests where going to other logger ( com.jorge.myapp.business ).

I hope this helps somebody, it's a bit confusing to explain... Below more info about my classes and config file:

Modified config file log4j2.xml :

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
    <Properties>
        <Property name="PATTERN">%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n</Property>
    </Properties>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
             <PatternLayout pattern="${PATTERN}"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="debug">
            <AppenderRef ref="Console" />
        </Root>
        <Logger name="com.jorge.myapp.business" level="debug">
        </Logger>

    </Loggers>
</Configuration>

This is the initializing method , it retrieves the current log4j2 config from the xml file and modifies the existing logger adding a new file appender

public static void initLogFile(String path, Level level){

    final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
    final Configuration config = ctx.getConfiguration();

    Layout layout = PatternLayout.createLayout(Constants.LOG_PATTERN, null, config, null, null, false, false, null, null);
    Appender appender = FileAppender.createAppender(path, "true", "true", "File", "true",
        "false", "false", null, layout, null, "false", null, config);
    appender.start();
    config.addAppender(appender);
    AppenderRef ref = AppenderRef.createAppenderRef("File", null, null);
    AppenderRef[] refs = new AppenderRef[] {ref};
    LoggerConfig loggerConfig = config.getLoggerConfig("com.jorge.myapp.business");
    loggerConfig.addAppender(appender, null, null);
    ctx.updateLoggers();
}

Some constants I used ( Constants.java class):

public static final String LOG_PATTERN = "%d{yyyy-MM-dd HH:mm:ss} [%-5level] MyApp - %msg%n";

More info about how Log4j2 actually works 'under the hood' can be found here , as pointed out by @rgoers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM