web-dev-qa-db-ja.com

Log4j2エラーのあるSLF4jクラスクラスのファクトリメソッドを呼び出すことができません...要素RollingFileのRollingFileAppender

TestNGでいくつかのWebDriverテストを並行して実行しています。そして、次のようなディレクトリ構造で実行されるテストごとに、ログログを個別のファイルに記録できるようにしたいと思います。

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");
    }
}
4
James Affleck

すでにローリングファイルアペンダーを使用してMDCロギングを無料で取得している場合、Log4j2はステロイドでポンプされているようです。

いずれにせよ、log4jスニペットは奇妙に見えます。終了アペンダー要素タグは表示されますが、対応する開始アペンダータグは表示されません。

ローリングファイルのアペンダー名には、動的テスト名とテスト開始時間の間にスペースがあるようです。

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

提案:分割統治法はどうですか。

そのようなタイプの動的構成が実際にサポートされている場合。まず、動的なパターンでファイル名のみを構成してみませんか?

問題を解決するための最も単純な構成を取得する前に、log4j構成を完全なステロイドに配置しているようです。

したがって、休憩時間に足を踏み入れて、次の取得に集中してください:fileName = "target/logs/dummyTest_dynamicComponent _ $ {ctx:testName} .log"

あなたのために働くこと。

Log4j 1.xバージョンでは、バグのある構成を理解するのに役立つlog4j.debugシステムプロパティがあり、出力は非常に便利でした。

最後に、log4j 1.Xバージョンで使用する機能では、独自のMDCアペンダーを明示的にプログラムする必要があります。 MDCアペンダーは通常、RollingFileAppendersをインスタンス化してファイルにログインし、ユーザーが入力したMDCコンテキスト(keyxValue)ペアを利用します。

しかし、あなたがしていることは有望に見えます。それがうまくいかない場合は、構成の複雑さのレベルを下げるだけです。

最後に、次のエラーが発生したときにログファイルが作成されるのを見ると、非常に驚​​きます。

エラー要素RollingFileのクラスクラスorg.Apache.logging.log4j.core.appender.RollingFileAppenderのファクトリメソッドを呼び出すことができません。 RollingFileタイプのAppenderを作成できません。

Log4jはあなたに言っています:ねえ、あなたが定義しているそのアペンダー。この構成をスワルログしようとしている私のファクトリはそれを処理できず、この構成でローリングファイルアペンダーをインスタンス化しません。

そのため、その構成を修正する必要があります。


答えに追加します。

ここに、必要なことを実行するためのLog4j2構成があります。

最初のスニペットは、log4j 2構成の場合、ルートロガーには3つの異なるアペンダーが与えられて再生されることがわかります。あなたは主にアペンダー3に関心がありますが、他の2つのアペンダーはあなたの典型的な出発点です。

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

この最後のアペンダーは、次のスレッドに基づいて構成されています: https://issues.Apache.org/jira/browse/LOG4J2-129

2番目のスニペットは、基本的なアーキタイプから新しいMavenプロジェクトを作成するときにEclipseから取得するダミーのjunitテストです。テストスニペットでは、スニペットの場合と同様に、スタックオーバーフローコンテキストコンテキストがスレッドコンテキストに設定されていることがわかります。

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

最後のスニペットはmavenの依存関係です。

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

Log4jがこの新しいルーティングアペンダーをもたらすのは面白いと思います。この種のことを行うために、MDCコンテキストサポートを備えた独自のローリングファイルアペンダーを実装しなければならなかった人の数を想像できれば。 Webアプリで非常に便利です。

乾杯。

5
99Sono