Java 使用 slf4j 和 log4j2 动态添加 appender

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22866275/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 18:30:54  来源:igfitidea点击:

Dynamically add appender with slf4j and log4j2

javaloggingslf4jlog4j2

提问by Daniele Torino

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 LoggerFactoy 检索记录器。

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 测试)并在构造函数中传递一个记录器以供测试类使用。测试类的每个实例都需要它自己的记录器和附加器来保存日志,以便以后可以在 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:

这是我非常基本的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.

我该怎么做才能让它发挥作用?也许我从错误的角度接近这个问题,但我想避免创建自己的记录器类。任何帮助是极大的赞赏。

回答by Remko Popma

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:

Daniele,一个 ListAppender 存在于 Log4J-2.0(包org.apache.logging.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>

回答by SilentMax

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

回答by lqbweb

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 相同的上下文。