こんにちは、@seitです。 今回は、社内のWebシステムでログ出力周りをいじることがあったので、その時の備忘録です。
やりたいこと
Spring Bootで、ログの出力先(標準出力、ファイル出力)を細かく切り分けたい。
・全てのログを標準出力とファイルに出力する
・一部のログは別のファイルにも切り出して出力する
・ログファイルはローテーションする
・ローテーションルールを指定する
Log出力で使用するライブラリ
Logbak+SLF4J
最初、過去に使ったことのあるlog4jを考えていましたが、公式によると"Logback は log4j プロジェクトの後継プロジェクトです。log4j の創始者であるCekiGülcü によって設計された"とのことなので、今回はLogback+SLF4Jにしました。
実装
Logbackは"spring-boot-starter-web"に含まれるため、Spring bootを利用している場合は新たに依存性を追加する必要はありません。
Logbackの設定ファイルである"logback-spring.xml"を"resources"フォルダの下に作成します。
今回は以下のようにします。
logback-spring.xml
<configuration> <!-- 標準出力用設定 --> ・・・① <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <charset>UTF-8</charset> <pattern>%d{yyyy/MM/dd HH:mm:ss} %-5level [%thread] - %msg%n</pattern> </encoder> </appender> <!-- ファイルA出力用設定 --> ・・・② <appender name="FILE_A" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 出力先ファイルパス --> <file>/<任意のパス>/alog.log</file> <!-- ログのローテーション設定 --> <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> <!-- 世代管理されるファイルの命名パターン --> <fileNamePattern>/<任意のパス>/alog.log.%i</fileNamePattern> <!-- %iはインデックス。minIndexからMaxIndexまでの連番 --> <minIndex>1</minIndex> <maxIndex>7</maxIndex> <!-- 最大7ファイルまで世代管理 --> </rollingPolicy> <!-- ローテーションのトリガ。10MBに達した時点でローテーション。 --> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <maxFileSize>10MB</maxFileSize> </triggeringPolicy> <encoder> <charset>UTF-8</charset> <pattern>%d{yyyy/MM/dd HH:mm:ss} %-5level [%thread] - %msg%n</pattern> </encoder> </appender> <!-- ファイルB出力用設定 --> ・・・③ <appender name="FILE_B" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>/<任意のパス>/blog.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>/<任意のパス>/blog.log.%d{yyyy-MM-dd}</fileNamePattern> <!-- %d{yyyy-MM-dd}の設定によって、自動でローテーションがトリガされる。日まで指定すると、毎日ローテーション。 --> <!-- 最大30日間保存 --> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <pattern>%d{yyyy/MM/dd HH:mm:ss} %-5level [%thread] - %msg%n</pattern> </encoder> </appender> <!-- rootロガーを継承したサブロガー。rootロガーの設定を引き継ぎつつ、追加でファイルBにもログを出す --> ・・・④ <logger name="com.example" level="INFO"> <appender-ref ref="FILE_B" /> </logger> <!-- rootロガー。ログは原則ファイルAと標準出力に出力する --> ・・・⑤ <root level="INFO"> <appender-ref ref="STDOUT" /> <appender-ref ref="FILE_A" /> </root> </configuration>
⑤で、rootロガーは標準出力用のappender①と、ファイル出力用のappender②を参照しているため、rootロガーを使ったログは標準出力とファイルAに出力されます。
④の"com.piyo"ロガーはファイルBに出力するロガーを参照しているため、"com.piyo"ロガーを使うと、ファイルBに出力されます。
ここで重要なのは、ロガーは継承関係を持つことができるということです。
(
ロガーは、"com.piyo.hoge.fuga..."のように、ロガーの名称を"."で繋ぐことで設定を継承させていくことができます。
root
↑(継承)
com.example
↑(継承)
com.example.hoge
↑(継承)
com.exampleexample.hoge.fuga
この例だと、com.piyo.hoge.fugaロガーを使用する場合、com.piyoの設定を引き継ぐため、ログがファイルBにも出力されることになります。
このloggerタグのname属性にパッケージ名(今回はcom.piyo)を指定しておくことで、com.piyo以下のパっケージのクラスで継承元のロガーを利用することができたりします(以下で説明)。
ログ出力コード
ログを出力するには、以下のように実装します。
com.example.logback.demo; import org.slf4j.Logger; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class SampleController { Logger loggerA = org.slf4j.LoggerFactory.getLogger("com.hoge"); ・・・① Logger loggerB = org.slf4j.LoggerFactory.getLogger(getClass().getPackage().getName()); ・・・② @GetMapping("/") // URLのパスの指定 private String top() { // リクエストを受け付けるメソッド loggerA.info("これはrootロガーのみ継承されているためFILE_Aのみに出力されます。"); loggerB.info("これはcom.exampleロガーを継承しているのでFILE_AとFILE_Bに出力されます。"); return "/index"; } }
getLoggerの引数に、取得するロガー名称を指定します。
①ではrootロガーのみ継承されることになるため、ファイルBには出力されず、標準出力とファイルAにのみ出力されます。
②では"com.example"ロガーを継承した"com.example.logback.demo.SampleController"ロガーが生成され、ログがファイルBにも出力されます。
以上です。