当前位置: 首页 > 知识库问答 >
问题:

使用slf4j和log4j2动态添加appender

沈枫涟
2023-03-14

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

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

我尝试了什么(为了简单起见,我创建了一个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() );
      }

  }

这是我最基本的附录:

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

  }

我能做些什么来使它工作?也许我从错误的角度来处理这个问题,但是我想避免创建我自己的日志记录类。非常感谢任何帮助。

共有3个答案

夏侯宏旷
2023-03-14

如果其他人对此感到困惑,那么slf4j版本1.7.26需要一个构建器模式,该模式与公认的答案相关。以下实现适用于java 8和scala 2.13(与lqbweb的答案略有不同):

爪哇:

java prettyprint-override">import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.core.LoggerContext

LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false)

Configuration configuration = loggerContext.getConfiguration()
LoggerConfig rootLoggerConfig = configuration.getLoggerConfig("")

PatternLayout.Builder layoutBuilder = PatternLayout.newBuilder()
layoutBuilder.withPattern("%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n")

RandomAccessFileAppender.Builder<?> appenderBuilder =
(RandomAccessFileAppender.Builder<?>) RandomAccessFileAppender
      .newBuilder()

appenderBuilder.setLayout(layoutBuilder.build())
appenderBuilder.withBufferedIo(true)
appenderBuilder.setFileName(logFile.getAbsolutePath)
appenderBuilder.setName("OutputDirFileLogger")

RandomAccessFileAppender appender = appenderBuilder.build()

appender.start() // important to make the appender usable instantly 


rootLoggerConfig.addAppender(appender, Level.DEBUG, null)

scala:

    import org.apache.logging.log4j.LogManager
    import org.apache.logging.log4j.Level
    import org.apache.logging.log4j.core.LoggerContext

    val loggerContext = LogManager.getContext(false).asInstanceOf[LoggerContext]

    val configuration: Configuration = loggerContext.getConfiguration
    val rootLoggerConfig: LoggerConfig = configuration.getLoggerConfig("")

    val layoutBuilder: PatternLayout.Builder = PatternLayout.newBuilder()
    layoutBuilder.withPattern("%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n")

    val appenderBuilder: RandomAccessFileAppender.Builder[_] = RandomAccessFileAppender
      .newBuilder()
      .asInstanceOf[RandomAccessFileAppender.Builder[_]]

    appenderBuilder.setLayout(layoutBuilder.build())
    appenderBuilder.withBufferedIo(true)
    appenderBuilder.setFileName(logFile.getAbsolutePath)
    appenderBuilder.setName("OutputDirFileLogger")

    val appender: RandomAccessFileAppender = appenderBuilder.build()
    appender.start()

    rootLoggerConfig.addAppender(appender, Level.DEBUG, null) 
孔和风
2023-03-14

通过代码/在运行时通过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(false);
        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);
    }
}

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

司寇祺
2023-03-14

我认为您的情况与我们的类似。生产中的日志更复杂,但在JUnit测试期间更简单,这样我们就可以断言没有错误。

如果您使用的是log4j2,那么就有使用构建器的更清洁的解决方案

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

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

 类似资料:
  • 我目前正在尝试用log4j/slf4j和java 11构建一个应用程序,但在运行时我面临这个问题: 此问题的根源代码是我的记录器的初始化: 我依赖于: org.apache.logging.log4j: log4j-api: 2.11.1 org.apache.logging.log4j: log4j-core: 2.11.1 org.apache.logging.log4j: log4j-slf

  • SLF4J强制应用程序记录字符串。Log4J2 API支持记录任何CharSequence(如果您想记录文本),但也支持按原样记录任何对象。 Log4j 2 API支持日志记录消息对象、Java 8 lambda表达式和无垃圾日志记录(它在日志记录CharSequence对象时避免创建vararg数组和字符串)。

  • 我想使用slf4j而不是Log4J。我在我的pom.xml中添加了以下依赖项(我对slf4j使用了1.7.25,对log4j2使用了2.10.0): 一切都构建得很好,没有编译错误或缺乏依赖,但我未能在类中指定负责日志程序初始化的配置(log4j2.xml)文件。在这种情况下,它总是打印相同的警告 我搜索了一种提供配置文件的适当方法,结果是: 问题是,在我的例子中,LogManager.getCo

  • 问题内容: 因此,我尝试按照其网站上的此(非Maven实现)和要求将slf4j添加到log4j。并尝试使用此代码 并将以下内容添加到我的库中 log4j-api-2.3.jar log4j-core-2.3.jar log4j-sl4j-impl-2.3.jar log4j-to-sl4j-2.3.jar slf4j-api-1.7.12.jar 当我尝试运行它时,出现以下错误 知道我要去哪里错了

  • 问题内容: 我在我的项目中使用log4j2和slf4j并使用maven进行构建。我正在使用以下pom文件(仅显示了相关依赖项),但是我在此pom文件的下面复制了错误- 我需要添加/删除所有内容才能使其正常工作。我已经访问了错误中的url以及log4j2依赖项页面,因此请不要仅仅指向响应中的URL。 信息: pom文件 更新: 我在pom文件中添加了以下依赖项,并且在mavenrepository中

  • 问题内容: 我使用指令创建联系表格。最初我创建用于显示客户表单的customerForm指令。在这种形式下,我有一个按钮,当我们单击添加按钮时,称为getData函数,该函数内部使用newDirective显示ul列表。为此,我使用$ compile api编译html代码。很好,当我们单击“删除”按钮时,它也显示列表值和“删除”按钮,它称为scope.remove()函数。但是它只能删除一个。之