I have some WebDriver tests running in parallel in TestNG. And I want to be able to have logging log to a separate file for each test that's run in a directory structure like this:
target\logs\TestNGSuiteName(SuiteStartTime)
Test1ClassName.TestMethod1 (TestStartTime).log
Test1ClassName.TestMethod2 (TestStartTime).log
etc.
Using Log4j and SLF4j is it possible to create a separate log file for each individual TestNG tests?
I have attempted using a RollingFileAppender but it doesn't look like that is designed for having separate instances run for separate log files like I'm attempting to do here.
And I'm getting the errors
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");
}
}