org.junit.rules.TestName から実行時のテストケースメソッド名を取得して
メソッド名のディレクトリにログ出力をテストケース単位にログファイルとして保存します。
SL4J + log4j2 を使用している場合と、SL4J + logback を使用している場合、
各々退避方法に工夫が必要です。
SL4J + log4j2 を使用している場合、
log4j2.xml に対してテストで使用する test-log4j2.xml が以下であるとします。
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="warn"> <Properties> <Property name="format1">[%d{yyyy-MM-dd HH:mm:ss.SSS}],[%-5p], %c, %m%n</Property> </Properties> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout> <pattern>${format1}</pattern> </PatternLayout> </Console> <!-- ########## test-log4j2.xml だけに記載する ############ --> <File name="file" fileName="/work/console.log"> <PatternLayout> <pattern>${format1}</pattern> </PatternLayout> </File> <!-- ##################################################### --> </Appenders> <Loggers> <Root level="trace"> <AppenderRef ref="Console" /> <!-- ########## test-log4j2.xml だけに記載する ############ --> <AppenderRef ref="file" /> <!-- ##################################################### --> </Root> <Logger name="org.apache.ibatis.transaction" level="WARN" /> <Logger name="org.mybatis.guice.transactional" level="WARN" /> <Logger name="org.myproject" level="INFO" /> </Loggers> </Configuration>
JUnit のソース
@BeforeClass メソッド=このJUnitインスタンス生成時だけ動くメソッドで
test-log4j2.xml で書いた "file" の FileAppender を探し出して、
コンソール標準出力したログファイルパスを記憶させます。
@After メソッドで
LogManager.shutdown(); による一時的なログ出力の中断を発生させて、、
記憶させたログファイルパスから、org.junit.rules.TestName で取得する
テストケースメソッド名に沿って、ログファイルを移動します。
import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.appender.FileAppender; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import org.junit.runner.RunWith; import org.junit.runners.JUnit4;
@RunWith(JUnit4.class) public class SampleTest{ private static String logfilepath; @Rule public TestName testName = new TestName(); @BeforeClass public static void initialize(){ System.setProperty("log4j.configurationFile", "test-log4j2.xml"); try(LoggerContext ctx = (LoggerContext) LogManager.getContext()){ logfilepath = ((FileAppender)ctx.getConfiguration().getAppender("file")).getFileName(); } } @Before public void before() { // TODO } @After public void after() { // LogManager shutdown LogManager.shutdown(); // 退避先を生成 File testOutDir = new File("/logrecord/" + testName.getMethodName()); testOutDir.mkdirs(); // ログファイルを移動 try{ Files.move(Paths.get(logfilepath), Paths.get(testOutDir.getAbsolutePath() + "/console.log"), StandardCopyOption.REPLACE_EXISTING); }catch(IOException e){ e.printStackTrace(); Assert.fail(); } } @Test public void test1() { // TODO } @Test public void test2() { // TODO } }
これで、/logrecord/test1/console.log と /logrecord/test2/console.log が保存されます。
SL4J + logback を使用している場合、
logback.xml に対してテストで使用する test-logback.xml が以下であるとします。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE logback> <configuration> <statusListener class="ch.qos.logback.core.status.NopStatusListener" /> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <Target>System.out</Target> <encoder> <Pattern>%-23d{yyyy/MM/dd HH:mm:ss.SSS} %-5p [%thread] %m\t\t\t[%C{0}.%method:%line]%n</Pattern> </encoder> </appender> <!-- ########## test-logback.xml だけに記載する ###################################### --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <File>/work/console.log</File> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern>/var/log/labo.%d{yyyy-MM-dd}.log</FileNamePattern> <maxHistory>6</maxHistory> </rollingPolicy> <encoder> <charset>UTF-8</charset> <Pattern>%-23d{yyyy/MM/dd HH:mm:ss.SSS} %-5p [%thread] %m\t\t\t[%C{0}.%method]%n</Pattern> </encoder> </appender> <!-- ################################################################################# --> <logger name="org.myproject"> <level value="debug" /> <appender-ref ref="STDOUT" /> <!-- ########## test-logback.xml だけに記載する ###################################### --> <appender-ref ref="FILE" /> <!-- ################################################################################# --> </logger> </configuration>
log4j2 のような、LogManager shutdown がありません。
テストケースの @After で、ファイルコピー後に、空文字でログファイルを上書きするという
ちょっとセンスがない方法しかないようです。
FileAppender を探索する方法もちょっと面倒な方法をしなければなりません。
import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.Iterator; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; import ch.qos.logback.core.FileAppender;
@RunWith(JUnit4.class) public class SampleTest{ @Rule public TestName testName = new TestName(); private static File appenderFile; @BeforeClass public static void initialize(){ System.setProperty("logback.configurationFile","test-logback.xml"); // FileAppenderを探索して、ログ Fileを求める appenderFile = getAppenderFile("FILE"); } @Before public void before() { // TODO } @After public void after() { // 退避先を生成 File testOutDir = new File("/logrecord/" + testName.getMethodName()); testOutDir.mkdirs(); // コピーで退避 try{ Files.copy(Paths.get(appenderFile.getAbsolutePath()), Paths.get(testOutDir.getAbsolutePath() + "/console.log"), StandardCopyOption.REPLACE_EXISTING); }catch(IOException e){ e.printStackTrace(); Assert.fail(); } // ログファイルを空文字で上書きする try(FileWriter writer = new FileWriter(appenderFile)){ writer.write(""); writer.flush(); }catch(IOException e){ e.printStackTrace(); Assert.fail(); } } private static File getAppenderFile(String appenderName) { ch.qos.logback.classic.LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory(); for(ch.qos.logback.classic.Logger logger : lc.getLoggerList()){ Iterator<Appender<ILoggingEvent>> appenderIterator = logger.iteratorForAppenders(); while(appenderIterator.hasNext()){ Appender<ILoggingEvent> appender = appenderIterator.next(); if (appender instanceof FileAppender){ FileAppender<ILoggingEvent> fileappender = (FileAppender<ILoggingEvent>)appender; if (appenderName.equals(fileappender.getName())) { return new File(fileappender.getFile()); } } } } throw new IllegalArgumentException("FileAppender not found name : "+appenderName); } @Test public void test1() { // TODO } @Test public void test2() { // TODO } }