2016-03-22 17 views
1

Ich habe einige WebDriver Tests in TestNG parallel ausgeführt. Und ich möchte in der Lage für jeden Testprotokollierung Protokoll in eine separate Datei zu haben, die in einer Verzeichnisstruktur wie folgt laufen gelassen:SLF4j mit Log4j2 ERROR Factory-Methode in Klasse Klasse kann nicht aufgerufen werden ... RollingFileAppender für Element RollingFile

target\logs\TestNGSuiteName(SuiteStartTime) 
    Test1ClassName.TestMethod1 (TestStartTime).log 
    Test1ClassName.TestMethod2 (TestStartTime).log 

usw.

Mit Log4j und SLF4J ist es möglich, eine separate zu erstellen Protokolldatei für jeden einzelnen TestNG-Test?

Ich habe versucht, einen RollingFileAppender zu verwenden, aber es sieht nicht so aus, dass für separate Instanzen getrennte Protokolldateien ausgeführt werden sollen, wie ich hier versuche.

Und ich bin immer die Fehler

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"); 
    } 
} 
+0

Haben Sie einen Stacktrace mit der Fehlermeldung? Es ist nicht klar, warum der Fehler angehängt wird. – juherr

+0

Hallo Julien, ich habe gerade den Code erneut ausgeführt, und es gibt keine Stack-Trace. Die Protokolldateien werden ordnungsgemäß erstellt, aber ihnen wird nichts geschrieben. Ich bin mir nicht sicher, welchen Teil ich hier falsch mache, aber ich denke, dass es mit dieser Zeile zu tun haben kann: LoggerFactory.getLogger (LumberjackTest.class); Oder der RollingFile Appender ist nicht der richtige Weg, dies zu tun. Ich konnte keinen Weg finden, Log-Dateien-Namen zu erhalten, indem ich eine Variable wie die folgende mit einem Datei-Appender übergab. –

+0

@JulienHerr fileName = "target/logs/$ {ctx: suiteTimestamp}/$ {ctx: testName ($ {ctx: testStartTime}). Log" –

Antwort

2

Log4j 2 scheint auf Steroiden gepumpt werden, wenn Sie erhalten MDC-Protokollierung bereits kostenlos mit der rollenden Datei appen die.

In jedem Fall sieht Ihr log4j-Ausschnitt seltsam aus. Wir sehen das schließende Appender-Element-Tag, aber nicht das entsprechende öffnende Appender-Tag.

Ihr Rolling File Appender Name scheint ein Leerzeichen zwischen dem Namen des dynamischen Tests und der Startzeit des Tests zu enthalten.

filename = "target/logs/$ {ctx: suiteTimestamp}/$ {ctx: Testname ($ {ctx: testStartTime}). Log"

Vorschlag: Wie über Sie erobern teilen.

Wenn diese Art der dynamischen Konfiguration tatsächlich unterstützt wird. Warum versuchst du nicht erst den Dateinamen mit einem dynamischen Muster zu konfigurieren?

Sie scheinen Ihre log4j-Konfiguration auf volle Steroide zu setzen, bevor Sie die einfachste Konfiguration für Ihr Problem haben.

So ein Fuß auf die Pause setzen und konzentrieren sich auf immer die: filename = "target/logs/dummyTest_dynamicComponent _ $ {ctx: Testname} .log"

für Sie arbeiten.

In log4j 1.x-Version hätten Sie die log4j.debug-Systemeigenschaft, die Ihnen bei der Berechnung der Buggy-Konfiguration hilft, und die Ausgabe war sehr, sehr nützlich.

Schließlich, in der Version log4j 1.X müssen Sie explizit einen eigenen MDC-Appender programmieren. Ihr MDC-Appender würde RollingFileAppenders normalerweise instanziieren, um sich in Dateien anzumelden, und Sie würden auf die vom Benutzer gesetzten MDC-Kontext (keyxValue) -Paare tippen.

Aber was Sie tun, sieht vielversprechend aus, reduzieren Sie einfach den Grad der Komplexität Ihrer Konfiguration, wenn es für Sie nicht funktioniert.

Schließlich wäre ich sehr überrascht, wenn Sie eine Protokolldatei erstellt sehen würde geting, wenn Sie die folgenden Fehler haben:

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.

Log4j Sie sagt: Hey, das appender Sie definieren. Meine Factory, die versucht, diese Konfiguration zu verschlingen, kann damit nicht umgehen und ich werde einen Rolling File Appender mit dieser Konfiguration nicht instanziieren.

Also müssen Sie diese Konfiguration beheben.


Hinzufügen zur Antwort.

Hier haben Sie eine funktionierende Log4j 2-Konfiguration für das, was Sie wollen:

Erste Schnipsel, wenn die log4j 2-Konfiguration, in der Sie sehen, dass die Wurzel Logger 3 verschiedenen Appen gegeben um zu spielen. Sie kümmern sich hauptsächlich um Appender 3, aber die anderen beiden Appender sind mehr von Ihrem typischen Ausgangspunkt.

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

Dieser letzte appender ist auf dem folgenden Threads konfiguriert basiert: https://issues.apache.org/jira/browse/LOG4J2-129

Der zweite Schnipsel ist ein Dummy junit Test, den Sie aus Eclipse erhalten, wenn Sie ein neues Maven Projekt aus einem Grund Urbild erstellen . Im Test-Snippet sehen Sie, dass der Stack-über-Fluss-Kontext in den Thread-Kontext eingefügt wird, wie Sie es in Ihren Snippets tun.

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); 
    } 
} 

Der letzte Schnipsel sind die Maven-Abhängigkeiten:

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

ich funy finden, dass log4j in diesem neuen Routing Appender bringt. Wenn Sie sich vorstellen können, wie viele Leute ihre eigenen rollenden Dateiapplikatoren mit MDC-Kontextunterstützung implementieren mussten, um diese Art von Dingen zu erledigen. Es ist sehr nützlich in Web-Apps.

Prost.

+2

Vielen Dank für Ihre Hilfe. mit deinem Kommentar 'Log4j sagt dir: Hey, der Appender, den du definierst. Meine Factory, die versucht, diese Konfiguration zu verschlucken, kann damit nicht umgehen, und ich werde keinen Rolling File Appender mit dieser Konfiguration instanziieren. Das hilft ein bisschen. Ich habe auch versucht, eine viel einfachere Datei zu erstellen (nur mit einer einzigen Variablen für den Dateinamen, filename = "c: \ users \ me \ logging-setup/$ {ctx: methodname} .log" Aber das führte keinen Erfolg mehr –

+0

Ich habe versucht, den Konfigurationsstatus zu verfolgen (ich sah, dass irgendwo in der Dokumentation), die mir mehr Log4J2 Logging, aber leider gab mir keine spezifischeren Informationen über diesen bestimmten Fehler. : -/ –

+0

Sie haben mir eine Idee gegeben: Ich habe die Dokumentation log4j1.2 gefunden, die viel weniger komplex aussieht, und ich denke, dass das Lesen auf einer grundlegenderen Ebene versteht, wie log4j aufgebaut ist und wie alle Teile passen müssen zusammen (wie Konfigurationen, Appender, Filter usw.) Danke für die Idee! –