3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SLF4J "No SLF4J providers were found" の原因と解決(推移依存の衝突)

3
Posted at

はじめに

新卒1年目エンジニアのhirotoです。研修中でWebアプリケーションを作成する課題があり、アクセスログの出力機能を追加しようとしたときに発生したエラーとその解決手順についてこの記事でまとめたいと思います。

本文

動作環境

  • 言語:Java(JDK 21)
  • ビルドツール:Gradle 9.6.1
  • フレームワーク:Spring Boot 4.1.0
  • テンプレートエンジン:Thymeleaf
  • DB:Postgres 15.18

発生したエラー

SLF4J: No SLF4J providers were found.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#noProviders for further details.

昨日までちゃんと動いてて、SLF4Jのimplementちゃんとgradleに書いてるのに急になんでNoと言われるんだ?という感じでした


原因

原因を探すときに参考にした記事

要素 詳細
原因 logstash-logback-encoder が推移依存で古い slf4j-apilogback-classic を引き込み、Spring Bootが管理する slf4j-api / logback-classicクラスパス上で競合
SLF4J の仕組み SLF4J 2.xは ServiceLoader で どこのが提供したものか(Provider)を検出する。APIのバージョンと providerのバージョンが不一致だと providerを発見できず NOP logger にフォールバックする
なぜ急に発生したか Gradle のキャッシュ更新や依存解決順序の変動で、先に古い slf4j-api が読み込まれるようになった

依存の衝突イメージ:

spring-boot-starter-web
  └─ spring-boot-starter-logging
       ├─ logback-classic:1.5.x  ← Spring Boot 管理(正)
       └─ slf4j-api:2.1.x        ← Spring Boot 管理(正)

logstash-logback-encoder:7.4
  ├─ logback-classic:1.4.x       ← 競合!
  └─ slf4j-api:2.0.x             ← 競合!

アクセスログの実装はこちらの記事を参考にしつつ、Copilotとも相談しつつ実装しました。その際、提案されたlogstash-logback-encoder:7.4を追加したのですがこれが原因で競合が発生してしまっていたようです。(Copilotに提案されたコードを仕組みを理解しないまま承認してしまったのが反省点ですね)


解決方法

手順1: 競合ライブラリのバージョンアップ

logstash-logback-encoder8.0(Spring Boot 4.x 互換バージョン)に上げる。

// build.gradle
implementation('net.logstash.logback:logstash-logback-encoder:8.0')

手順2: 推移依存をexcludeで除外して固定化する

念のため、SLF4J / Logback の推移依存は Spring Boot の dependency management に統一する

implementation('net.logstash.logback:logstash-logback-encoder:8.0') {
    exclude group: 'ch.qos.logback', module: 'logback-classic'
    exclude group: 'ch.qos.logback', module: 'logback-core'
    exclude group: 'org.slf4j', module: 'slf4j-api'
}

手順3: 確認

SLF4jの依存関連を調査する

.\gradlew.bat dependencyInsight --configuration runtimeClasspath --dependency org.slf4j

再発防止のためには

  • SLF4J / Logback を直接 implementation に書かない(Spring Boot の BOM 管理に任せてバージョンは書かない)
  • サードパーティのロギング系ライブラリ追加時は exclude で推移依存を除外する
  • 依存追加後に dependencies --configuration runtimeClasspathslf4j-api / logback-classic1バージョンだけ 存在するか確認する

付録:調査に使えるコマンド集

# 特定の依存がどこから来ているか調べる
.\gradlew.bat dependencyInsight --dependency slf4j-api --configuration runtimeClasspath

# logback の重複を調べる
.\gradlew.bat dependencyInsight --dependency logback-classic --configuration runtimeClasspath

# テスト用クラスパスの確認
.\gradlew.bat dependencies --configuration testRuntimeClasspath | Select-String "slf4j|logback"

まとめ

今回はアクセスログの実装しながら発生したエラーについてとその解決方法について紹介しました。
これまでの知識ではSLF4J+logbackでログを外部ファイルに出力することしか知らなかったため、Spring Bootが管理するSLF4Jがあることを知らずクラスパスの競合に気づけませんでした。
CLIでgradleの依存関係の調査なども初めてやってみたので今後も似たような事象があれば活用していきたいと思います

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?