使用slf4j和log4j2动态添加appender

时间:2021-06-18 21:50:29

I want to dynamically create an appender and add it to a logger. However, this seems not to be possible with slf4j. I can add my appender to a log4j logger but then I fail to retrieve the logger with the slf4j LoggerFactoy.

我想动态创建一个appender并将其添加到日志记录器中。然而,对于slf4j来说,这似乎是不可能的。我可以将我的appender添加到log4j日志记录器中,但是我无法使用slf4j日志记录器检索日志记录器。

What I want to do: I create a test class (not a jUnit test) and pass a logger in the constructor for the test class to use. Every instance of the test class needs it's own logger and appender that saves the log so it can be later used in an HTML report.

我想做的是:我创建一个测试类(而不是jUnit测试),并在构造函数中传递一个日志记录器,以便测试类使用。测试类的每个实例都需要它自己的日志记录器和appender,这样可以保存日志,以便以后在HTML报告中使用它。

What I tried (for simplicity I created a jUnit test):

我所尝试的(为了简单起见,我创建了一个jUnit测试):

  import static org.junit.Assert.assertEquals;

  import java.util.LinkedList;
  import java.util.List;

  import org.apache.logging.log4j.core.LogEvent;
  import org.junit.Test;
  import org.slf4j.helpers.Log4jLoggerFactory;

  import ch.fides.fusion.logging.ListAppender;

  public class ListAppenderTest {

      @Test
      public void test() {

          String testName = "test1";

          // the log messages are to be inserted in this list
          List<LogEvent> testLog = new LinkedList<>();

          // create log4j logger
          org.apache.logging.log4j.core.Logger log4jlogger = (org.apache.logging.log4j.core.Logger) org.apache.logging.log4j.LogManager
                                          .getLogger("Test:" + testName);

          // create appender and add it to the logger
          ListAppender listAppender = new ListAppender("Test:" + testName + ":MemoryAppender", testLog);
          log4jlogger.addAppender(listAppender);

          // get the slf4j logger
          org.slf4j.helpers.Log4jLoggerFactory loggerFactory = new Log4jLoggerFactory();
          org.slf4j.Logger testLogger = loggerFactory.getLogger("Test:" + testName);

          // test it
          final String TEST_MESSAGE = "test message";
          testLogger.info(TEST_MESSAGE);

          assertEquals(1, testLog.size());
          LogEvent logEvent = testLog.get(0);
          assertEquals(TEST_MESSAGE, logEvent.getMessage().getFormattedMessage() );
      }

  }

and this is my very basic appender:

这是我最基本的附属物

 package ch.fides.fusion.logging;

  import java.util.List;

  import org.apache.logging.log4j.core.LogEvent;
  import org.apache.logging.log4j.core.appender.AbstractAppender;

  public class ListAppender extends AbstractAppender {

      private final List<LogEvent> log;

      public ListAppender(String name, List<LogEvent> testLog) {
          super(name, null, null);
          this.log = testLog;
      }

      @Override
      public void append(LogEvent logEvent) {
          log.add(new TestLogEvent(logEvent));
      }

  }

What can I do to get this to work? Maybe I am approaching this from the wrong angle but I would like to avoid creating my own logger class. Any help is greatly appreciated.

我该怎么做才能让它工作呢?也许我是从错误的角度来处理这个问题的,但是我想避免创建我自己的logger类。非常感谢您的帮助。

3 个解决方案

#1


6  

Accessing and manipulating log4j2 over slf4j by code/at runtime:

通过代码/运行时在slf4j*问和操作log4j2:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Log4j2OverSlf4jConfigurator {

    final private static Logger LOGGER = LoggerFactory.getLogger(Log4j2OverSlf4jConfigurator.class);

    public static void main(final String[] args) {
        LOGGER.info("Starting");
        LoggerContext loggerContext = (LoggerContext) LogManager.getContext();
        Configuration configuration = loggerContext.getConfiguration();

        LOGGER.info("Filepath: {}", configuration.getConfigurationSource().getLocation());
        // Log4j root logger has no name attribute -> name == ""
        LoggerConfig rootLoggerConfig = configuration.getLoggerConfig("");

        rootLoggerConfig.getAppenders().forEach((name, appender) -> {
            LOGGER.info("Appender {}: {}", name, appender.getLayout().toString());
            // rootLoggerConfig.removeAppender(a.getName());
        });

        rootLoggerConfig.getAppenderRefs().forEach(ar -> {
            System.out.println("AppenderReference: " + ar.getRef());
        });

        // adding appenders
        configuration.addAppender(null);
    }
}

Reference: https://logging.apache.org/log4j/2.x/manual/customconfig.html

参考:https://logging.apache.org/log4j/2.x/manual/customconfig.html

#2


1  

I think you are having a similar scenario as ours. A more complex logging in production, but a simpler one during JUnit testing, so that we can assert that there has been no errors.

我想你和我们的情况差不多。在生产环境中进行更复杂的日志记录,但是在JUnit测试期间进行更简单的日志记录,这样我们就可以断言没有错误。

There are cleaner solutions using builders if you are using log4j2 > 2.4 (but then, no support for Java6), but this is the one that I have got working with log4j2 2.3:

如果您正在使用log4j2 > 2.4(但是,没有对Java6的支持),那么有更干净的解决方案可以使用构建器,但是这是我在使用log4j2 2.3时得到的解决方案:

@Test
public void testClass() {
    LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);

    Configuration configuration = loggerContext.getConfiguration();
    LoggerConfig rootLoggerConfig = configuration.getLoggerConfig("");
    ListAppender listAppender = new ListAppender("testAppender");

    rootLoggerConfig.addAppender(listAppender, Level.ALL, null);

    new TestClass();    //this is doing writing an error like org.slf4j.LoggerFactory.getLogger(TestClass.class).error("testing this");

    assertEquals(1, listAppender.getEvents().size());
}

Important to note that we need to pass "false" when calling getContext, as otherwise it seems not to be getting the same context as slf4j.

需要注意的是,在调用getContext时,我们需要传递“false”,否则它似乎不会获得与slf4j相同的上下文。

#3


0  

Daniele, a ListAppender exists in Log4J-2.0 (package org.apache.logging.log4j.test.appender). It is part of the distribution, but it is in the log4j-core-tests jar. It is mostly used for JUnit tests. The JUnit test source also has sample configurations showing how to configure with this ListAppender. A sample config looks something like this:

一个ListAppender存在于Log4J-2.0(包org.apache. logger .log4j.test.appender)中。它是发行版的一部分,但是在log4j-core-tests jar中。它主要用于JUnit测试。JUnit测试源也有样例配置,显示如何使用这个ListAppender进行配置。一个示例配置如下所示:

<Configuration status="warn" packages="org.apache.logging.log4j.test">
  <Appenders>
    <List name="MyList">
    </List>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="MyList"/>
    </Root>
  </Loggers>
</Configuration>

#1


6  

Accessing and manipulating log4j2 over slf4j by code/at runtime:

通过代码/运行时在slf4j*问和操作log4j2:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Log4j2OverSlf4jConfigurator {

    final private static Logger LOGGER = LoggerFactory.getLogger(Log4j2OverSlf4jConfigurator.class);

    public static void main(final String[] args) {
        LOGGER.info("Starting");
        LoggerContext loggerContext = (LoggerContext) LogManager.getContext();
        Configuration configuration = loggerContext.getConfiguration();

        LOGGER.info("Filepath: {}", configuration.getConfigurationSource().getLocation());
        // Log4j root logger has no name attribute -> name == ""
        LoggerConfig rootLoggerConfig = configuration.getLoggerConfig("");

        rootLoggerConfig.getAppenders().forEach((name, appender) -> {
            LOGGER.info("Appender {}: {}", name, appender.getLayout().toString());
            // rootLoggerConfig.removeAppender(a.getName());
        });

        rootLoggerConfig.getAppenderRefs().forEach(ar -> {
            System.out.println("AppenderReference: " + ar.getRef());
        });

        // adding appenders
        configuration.addAppender(null);
    }
}

Reference: https://logging.apache.org/log4j/2.x/manual/customconfig.html

参考:https://logging.apache.org/log4j/2.x/manual/customconfig.html

#2


1  

I think you are having a similar scenario as ours. A more complex logging in production, but a simpler one during JUnit testing, so that we can assert that there has been no errors.

我想你和我们的情况差不多。在生产环境中进行更复杂的日志记录,但是在JUnit测试期间进行更简单的日志记录,这样我们就可以断言没有错误。

There are cleaner solutions using builders if you are using log4j2 > 2.4 (but then, no support for Java6), but this is the one that I have got working with log4j2 2.3:

如果您正在使用log4j2 > 2.4(但是,没有对Java6的支持),那么有更干净的解决方案可以使用构建器,但是这是我在使用log4j2 2.3时得到的解决方案:

@Test
public void testClass() {
    LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);

    Configuration configuration = loggerContext.getConfiguration();
    LoggerConfig rootLoggerConfig = configuration.getLoggerConfig("");
    ListAppender listAppender = new ListAppender("testAppender");

    rootLoggerConfig.addAppender(listAppender, Level.ALL, null);

    new TestClass();    //this is doing writing an error like org.slf4j.LoggerFactory.getLogger(TestClass.class).error("testing this");

    assertEquals(1, listAppender.getEvents().size());
}

Important to note that we need to pass "false" when calling getContext, as otherwise it seems not to be getting the same context as slf4j.

需要注意的是,在调用getContext时,我们需要传递“false”,否则它似乎不会获得与slf4j相同的上下文。

#3


0  

Daniele, a ListAppender exists in Log4J-2.0 (package org.apache.logging.log4j.test.appender). It is part of the distribution, but it is in the log4j-core-tests jar. It is mostly used for JUnit tests. The JUnit test source also has sample configurations showing how to configure with this ListAppender. A sample config looks something like this:

一个ListAppender存在于Log4J-2.0(包org.apache. logger .log4j.test.appender)中。它是发行版的一部分,但是在log4j-core-tests jar中。它主要用于JUnit测试。JUnit测试源也有样例配置,显示如何使用这个ListAppender进行配置。一个示例配置如下所示:

<Configuration status="warn" packages="org.apache.logging.log4j.test">
  <Appenders>
    <List name="MyList">
    </List>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="MyList"/>
    </Root>
  </Loggers>
</Configuration>