やりたいこと
運用をしていると、エラーを調査するために膨大なログの中からgrepすることが多々あります。
特に統計情報や、運用のためにInfoレベルでログに書き込んでいる情報がある場合、ログの出力数が多くなり、エラー情報をgrepするのに時間がかかることが多いです。
そんな場合には、レベルごとに出力するファイルを分けることで、grepする時間を少なくすることができます。
また分け方や運用方法によっては、監視用スクリプトの設定を容易にすることもできると思います
やりかた
フィルターのLevelFilterを使用することで可能となります。
logger.xml
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
設定してみる
PlayFrameworkを使ったアプリケーションで、Trace以外のレベルごとにファイルを分けた設定例です。
logger.xml
<configuration>
<conversionRule conversionWord="coloredLevel" converterClass="play.api.Logger$ColoredLevel" />
<appender name="FILE_ERROR" class="ch.qos.logback.core.FileAppender">
<file>${application.home}/logs/error.log</file>
<encoder>
<pattern>%date{yyyy-MM-dd'T'HH:mm:ssZ,GMT} [%level] %logger %replace(%message){'\n', '\\n'} %replace(%xException){'\n', '\\n'}%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="FILE_INFO" class="ch.qos.logback.core.FileAppender">
<file>${application.home}/logs/info.log</file>
<encoder>
<pattern>%date{yyyy-MM-dd'T'HH:mm:ssZ,GMT} [%level] %logger %replace(%message){'\n', '\\n'} %replace(%xException){'\n', '\\n'}%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="FILE_WARN" class="ch.qos.logback.core.FileAppender">
<file>${application.home}/logs/warn.log</file>
<encoder>
<pattern>%date{yyyy-MM-dd'T'HH:mm:ssZ,GMT} [%level] %logger %replace(%message){'\n', '\\n'} %replace(%xException){'\n', '\\n'}%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="FILE_DEBUG" class="ch.qos.logback.core.FileAppender">
<file>${application.home}/logs/debug.log</file>
<encoder>
<pattern>%date{yyyy-MM-dd'T'HH:mm:ssZ,GMT} [%level] %logger %message %xException%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%coloredLevel %logger{15} - %message%n%xException{5}</pattern>
</encoder>
</appender>
<logger name="play" level="INFO" />
<logger name="application" level="DEBUG" />
<!-- Off these ones as they are annoying, and anyway we manage configuration ourself -->
<logger name="com.avaje.ebean.config.PropertyMapLoader" level="OFF" />
<logger name="com.avaje.ebeaninternal.server.core.XmlConfigLoader" level="OFF" />
<logger name="com.avaje.ebeaninternal.server.lib.BackgroundThread" level="OFF" />
<logger name="com.gargoylesoftware.htmlunit.javascript" level="OFF" />
<root level="ERROR">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE_ERROR" />
<appender-ref ref="FILE_WARN" />
<appender-ref ref="FILE_INFO" />
<appender-ref ref="FILE_DEBUG" />
</root>
</configuration>
動かしてみる
実行
Logger.debug("debug")
Logger.info("info")
Logger.warn("warn")
Logger.error("error")
ファイルへの出力内容
tail -n 1 logs/{debug,info,warn,error}.log
==> logs/debug.log <==
2015-01-17T15:48:23+0000 [DEBUG] application debug
==> logs/info.log <==
2015-01-17T15:48:23+0000 [INFO] application info
==> logs/warn.log <==
2015-01-17T15:48:23+0000 [WARN] application warn
==> logs/error.log <==
2015-01-17T15:48:23+0000 [ERROR] application error
おわり
EvaluatorFilterを使って、書込内容ごとにログ出力先を分けることも可能ですが、それはまた別ライブラリー紹介と一緒に投稿したいと思います。