SLF4j with Log4j2 ERROR Cannot call factory method in class class ... RollingFileAppender for element RollingFile

I have several WebDriver tests running in parallel in TestNG. And I want to have a logging log for a separate file for each test, which runs in the directory structure as follows:

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 test?

I tried using RollingFileAppender, but it doesn't seem to be designed to have individual instances run for individual log files such as what I'm trying to do here.

And I get 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"); } } 
+1
source share
1 answer

Log4j 2 seems to get pumped up on steroids if you started MDC logging for free already using the copied file.

Anyway, your log4j fragment looks weird. We see the element tag of the closing appender, but not its corresponding opening appender tag.

Your download file name has a space between the dynamic test name and the test start time.

fileName = "target / logs / $ {ctx: packageTimestamp} / $ {ctx: testName ($ {ctx: testStartTime}) log"

Suggestion: How about you sharing the conqueror.

If this type of dynamic configuration is really supported. Why are you not trying to configure only the dynamic template file name first?

It seems you are putting your log4j configuration on full steroids before you have the easiest configuration possible for your problem.

So, put your feet on the break and focus on getting: FileName = "target / logs / dummyTest_dynamicComponent _ $ {CTX: ASMAP} .log"

To work for you.

In log4j 1.x, you will have the log4j.debug system property to help you understand the bugy configuration, and the result was very useful.

Finally, in log4j 1.X, the function you want to use will require you to explicitly program the MDC application. Your MDC binder usually creates an instance of RollingFileAppenders to enter the files, and you will use the MDC context pairs (keyxValue) entered by the user.

But what you do looks promising, just reduce the complexity level of your configuration if it does not work for you.

Finally, I would be very surprised if you saw that any log file is created when the following error occurs:

ERROR Cannot call factory method on class class org.apache.logging.log4j.core.appender.RollingFileAppender on the RollingFile element. Failed to create Appender type RollingFile.

Log4j tells you: Hey, this is the application that you define. My factory trying to execute this procedure will not be able to process it, and I will not create an instance of the copied file with this configuration.

So you have to fix this configuration.


Adding an answer.

Here you have a working Log4j 2 configuration to accomplish what you want:

The first fragment, if the configuration is log4j 2, where you will see that the root logger is given 3 different applications for playback. You mainly care about appender 3, but the other two add-ons are a more typical starting point.

 <?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <!-- APPENDER 1: CONSOLE --> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> </Console> <!-- APPENDER 2: ROLLING FILE --> <RollingFile name="AppenderTwo" fileName="target/logs/test.log" filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz"> <PatternLayout> <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> </PatternLayout> <Policies> <TimeBasedTriggeringPolicy /> <SizeBasedTriggeringPolicy size="10 MB" /> </Policies> </RollingFile> <!-- APPENDER 3: ROUTING APPENDER --> <Routing name="AppenderThree"> <Routes pattern="${ctx:stackOverFlow}"> <!-- Route Nr.1 --> <Route> <!-- Rolling file appender for route Nr.1 --> <RollingFile name="NestedAppender-${ctx:stackOverFlow}" fileName="target/logs/test_threadContext_${ctx:stackOverFlow}.log" filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz"> <PatternLayout> <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> </PatternLayout> <Policies> <TimeBasedTriggeringPolicy /> <SizeBasedTriggeringPolicy size="10 MB" /> </Policies> </RollingFile> </Route> <!-- Route Nr.2 fallback --> <!-- By having this set to ${ctx:filename} it will match when filename is not set in the context --> <Route ref="Console" key="${ctx:stackOverFlow}" /> </Routes> </Routing> </Appenders> <Loggers> <Root level="all"> <AppenderRef ref="Console" /> <AppenderRef ref="AppenderTwo" /> <AppenderRef ref="AppenderThree" /> </Root> </Loggers> </Configuration> 

This latest appender is configured based on the following thread: https://issues.apache.org/jira/browse/LOG4J2-129

The second snippet is a dummy junit test, which you exit from the eclipse when you create a new maven project from the base archetype. In the test fragment, you will see that the context of the stack context in the stream is set to the stream context, as in your fragments.

 package stackoverflow.test.tutorial; import org.apache.logging.log4j.ThreadContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class AppTest extends TestCase { private static final Logger LOGGER = LoggerFactory.getLogger(TestCase.class); /** * Create the test case * * @param testName * name of the test case */ public AppTest(String testName) { super(testName); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite(AppTest.class); } /** * Rigourous Test :-) */ public void testApp() { ThreadContext.put("stackOverFlow", "dummyContextValue"); LOGGER.info("LALAL LLA"); assertTrue(true); } } 

The last snippet is the maven dependencies:

 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>stackoverflow.test</groupId> <artifactId>tutorial</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>tutorial</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.12</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project> 

I find it funny that log4j contributes this new Routing Appender. If you could imagine how many people had to implement their own copied file applications with MDC context support to make this type of material. This is very useful in web applications.

Greetings.

+2
source

Source: https://habr.com/ru/post/1205162/


All Articles