【问题标题】:How to create separate Log4j2 rolling file appenders and loggers programatically for multiple threads如何以编程方式为多个线程创建单独的 Log4j2 滚动文件附加程序和记录器
【发布时间】:2017-09-28 19:57:21
【问题描述】:
我正在多个线程上运行我的 TestNG 测试(同时在多个设备上进行 Appium 测试),并希望在不同文件中的不同线程上编写测试日志。这里的线程是在测试流程开始之前自动创建的。
所以我想以编程方式创建单独的附加程序和单独的记录器,以便每个附加程序仅附加到其自己的线程,然后在一个线程中创建的记录器将仅在该线程中创建附加程序。
请告诉我如何逐步实现它。
【问题讨论】:
标签:
multithreading
logging
testng
log4j2
【解决方案1】:
首先,这感觉像是XY Problem,尤其是因为您没有提供任何理由说明您为什么要使用程序化解决方案。
可以在逐个线程的基础上实现单独的日志文件,而无需以编程方式创建记录器和附加程序。因为我相信这是一个更优化的解决方案,所以我将提供一个演示来说明它是如何完成的。
说明
此解决方案将使用RoutingAppender 和ThreadContext 为每个Thread 创建附加程序。下面的示例将使用简单的FileAppender,但您可以非常轻松地换入RollingFileAppender。由于您没有声明您有任何要求为每个线程使用不同的日志级别,因此以下示例将不会实现此功能。最后,该示例不会使用 TestNG,因为它有一些设置开销;相反,将使用一个带有创建两个线程的 main 的简单类。
示例实现
log4j2.xml 文件:
<?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.txt"
name="appender-${ctx:threadName}">
<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="Thread" level="TRACE" additivity="false">
<AppenderRef ref="STDOUT" />
<AppenderRef ref="MyRoutingAppender" />
</Logger>
<Root level="WARN">
<AppenderRef ref="STDOUT" />
</Root>
</Loggers>
</Configuration>
使用 2 个并发线程生成日志的 Java 类:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;
public class MultiThreadLog4j2SepFilesMain {
//Create a lock to use for synchronizing the getting of the logger
private static final Object lock = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable(){
public void run() {
//Set up the context before getting logger
ThreadContext.put("threadName", Thread.currentThread().getName());
//Get the logger for this thread
Logger log = null;
synchronized(lock){
log = LogManager.getLogger(Thread.currentThread().getName());
}
//Generate some logs
log.info("here's the first thread");
//Wait a while so that threads interleave
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Generate more logs
log.debug("some debug in first thread");
log.info("finishing first thread");
}}, "Thread.1"); //Use a name that will allow us to use Thread.getName when getting the logger inside the thread
Thread t2 = new Thread(new Runnable(){
public void run() {
//Set up the context before getting logger
ThreadContext.put("threadName", Thread.currentThread().getName());
//Get logger for this thread
Logger log = null;
synchronized(lock){
log = LogManager.getLogger(Thread.currentThread().getName());
}
//Generate some logs
log.info("here's the second thread");
log.debug("some debug in second thread");
}}, "Thread.2"); //Use a name that will allow us to use Thread.getName when getting the logger inside the thread
//Start both threads
t1.start();
t2.start();
}
}