【发布时间】:2016-03-22 00:41:46
【问题描述】:
我有一些 WebDriver 测试在 TestNG 中并行运行。而且我希望能够为在这样的目录结构中运行的每个测试将日志记录到一个单独的文件中:
target\logs\TestNGSuiteName(SuiteStartTime)
Test1ClassName.TestMethod1 (TestStartTime).log
Test1ClassName.TestMethod2 (TestStartTime).log
等等。
使用 Log4j 和 SLF4j 是否可以为每个单独的 TestNG 测试创建单独的日志文件?
我曾尝试使用 RollingFileAppender,但它看起来不像是为单独的日志文件运行单独的实例而设计的,就像我在这里尝试做的那样。
我得到了错误
ERROR Unable to invoke factory method in class class org.apache.logging.log4j.core.appender.RollingFileAppender for element RollingFile.
Unable to create Appender of type RollingFile.
Log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Routing name="Routing">
<Routes pattern="$${ctx:ROUTINGKEY}">
<Route>
<RollingFile name="Rolling-${ctx:ROUTINGKEY}"
fileName="target/logs/${ctx:suiteTimestamp}/${ctx:testName} (${ctx:testStartTime}).log"
filePattern="target/logs/${ctx:testname} ${ctx:testStartTime}_%i.log.gz">
<PatternLayout>
<pattern>%d{HH:mm:ss.SSS} [%t] %p %c{3} - %m%n</pattern>
</PatternLayout>
<Policies> <!-- 6 hour rollover-->
<TimeBasedTriggeringPolicy interval="6" modulate="true"/>
<SizeBasedTriggeringPolicy size="10 MB"/>
</Policies>
</RollingFile>
</Route>
</Routes>
</Routing>
</Appenders>
<Loggers>
<Logger name="james.log" level="debug" additivity="false">
<AppenderRef ref="Routing"/>
</Logger>
</Loggers>
</Configuration>
LumberJack.java
package james.log;
import james.util.ConcurrentDateFormatAccess;
import org.apache.logging.log4j.ThreadContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.ITestContext;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.lang.reflect.Method;
/**
* @author james.affleck
*/
public class LumberjackTest {
private static final Logger logger = LoggerFactory.getLogger(LumberjackTest.class);
private static ThreadLocal<String> methodLogName = new ThreadLocal<>();
private static String suiteName = "";
@BeforeMethod
public void loggerDoTheThings(ITestContext context, Method method) {
if(suiteName.isEmpty()) {
String suite = context.getSuite().getName() + "(";
String suiteTime = new ConcurrentDateFormatAccess().getCurrentDateSPrecision();
suite += suiteTime + ")";
suiteName = suite;
}
// Test filename = testClass.testMethodname
String classname = this.getClass().getName();
classname = classname.substring(classname.lastIndexOf(".") + 1); //get rid of package info we don't care about
String testName = classname + "." + method.getName();
// Using this to store logger instance for later
String testStart = new ConcurrentDateFormatAccess().getCurrentDateMSPrecision();
methodLogName.set(testName + testStart);
ThreadContext.put("suiteTimestamp", suiteName);
ThreadContext.put("testName", testName);
ThreadContext.put("testStartTime", testStart);
}
@AfterMethod
public void closeTheThings() {
methodLogName.set(null);
}
@AfterSuite
public void closeSuite() {
suiteName = null;
}
@Test
public void testLog1() {
logThings();
}
@Test
public void testLog2() {
logThings();
}
public void logThings() {
logger.info("info message");
logger.debug("debug message");
logger.warn("warn message");
}
}
【问题讨论】:
-
您有带有错误消息的堆栈跟踪吗?目前尚不清楚为什么会附加错误。
-
嗨 Julien,我刚刚再次运行代码,没有堆栈跟踪。日志文件已正确创建,但没有写入任何内容。我不确定我在这里做错了哪一部分,但我认为这可能与这一行有关:LoggerFactory.getLogger(LumberjackTest.class);或者可能是 RollingFile appender 不是执行此操作的正确方法。我想不出通过使用 File Appender 传递如下变量来获取日志文件名称的方法。
-
@JulienHerr fileName="target/logs/${ctx:suiteTimestamp}/${ctx:testName(${ctx:testStartTime}).log"
-
也许我需要使用configuration factory 处理某些事情,或者我必须使用loggerConfiguration / Logmanager 做一些事情。文档有点难以理解,感觉就像从消防水管中喝水一样。如果有一种简单的方法可以使用 LogBack 而不是 Log4j 或者不使用 SLF4j 外观。我也对这些选项持开放态度。
-
见stackoverflow.com/q/25148492/3255152。您需要将其调整为测试而不是类,但这应该不会太难。您可以使用 TestListener 来帮助管理记录器的创建等。