Log4j2:在并行测试的情况下清空日志文件

时间:2018-04-24 12:32:16

标签: log4j2 fileappender

我的测试自动化项目存在日志问题。我使用log4j2 logger和FileAppender。我使用它的方式是:

Logger logger = (Logger) LogManager.getLogger(loggerName);
Appender appender = FileAppender.newBuilder()
            .withAppend(false)
            .withBufferedIo(true)
            .withFileName(DIR_NAME + File.separator + loggerName + ".log")
            .withIgnoreExceptions(false)
            .withImmediateFlush(true)
            .withLocking(false)
            .withLayout(PatternLayout.newBuilder().withPattern("%d{HH:mm:ss.SSS}  [%-5level] %msg%n").withCharset(Charset.forName("UTF-8")).build())
            .withName(loggerName)
            .build();

    appender.start();
    logger.addAppender(appender);

当我进行单一测试时,它会起作用。所有数据都在控制台中可见,创建文件并在文件中写入测试日志。如果测试并行运行 - 在不同的线程中出现问题。

在这种情况下,会创建两个不同的记录器和文件追加器。还会创建来自两个文件追加程序的日志文件,并且在控制台中可以看到来自两个测试的日志。一切似乎都很好,但每次其中一个日志文件为空。空日志属于稍后开始的测试。

我怀疑缓存有问题。第一个文件追加器保存所有用于写入的高速缓存,因此第二个无法写入。我对吗?这是什么解决方案?

谢谢。

1 个答案:

答案 0 :(得分:0)

您应该能够在不使用编程配置的情况下实现您的目标。有很多理由不以编程方式配置log4j2,但在我看来,最好的原因是,这样做会使你的代码依赖于log4j2的公共API的一部分。这意味着如果log4j2的实现发生变化,您的代码也必须改变。从长远来看,这为您创造了更多的工作。

因此,考虑到这一点,我将提供一个演示如何使用XML配置文件设置log4j2,以便为每个测试生成单独的日志。我假设,因为在您的问题中未指定,您的目标是使用Test注释为每个方法创建一个日志,并且每个方法都是并行执行的。

首先,这是我的TestNG课程:

package testpkg;

import java.lang.reflect.Method;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class NewTest {
    private static final Logger log = LogManager.getLogger();

    @BeforeMethod
    public void setThreadName(Method method){
        ThreadContext.put("threadName", method.getName());
    }

    @Test
    public void test1() {
        log.info("This is the first test!");
        log.warn("Something may be wrong, better take a look.");
    }

    @Test
    public void test2() {
        log.info("Here's the second test!");
        log.error("There's a problem, better fix it");
    }
}

正如您在此处所见,我有两个Test方法和一个名为BeforeMethod的{​​{1}}。显然,setThreadName方法在每个setThreadName方法之前执行。它使用即将运行的方法的名称将名为Test的密钥放入log4j2 threadName。这将用作log4j2配置文件中日志文件名的一部分。

这是log4j2.xml文件:

ThreadContext

正如您所见,我已设置配置文件以使用<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <Routing name="MyRoutingAppender"> <Routes pattern="$${ctx:threadName}"> <Route> <File fileName="logs/${ctx:threadName}.log" name="appender-${ctx:threadName}" append="false"> <PatternLayout> <Pattern>[%date{ISO8601}][%-5level][%t] %m%n</Pattern> </PatternLayout> </File> </Route> </Routes> </Routing> <Console name="STDOUT" target="SYSTEM_OUT"> <PatternLayout pattern="[%date{ISO8601}][%-5level][%t] %m%n" /> </Console> </Appenders> <Loggers> <Logger name="testpkg" level="TRACE" additivity="false"> <AppenderRef ref="STDOUT" /> <AppenderRef ref="MyRoutingAppender" /> </Logger> <Root level="WARN"> <AppenderRef ref="STDOUT" /> </Root> </Loggers> </Configuration> 在运行时根据RoutingAppender密钥ThreadContext和{{1}动态生成appender }}也用于threadName的{​​{1}}属性。

这是我的testNG配置文件:

threadName

正如您在此处所见,我已对其进行了设置,以便我班级中的每个fileName方法并行运行。

执行时会产生以下控制台输出:

FileAppender

您可以清楚地看到两种方法的输出是交错的,因此我们知道这些方法确实是并行运行的。

测试类的执行还会按预期创建两个日志文件。它们被命名为test1.log和test2.log

以下是他们的内容:

test1.log:

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<suite name="My suite" parallel="methods" thread-count="5" verbose="1">
  <test name="testpkg" >
    <classes>
       <class name="testpkg.NewTest" />
    </classes>
  </test>
</suite>

test2.log:

Test

所以我们在这里看到,正如预期的那样,第一种方法的日志转到了test1.log,第二种方法的日志转到了test2.log

享受!