quarkpt
quarkpt

Reputation: 119

How to use log4j2 FileAppender with SLF4J API

I've been trying to follow the recipes on this site and others for how to add a log4j2 FileAppender instance to a class that's using the SLF4J API for its overall logging, and I'm not having much luck. Can somebody tell me what I'm doing wrong with the following code?

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.FileAppender;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FileAppenderTest {

    private static final Logger log = LoggerFactory.getLogger(FileAppenderTest.class);

    private final Layout<?> taskLogLayout = PatternLayout.newBuilder()
        .withPattern("%d %-5p [%t:%C{1}.%M] %m%n")
        .build();

    @Test
    public void testFileAppender() throws IOException {

        File appenderFile = new File("build/test", "file-appender.log");

        FileAppender taskLog = FileAppender.newBuilder()
            .withFileName(appenderFile.getAbsolutePath())
            .setName("file-appender.log")
            .setLayout(taskLogLayout)
            .build();
        taskLog.start();

        LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
        Configuration c = ctx.getConfiguration();
        LoggerConfig lc = c.getLoggerConfig("");
        lc.addAppender(taskLog, Level.ALL, null);

        log.info("Here is a test message with INFO level");
        log.warn("Here is a test message with WARN level");

        lc.removeAppender("file-appender.log");

        assertTrue(appenderFile.exists());

        List<String> logLines = FileUtils.readLines(appenderFile, (Charset) null);
        assertEquals(2, logLines.size());

    }

}

The assertTrue statement passes, so the file is created by the FileAppender, but the assertEquals fails because the length of logLines is zero (i.e., the log file has no content).

Any ideas?

Upvotes: 0

Views: 1440

Answers (1)

quarkpt
quarkpt

Reputation: 119

Found the problem:

In the code above, note that the Logger is obtained from the LoggerFactory prior to application of any logging configuration settings. This causes the logger to have a default level of ERROR. Thus, even though the APPENDER has a level of INFO, the LOGGER has a level of ERROR and that rejects the INFO and WARN messages before they even get to the appender!

The following code will pass all its tests. Note that the Logger is no longer a static member and that the configuration is set to have a level of ANY before the Logger is obtained.

package gov.nasa.ziggy.worker;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.FileAppender;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FileAppenderTest {

    private final Layout<?> taskLogLayout = PatternLayout.newBuilder()
        .withPattern("%d %-5p [%t:%C{1}.%M] %m%n")
        .build();

    @Test
    public void testFileAppender() throws IOException {

        LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
        Configuration c = ctx.getConfiguration();
        LoggerConfig lc = c.getLoggerConfig("");
        lc.setLevel(Level.ALL);

        Logger log = LoggerFactory.getLogger(FileAppenderTest.class);
        File appenderFile = new File("build/test", "file-appender.log");

        FileAppender taskLog = FileAppender.newBuilder()
            .withFileName(appenderFile.getAbsolutePath())
            .setName("file-appender.log")
            .setLayout(taskLogLayout)
            .build();
        taskLog.start();

        lc.addAppender(taskLog, Level.ALL, null);

        log.info("Here is a test message with INFO level");
        log.warn("Here is a test message with WARN level");

        lc.removeAppender("file-appender.log");

        assertTrue(appenderFile.exists());

        List<String> logLines = FileUtils.readLines(appenderFile, (Charset) null);
        assertEquals(2, logLines.size());

    }

}

Upvotes: 1

Related Questions