1. kazurof

    No comment

    kazurof
Changes in body
Source | HTML | Preview
@@ -1,181 +1,183 @@
Log4j 2でログ出力をテストするサンプルソースです。ちなみにLog4j 1.*系とはAPIが構造もろとも変わっており、色々書き直す必要があります。(`Logger`から`addAppender()`メソッドが無くなっています。)
結論、ここに本家ドキュメントの説明があるのですが、勉強の意味もこめて僕なりにサンプルを書いてみました。
http://logging.apache.org/log4j/2.x/manual/customconfig.html#AppendingToWritersAndOutputStreams
# ソース
## テスト対象のコード
``` SampleClass.java
package org.example;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class SampleClass {
private static final Logger LOGGER = LogManager.getLogger(SampleClass.class);
void doSomething(String message) {
// Java8 & Log4j2として、ラムダを使うことでinfoか否かの判定をLog4j2に実施させる。
// みんなラムダで幸せになろうよ。
LOGGER.info(() -> "message ->" + message);
}
}
```
## テストコード
``` SampleClassTest.java
package org.example;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.WriterAppender;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.junit.Test;
import java.io.StringWriter;
import java.io.Writer;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
public class SampleClassTest {
static final String TEST_APPENDER_NAME = "fortest";
@Test
public void testDoSomething() throws Exception {
StringWriter writer = new StringWriter();
//現在のロガーに、writerを持つappenderを追加する。
addAppender(writer, TEST_APPENDER_NAME);
// テスト実行して、writerに流れてきている文字列を検証する。
SampleClass sut = new SampleClass();
sut.doSomething("nantoka");
assertThat(writer.toString(), is("message ->nantoka" + System.lineSeparator()));
// 追加したappenderを削除する。writerの中身も消す。
removeAppender(TEST_APPENDER_NAME);
writer.getBuffer().delete(0, writer.getBuffer().length());
// もう一度テスト実行して、writerに流れてきている文字列を検証する。今度は空である。
sut = new SampleClass();
sut.doSomething("kantoka");
assertTrue(writer.toString().length() == 0);
}
void addAppender(Writer writer, String name) {
final LoggerContext context = LoggerContext.getContext(false);
final Configuration config = context.getConfiguration();
final PatternLayout layout = PatternLayout.createDefaultLayout(config);
Appender appender = WriterAppender.createAppender(layout, null, writer, name, false, true);
appender.start();
config.addAppender(appender);
updateLoggers(appender, config);
}
private void updateLoggers(final Appender appender, final Configuration config) {
for (final LoggerConfig loggerConfig : config.getLoggers().values()) {
loggerConfig.addAppender(appender, null, null);
}
config.getRootLogger().addAppender(appender, null, null);
}
private void removeAppender(String name) {
final LoggerContext context = LoggerContext.getContext(false);
final Configuration config = context.getConfiguration();
for (final LoggerConfig loggerConfig : config.getLoggers().values()) {
loggerConfig.removeAppender(name);
}
config.getRootLogger().removeAppender(name);
}
}
```
## Log4j 2 設定ファイル
``` log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="OFF">
<appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5p [%t] - %m (%C.java:%L)%n"/>
</Console>
</appenders>
<loggers>
<Logger name="org.example" level="debug" additivity="false">
<appender-ref level="info" ref="console"/>
</Logger>
<root level="trace">
<appender-ref ref="console"/>
</root>
</loggers>
</configuration>
```
## gradle ビルドファイル
``` build.gradle
apply plugin: 'java'
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
def defaultEncoding = 'UTF-8'
tasks.withType(AbstractCompile) each { it.options.encoding = defaultEncoding }
test {
testLogging.showStandardStreams = true
}
repositories {
jcenter()
}
dependencies {
testCompile 'junit:junit:4.12'
compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.5'
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.5'
}
```
# 実行結果
```
C:\home\idea\log4j2>gradle test
:compileJava
:processResources
:classes
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
org.example.SampleClassTest > testDoSomething STANDARD_OUT
2016-02-22 01:40:58,092 INFO [Test worker] - message ->nantoka (org.example.SampleClass.java:11)
2016-02-22 01:40:58,097 INFO [Test worker] - message ->kantoka (org.example.SampleClass.java:11)
BUILD SUCCESSFUL
Total time: 14.312 secs
C:\home\idea\log4j2>
```
# 所感
Log4j2のドキュメントでは、「プログラムでログ設定をいじるAPIを単純にしました。」とは言うけれど、なんだか関わるクラスが増えているような気がしないでもない。`Appender`の識別を名前ベースでできるようになったのは良いことだと思う。(1.*では、`Appender`のインスタンスそのもので識別だった。)私の習熟度もまだ低いので、writerを追加するためだけだったらもっと簡単に書けるかもしれない。
-ちなみに、Loggerをモックする方向も考えたがライブラリの依存をなるべく減らしたいことと、Log4j2の機能で実現できることであるのでこちらを採用することにした。
+ちなみに、Loggerをモックする方向も考えたがライブラリの依存をなるべく減らしたいことと、Log4j2の機能で実現できることであるのでこちらを採用することにした。
+
+また、このやり方は現在の最新であるLog4j 2.5からの機能らしい。stackoverflowに同様の議論があるが、古いバージョン向けのいかにもworkaroundに見えるやり方なので、どちらかと言えばこのバージョンでこのやり方のほうが良いと思う。(それでもまだ簡素な感じはしないけど)
+
# バージョン番号など
- Java8
- Log4j 2.5
- Gradle 2.11