はじめに
琴葉ちゃんドリンク、おいしい😋 nikkieです
最近学んだJavaのloggingについてです。
素振りのために用意したプロジェクト(SLF4J + logback)を使っていきます。
目次
- はじめに
- 目次
- logback.xmlでconfiguration!
- ファイルに出力するappenderを追加
- あれ?標準出力にDEBUGログも出力されてる
- ✍️appenderのadditivity
- additivityをfalseに設定
- 終わりに
logback.xmlでconfiguration!
「Configuration with logback-test.xml or logback.xml」を参考に設定します。
<configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern> </encoder> </appender> <logger name="org.example" level="INFO"> <appender-ref ref="STDOUT"/> </logger> </configuration>
org.example
ロガーに、INFO以上を標準出力にロギングするように設定しました。
org.example.Main
クラスのmain()
メソッドを実行します。
// https://nikkie-ftnext.hatenablog.com/entry/start-java-slf4j-logback-project のものです public class Main { public static void main(String[] args) { Logger logger = LoggerFactory.getLogger(Main.class); logger.info("Start"); System.out.println("Hello world!"); logger.debug("End"); } }
21:38:04.335 [main] INFO org.example.Main -- Start Hello world!
org.example.Main
クラスのロガーはorg.example
ロガーの子にあたります。
子ロガーにレベルを設定していないので、org.example
ロガー同様にINFO以上をロギングします1。
そのため、INFOのみ出力されています(DEBUGは出力されません)
ファイルに出力するappenderを追加
In logback speak, an output destination is called an appender. (Appenders and Layouts (Chapter 2: Architecture))
意訳 logbackの用語で言えば、出力先はappenderと呼ばれます。
上のconfigurationではConsoleAppender
を使って出力しました。
https://logback.qos.ch/manual/appenders.html#ConsoleAppender
RollingFileAppender
も使ってみます。
https://logback.qos.ch/manual/appenders.html#RollingFileAppender
<configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern> </encoder> </appender> + <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>logFile.log</file> + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern> + <maxHistory>7</maxHistory> + </rollingPolicy> + <encoder> + <pattern>%d %-5level [%thread] %logger{0}: %msg%n</pattern> + <outputPatternAsHeader>true</outputPatternAsHeader> + </encoder> + </appender> <logger name="org.example" level="INFO"> <appender-ref ref="STDOUT"/> </logger> + <logger name="org.example.Main" level="DEBUG"> + <appender-ref ref="FILE"/> + </logger> </configuration>
org.example
ロガーに、INFO以上を標準出力にロギングするように設定(変更なし)org.example.Main
ロガーには、DEBUG以上をファイルにロギングするように設定- 後述しますが、これは思い込みから誤解があります
あれ?標準出力にDEBUGログも出力されてる
logFile.logには、DEBUG以上のログが記録されていて、期待通りです。
#logback.classic pattern: %d %-5level [%thread] %logger{0}: %msg%n 2024-07-09 21:48:47,416 INFO [main] Main: Start 2024-07-09 21:48:47,416 DEBUG [main] Main: End
ところが、標準出力の方も見てみると
21:48:47.416 [main] INFO org.example.Main -- Start Hello world! 21:48:47.416 [main] DEBUG org.example.Main -- End
あれ? ConsoleAppender
のみだったときにはなかったDEBUGログが出力されているぞ??
ここから学びを得ました
✍️appenderのadditivity
Appender Additivity (Chapter 2: Architecture)
The output of a log statement of logger L will go to all the appenders in L and its ancestors.
意訳 ロガーL
のログステートメントの出力は、L
とその親以上のすべてのappenderに向かいます。
この挙動はオンオフできますが、デフォルトで有効になっています。
Loggers have their additivity flag set to true by default.
リンク先の表が理解の助けになりました。
このadditivityという性質から最初のconfigurationの理解が深まります。
org.example
ロガーにConsoleAppender
を設定- 子の
org.example.Main
ロガーのappenderはconfigurationにはない- additivityにより、親の
org.example
ロガーのappenderを使う - つまり、
org.example.Main
ロガーのINFO以上のログは、親のorg.example
ロガーのConsoleAppender
に流れる
- additivityにより、親の
親のロガーのappenderを使っているので、子のロガーにappenderを設定していませんが、出力されたわけですね。
RollingFileAppender
を使った後は以下のようになっているわけです。
org.example
ロガーにConsoleAppender
を設定- 子の
org.example.Main
ロガーにRollingFileAppender
を設定- 子のロガーは(親のINFO以上と変えて)DEBUG以上をファイルにロギングするように設定
- additivityにより、親の
org.example
ロガーのappenderも使う - つまり、
org.example.Main
ロガーのDEBUG以上のログは、親のorg.example
ロガーConsoleAppender
に流れる
なので、DEBUGログが標準出力にも出力されたというわけです!
additivityをfalseに設定
- <logger name="org.example.Main" level="DEBUG"> + <logger name="org.example.Main" level="DEBUG" additivity="false"> <appender-ref ref="FILE"/> </logger>
logFile.logには、DEBUG以上のログが記録されているままです。
#logback.classic pattern: %d %-5level [%thread] %logger{0}: %msg%n 2024-07-09 22:09:28,829 INFO [main] Main: Start 2024-07-09 22:09:28,830 DEBUG [main] Main: End
標準出力が変わります
Hello world!
additivityを無効にしたので、親のロガーのConsoleAppender
は使いません。
RollingFileAppender
のみです。
なので、標準出力にログが出なくなったわけですね。
Configurationのドキュメントの以下のあたりかと思います(積ん読)
https://logback.qos.ch/manual/configuration.html#overridingCumulativity
終わりに
logbackのappenderのadditivityを知りました。
- ロガーには階層構造がある
- ログの出力先がappender。ロガーに設定する
- 子のロガーは自身と親以上に設定されたappenderすべてにログを流す(デフォルトは
additivity="true"
)- この挙動は無効にできる
今回のソースコードは以下にあります。
なお、今回のケースにて子のロガーで(DEBUGを除いて)INFO以上を標準出力にも出したいときには、filterを使うのかなと考えています(宿題事項)
Pythonのロギングと似ている点(ロガーの階層構造)もあれば、appenderのadditivityという違いもあって、興味深かったです
- このあたりも面白いと思いました。Effective Level aka Level Inheritance (Chapter 2: Architecture)↩