この記事は リクルートライフスタイルアドベントカレンダー2017 の5日目です。
ホットペッパービューティーでゆるふわエンジニアをしています。しゃぜです。
logbackでプログラマブルにLoggerを生成すると題しまして、動的なLogger生成について書いてみます。
ま、あんまりドキュメントに無さそうな、logback小ネタです。
はじめに
通常logbackを使う際は、以下のようなlogback.xmlを用意すると思います。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
<appender name="APP"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/var/log/app.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<maxHistory>14</maxHistory>
</rollingPolicy>
<encoder>
<pattern>time:%d{yyyy-MMM-dd HH:mm:ss.SSS} level:%level marker:%marker thread:%thread logger:%logger file:%file line:%line message:%msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="APP" />
</root>
</configuration>
やりたいこと
ところが、このXML記法だと困るケースがありました。具体的にいうと、この仕事の時だったんですが。
やりたかったこととしては、ログファイルのPATHを動的に設定したいです。どういうことかというと...。
- 条件
- host1から転送されてきたログは、
/tmp/host1/app-yyyyMMdd.log
とする。 - hostNから転送されてきたログは、
/tmp/hostN/app-yyyyMMdd.log
とする。 - hostは可変である。
- host1から転送されてきたログは、
というものです。まぁ、転送されてくるホストの増減に合わせて、xmlをメンテナンスするとかやりたくないです。
いろんなやり方があると思うのですが(logbackを使わないも含めて)、今回はlogbackでプログラマブルにLoggerの生成するやりかたでやってみました。
以下動作サンプルのコードです。githubにもあげておきますね。https://github.com/shase/logback-dynamic-logger-sample
import java.util.stream.Stream;
import ch.qos.logback.classic.Logger;
public class Main {
public static void main(String...args) {
// ここは外部からhost名が入ってくるのをイメージしてみてください。
String[] path = {"/tmp/host1/app-%d{yyyyMMdd}.log","/tmp/host2/app-%d{yyyyMMdd}.log","/tmp/host3/app-%d{yyyyMMdd}.log"};
Stream.of(path)
.forEach(p -> {
Logger logger = new SimpleLoggerFactory().getLogger("sample", p);
logger.info("dynamic path");
});
}
}
import java.nio.charset.StandardCharsets;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
public class SimpleLoggerFactory {
public Logger getLogger(String loggerName, String path) {
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
PatternLayoutEncoder ple = new PatternLayoutEncoder();
ple.setPattern("%msg%n");
ple.setContext(lc);
ple.setCharset(StandardCharsets.UTF_8);
ple.start();
RollingFileAppender<ILoggingEvent> fileAppender = new RollingFileAppender<>();
TimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new TimeBasedRollingPolicy<>();
rollingPolicy.setFileNamePattern(path);
rollingPolicy.setMaxHistory(14);
rollingPolicy.setParent(fileAppender);
rollingPolicy.setContext(lc);
rollingPolicy.start();
fileAppender.setAppend(true);
fileAppender.setEncoder(ple);
fileAppender.setRollingPolicy(rollingPolicy);
fileAppender.setContext(lc);
fileAppender.start();
Logger logger = (Logger) LoggerFactory.getLogger(loggerName);
logger.addAppender(fileAppender);
logger.setAdditive(false);
return logger;
}
}
- これによって、出力先のpathを動的にすることができました。
- logbackを普段使っている人が見ればわかると思いますが、普段xmlで定義していることをそのまま書いているだけですね。
- この例ではxmlは不要です。
おわりに
- 実際に使う際は、毎回同じloggerを生成する必要はないので(初回生成だけでよいので)、何かしらの手段でsingletonにするとよいでしょう。
- これを書いた後に気がついたのですが、logbackにはGroovy Configurationもあるので、そっちでもやりたいことはできたのかも?機会があったら試してみようと思います。
- ではではー。Merry Xmas !!!