Maven Surefire 中的 Log4j2 自定义 Appender:可以跨线程重用吗?

问题描述 投票:0回答:1

我有一个 Log4j2 自定义附加程序,它正在我的许多 JUnit5 测试中使用。

(就其价值而言,我在几篇文章中发现了该代码,并且只做了一些细微的调整。)

在 Surefire 中运行时,如果我使用默认的 Surefire 插件设置,作为被测系统的一部分生成的日志不会产生预期值,因为它使用多个线程来加快速度。

具体来说,自定义附加程序的输出流一次包含我所期望的内容,而其他流则为空。

此行为与 Surefire 的并行化相一致:如果我将

reuseForks
设置为
false
,一切都会正常工作,但测试套件的运行速度当然要慢得多。

我已经尝试同步日志扩展中的方法,但无济于事(我认为这是有道理的,因为我很可能受到

LogManager
及其返回的上下文的支配,而每个测试都应该实例化注册的扩展在它自己的线程中)。我可能误解了某些东西或遗漏了一些明显的东西 - 或者也许我想做的事情是不可能的,我应该使用 Spring 的
OutputCaptureExtension
(这对我来说不太理想)。

任何帮助将不胜感激。

提前谢谢您。

Logger扩展类:

public class LoggerExtension implements BeforeEachCallback, AfterEachCallback {

    private static final String APPENDER_NAME = "TestAppender";
    private ByteArrayOutputStream outContent;

    @Override
    public void beforeEach(ExtensionContext context) {
      if (outContent == null) { 
        outContent = new ByteArrayOutputStream();
      } else {
        outContent.reset();
      }
      
      LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
      loggerContext.getConfiguration().getRootLogger().setLevel(Level.INFO);
      Appender appender = WriterAppender.newBuilder()
              .setName(APPENDER_NAME)
              .setTarget(new OutputStreamWriter(outContent))
              .build();
      appender.start();
      loggerContext.getConfiguration().addAppender(appender);
      loggerContext.getRootLogger().addAppender(loggerContext.getConfiguration().getAppender(appender.getName()));
      loggerContext.updateLoggers();
    }

    @Override
    public void afterEach(ExtensionContext context) {
        LoggerContext loggerContext = (LoggerContext) LogManager.getContext();
        Configuration configuration = loggerContext.getConfiguration();
        configuration.getRootLogger().removeAppender(APPENDER_NAME);
    }

    public void verifyMessage(String message) {
        // outContent is ony valid once; every other time it is empty
        assertThat(outContent.toString(), containsString(message));
    }
}

测试用例示例:

public class TestCase {
  @RegisterExtension
  public static LoggerExtension logVerifier = new LoggerExtension();

  // ...

  @Test
  public void shouldDoSomething() {
    // code that generates logs here

    logVerifier.verifyMessage("match the log string");
  }
}
java spring log4j2 junit5 maven-surefire-plugin
1个回答
0
投票

每个追加器都需要有一个唯一的名称。尝试添加多个具有相同名称的附加程序最终会导致其中一些附加程序被忽略。

为每个附加程序选择不同的名称,应该可以解决空输出流的问题:

      Appender appender = WriterAppender.newBuilder()
              .setName(context.getUniqueId())
              .setTarget(new OutputStreamWriter(outContent))
              .build();

备注:在当前设置中,每个测试附加程序还将包含同时运行的其他测试的输出。要解决这个问题,您应该阅读log4j-user@logging

上的此线程。

要解决这个问题:

  • 如果您使用实例
    Logger
    字段,您应该考虑使用我们的
    @LoggerContextSource
    为每个测试提供一个记录器上下文,并将
    Logger
    实例注入到您的服务类中,
  • 如果您使用静态
    Logger
    字段,则基于 ListAppender
     值创建 
    ThreadContext
     的路由附加程序应该可以工作。您只需要为需要多个线程的测试适当地传播线程上下文映射即可。
© www.soinside.com 2019 - 2024. All rights reserved.