解決編: logbackで同一レベルの複数ログを異なる出力先に出す
前回の「疑問メモ: logbackで同一レベルの複数ログを異なる出力先に出したい」の解決編、「同一レベルのログを用途に応じて出し分ける」話の続きです。
師匠から参考になる情報をいただき、無事実現できました。ありがとうございます。
JaninoEventEvaluator を活用すればよいのですね。
JaninoEventEvaluator を使うには Janino libraryが追加で必要なので、準備します。
- http://docs.codehaus.org/display/JANINO/Download から、最新版(本日時点では2.6.1)をダウンロードする
- zipを解凍して、出てきた janino.jar と commons-compiler.jar をビルドパスに通す
<?xml version="1.0" encoding="UTF-8"?> <configuration> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <filter class="ch.qos.logback.core.filter.EvaluatorFilter"> <evaluator> <expression>return message.contains("> ");</expression> </evaluator> <OnMismatch>ACCEPT</OnMismatch> <OnMatch>DENY</OnMatch> </filter> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>DENY</onMatch> <onMismatch>ACCEPT</onMismatch> </filter> <File>sample.log</File> <Append>true</Append> <encoder> <pattern>%d %5p %c{1} - %m%n</pattern> </encoder> </appender> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.core.filter.EvaluatorFilter"> <evaluator> <!-- defaults to type ch.qos.logback.classic.boolex.JaninoEventEvaluator --> <expression>return message.contains("> ");</expression> </evaluator> <OnMismatch>DENY</OnMismatch> <OnMatch>ACCEPT</OnMatch> </filter> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <encoder> <Pattern>%msg%n</Pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="STDOUT" /> <appender-ref ref="FILE" /> </root> </configuration>
今回は、メッセージに「> 」という文字列が含まれていればコンソールに出力し、そうでなければファイルに出力するようにしました。
動かすコードのログ出力部分も、その前提を踏まえた形式に変更します。
package xxx.yyy.zzz; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; import org.slf4j.MarkerFactory; import xxx.yyy.zzz.impl.Foo; public class Main { private static final Logger logger = LoggerFactory.getLogger(Main.class); private static Marker msg = MarkerFactory.getMarker("MESSAGE"); private static Marker detail = MarkerFactory.getMarker("DETAIL"); public static void main(String[] args) { QQQ foo = new Foo("foo"); logger.info(msg, "> hello!"); logger.info(detail, "mogamoga(for developers)"); foo.execute(); logger.info(msg, "> bye!"); // for sample logger.debug("Main debug hogehoge"); logger.warn("Main warn hogehoge"); logger.error("Main error hogehoge"); } }
残りのコード(インタフェースQQQ.javaと、実装クラスFoo.java)は、前回と同じです。
コンソール出力結果。
> hello! > bye!
ファイル出力結果。
2012-06-19 01:50:46,887 INFO x.y.z.Main - mogamoga(for developers) 2012-06-19 01:50:46,889 DEBUG x.y.z.i.Foo - Foo#execute done. 2012-06-19 01:50:46,889 INFO x.y.z.i.Foo - Foo info hogehoge 2012-06-19 01:50:46,889 WARN x.y.z.i.Foo - Foo warn hogehoge 2012-06-19 01:50:46,889 ERROR x.y.z.i.Foo - Foo error hogehoge 2012-06-19 01:50:46,889 DEBUG x.y.z.Main - Main debug hogehoge 2012-06-19 01:50:46,889 WARN x.y.z.Main - Main warn hogehoge 2012-06-19 01:50:46,889 ERROR x.y.z.Main - Main error hogehoge
やったー。これがやりたかったんダヨー。
expression の要素には、式が書けます。セミコロンで区切って複数の式を書くこともできますし、if文なんかも書けます。(だからcommons-compilerが必要になるんですね)
ほかにも、ドキュメントのサンプルを見ると、エラーオブジェクトに詰め込んだ状態を見て処理を分岐させるような処理もやっています。エラーのステータスコードが404だったら、特定のURIにリクエストを投げたりだとか。ログの設定ファイルに書くには、ちと複雑な処理のような気がしますが、そういう使い方もあるんですね。
logbackすごい。