これがSpring Bootのロギング実装って何もしない設定しない場合どうなっているのだろうか・・・?
という疑問が湧いたので調べてみた。
Spring Bootでは全ての内部ロギングでCommons Loggingを利用しているが、基本となるログ実装部分は開発者に委ねられている。
(中略)
デフォルト設定では、Starter POMを利用する場合はLogbackが採用される。Logback以外を利用したければdependencyを変更するだけでOK。
Spring Boot Features 4. Logging / Spring Boot Reference Documentation
Spring Bootのログ出力(概要) / @NagaokaKenichi
Spring Boot内のロギングではインターフェースとして「Commons-Logging」を使用する。
実際のログ出力処理の際には「Logback」というライブラリを使用する。(デフォルト時)
巷で話題になった「log4j2」は何も設定を変更しない限り使われることはない。
これがSpring Bootのデフォルトの実装となる。
ロギングの歴史と仕組みを全く知らないと非常に分かりづらいが、要するに
import org.apache.commons.logging.Log import org.apache.commons.logging.LogFactory class HogeMoge { private val log: Log = LogFactory.getLog(javaClass) fun MoMoge() { log.info("INFOです") } }
上記のような形だと一見ログ処理は「Commons-Logging」が担当しており、ログ出力する実際の処理も「Commons-Logging」で行っている風に見える。
が、あくまでも「Commons-Logging」とはインターフェースを提供するライブラリ。
出力処理の実装を担っているのは「Logback」という別のライブラリになる。
環境情報
Gradle: 6.0.1
Spring Boot: 2.2.4.RELEASE
Spring Boot のデフォルトのログ出力
Spring Initializrでサンプルプロジェクトを用意してBootRunしてみる。
依存関係にはspring-starter-web
だけを設定。
何も弄っていないデフォルトのログ出力はこんな感じ。
「Commons-Logging」→「Logback」という形でログが出力されている。
Spring Boot プロジェクトから Logback を除外してみる
build.gradle.ktsにてLogbackをexcludeする記述を書いてみます。
spring-boot-starter-logging
を依存関係から除してみます。
configurations.all { exclude(module = "spring-boot-starter-logging") }
このライブラリはspring-boot-starter-web
で自動的に依存関係のライブラリとして入れられるものです。
そしてBootRunしてみる。すると以下のようになる。
ログが赤字で表示されている。
現在の状態は
Spring Bootの内部ロギングで使用している「Commons-Logging」 は存在する。
が、肝心のログ出力処理を実装しているライブラリ「Logback」は存在しない。
そのため、いつも見るLogbackで出力される白文字のログは出ていない事が分かる。
「Logbackが存在しないと全くログが出力されないのでは?」
と思ったが、そのような事はなくTomcatの依存関係であるログライブラリがログを出力しており、Tomcat側がログを出力する形になるとのこと。
Spring Bootの赤字で出るログは何が出力している?/ teratail
Spring Boot プロジェクトから Commons-Logging を除外してみる
次はCommons-Loggingを除外してみます。
build.gradle.ktsにてspring-jclをexcludeすればOK。
configurations.all { exclude(module = "spring-jcl") }
命名も相まって理解を難しくさせるが、「jcl」とは「Jakarta Commons Logging」の略称。
BootRunして確認してみる。
java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
と出ているのが分かる。
> Task :bootRun FAILED Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory at org.springframework.boot.SpringApplication.(SpringApplication.java:196) at com.test2.test2.Test2ApplicationKt.main(Test2Application.kt:28) Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory at java.net.URLClassLoader.findClass(URLClassLoader.java:381) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ... 2 more
現在の状態は
Spring Bootの内部ロギングで使用している「Commons-Logging」 は存在しない。
実際にログ出力処理を実装しているライブラリ「Logback」は存在する。
そのため、当然NoClassDefFoundError
でBuildに失敗する。
spring-jcl って何?
Spring Framework 5.0からは、「Commons LoggingのAPIを実装したブリッジライブラリ」の部分をspring-jclが担うため、jcl-over-slf4jなどのブリッジライブラリを追加することなく、Commons LoggingのAPIを使用して「Log4j 2.x」「SLF4J」「JUL(
java.util.logging
)」経由でログを出力することができます。
Spring Framework 5.0 コア機能の主な変更点 @kazuki43zoo
「spring-jcl
はすごいやつ(超意訳)」
登場前は開発者側がブリッジライブラリ追加や「本家Commons-Logging」除外などを行わなければならなかった。
しかし、spring-jcl
モジュールが存在するだけでそれを解決してくれるすごいやつ。
slf4j って何?
ここで出た「SLF4J」とは「Commons-Logging」と同じくインターフェース的ライブラリ。
ちなみに「ログファサードライブラリ」とも呼ぶ。
jcl-over-slf4j って何?
jcl-over-slf4j
の例があるが、これは「Commons-Logging」での実装を「SLF4J」をバイパスしてくれるもの。
spring-jcl
登場以前は別個でこのライブラリを追加しなければならなかった。
要するに、spring-jcl
が「Commons-Logging」と「SLF4J」の2つのログ出力が依存モジュールで混在していてもブリッジの役割を果たして出力してくれるという事になる。
サンプルプロジェクトではSpring Bootの最小構成であるが、プロジェクトが大きくなるにつれてライブラリも増えログ出力の種類が混在してくるようになる。
その時になると恩恵を感じられるようになるもの。
分かりづらい世界であるが、下記記事が分かりやすく参考になる。
Javaのロギングライブラリの歴史と現状をふんわり把握する(初学者向け) / @nisshiee
まとめ
何も考えずにSpring Bootロギングの奥を覗いてみたら、Javaのロギングの歴史に片足を突っ込んでしまった。
しかし、基本的にアプリ開発者は煩わしい事を考えずにログ実装が出来るようにされてある。
$ ./gradlew dependencies
+--- org.springframework.boot:spring-boot-starter-web -> 2.2.4.RELEASE
| +--- org.springframework.boot:spring-boot-starter:2.2.4.RELEASE
| | +--- org.springframework.boot:spring-boot:2.2.4.RELEASE
| | | +--- org.springframework:spring-core:5.2.3.RELEASE
| | | | \--- org.springframework:spring-jcl:5.2.3.RELEASE
~~~~~~~~~~~(中略)~~~~~~~~~~~
| | | \--- org.springframework.boot:spring-boot:2.2.4.RELEASE (*)
| | +--- org.springframework.boot:spring-boot-starter-logging:2.2.4.RELEASE
| | | +--- ch.qos.logback:logback-classic:1.2.3
| | | | +--- ch.qos.logback:logback-core:1.2.3
| | | | \--- org.slf4j:slf4j-api:1.7.25 -> 1.7.30
| | | +--- org.apache.logging.log4j:log4j-to-slf4j:2.12.1
| | | | +--- org.slf4j:slf4j-api:1.7.25 -> 1.7.30
| | | | \--- org.apache.logging.log4j:log4j-api:2.12.1
| | | \--- org.slf4j:jul-to-slf4j:1.7.30
| | | \--- org.slf4j:slf4j-api:1.7.30
おしまい。
コメント