日々常々

ふつうのプログラマがあたりまえにしたいこと。

Javaで「ライブラリの最新版がある」と言うときの基礎知識

Log4j 2のバージョンアップのやりかた で "「Mavenリポジトリ」の指すもの" を軽く書きましたが、いい機会なのでもう少し書いておきます。

最新版は使える?

https://twitter.com/irof/status/1469139048954724354

こういうツイートをしまして。 見てる順番は Log4j 2のトップページMvnRepositoryのlog4j-coreGitHubのLog4j 2のタグ一覧Central Repositoryのlog4j-apiディレクトリです。

ツイートの状態から「Log4j 2はリリース成功してからタグ作るで運用してるんだなぁ」とか、リリース成功したら自動でタグ作ってるわけでもないのかなぁとか思いました。私はタグをトリガーにリリースのパイプライン動かすのが好きです。リリース失敗したら消したくなるけど。

基本的に「最新版が使える」は「公開されているMavenリポジトリから取得できる」であり、狭義では「Central Repositoryに公開されている」を指します。 ツイートの4枚目で 2.15.0 の存在を確認できているので「最新版が使える」状態でした。

現代ではMavenやGradleのようなビルドツールを使わないことは考えづらく、jarを手動でダウンロードすることはほとんどありません。 あるとしても一部のAntでのビルドを保守している文脈か、ライセンス等の問題でCentral Repositoryなどで公開できない一部のライブラリを使用する場合です。

たとえば Log4j 2サイトのダウンロードページから最新版のjarがダウンロードできたとしても、それを使用するようにビルドツールを設定してあげる必要があります。 最近ではほとんど用途のないlibディレクトリに配置してのパス指定や、後述のインハウスリポジトリへの一時的な登録などになります。これらをしなければ「自分の手元ではビルドできてもCIではビルドできない」となったりして、前時代的だし、どちらも面倒。やらずに済むならやりたくないのが今日この頃です。やらずに済んでよかった。

インデックスサイト

リポジトリの階層をたどるのは面倒なので、 https://central.sonatype.comhttps://search.maven.org から変わった) や https://mavenrepository.com のようなMavenリポジトリの検索を助けてくれるサービスを使用することが多いでしょう。Googleとかで検索してもこれらが先に出てくるはずです。

私はインデックスサイトとか呼んでますが、共通の呼び名は知りません。インデックスサイトはそれぞれのサイト次第であり、別に mvn コマンドが使用する最新のリポジトリの状態を示すものではありません。

ここになくても、使えるものは使えます。検索を助けてくれるだけ。

Mavenリポジトリ

mvn コマンドはMavenリポジトリを使用します。 Mavenリポジトリはローカルリポジトリとリモートリポジトリに分けられますが、基本的にリモートリポジトリが原本、ローカルはキャッシュくらいの感覚でOKです。

mvn install とか叩くとローカルリポジトリに登録されますが、使えるのは自分だけです。リモートリポジトリへの登録は mvn deploy とかですが、サーバーのURLとか認証情報とかが必要になってきます。

リポジトリの場所は ~/.m2/settings.xml とか pom.xml とかコマンド実行時の引数とかで設定できますが、何も設定しなければローカルは ~/.m2/repository で、リモートリポジトリは https://repo.maven.apache.org/maven2/ です。

プロジェクトで使ってるリモートリポジトリがわからなかったら、存在しないライブラリ落とそうとしたらエラーログに出てきたりします。

修飾なしで「Mavenリポジトリ」と言うと、基本的にはCentral Repositoryを指すと思って大丈夫です。 他は「SpringのMavenリポジトリ」のように修飾(「Springの」)をつけて呼ぶはずです。Central Repository以外のリポジトリを使用する動機は、そのリポジトリでしか公開されていないものを使用したい場合で、多くはSNAPSHOTやMilestoneなリリースを使いたい場合になります。他は作者が何かしらの理由によりCentral Repositoryで公開していない場合でしょう。(「めんどくさい」って理由で上げないことも多いと思います。)

ライブラリごとのリポジトリの場所

たとえばLog4j 2のリリースリポジトリhttps://repository.apache.org/service/local/staging/deploy/maven2 で、SNAPSHOTリポジトリhttps://repository.apache.org/content/repositories/snapshots のようです。 なのでSNAPSHOTはSNAPSHOTリポジトリorg/apache/logging/log4j/log4j-core にあります。

これはLog4j 2のpom.xml を読めばわかることです。 org.apache.logging.log4j:log4j-core -> org.apache.logging.log4j:log4j -> org.apache.logging:logging-parent -> org.apache:apache とparentを辿れば <distributionManagement>に書かれています。4階層もあって面倒なので、自力でpom読むよりは手元のpomでmvn help:effective-pom する方が確実ですし、結局<property>なのでビルド時に上書きできるんですけども。

と、pom.xmlを見たら書いてたりするという、あまり役に立たない知識を書いておきたかっただけです。 公式サイトのSnapshot buildsに書いてあるので、素直にこっち見た方がいいです。

インハウスリポジトリ

リモートリポジトリのうち、組織内部でのみ使用する目的で作られたリポジトリをインハウスリポジトリと言ったりします。

私が使ったことがあるのはSonatypeのNexus(Central Repositoryが使用している)や、JFrogのArtifactory(Springが使用している)。最近ではGitHub PackagesAWS CodeArtifactなども選択肢に入るかもしれません。

自分達だけが使うライブラリを登録したい場合はもちろん、クローズドなCentral Repositoryにアクセスできない環境や、Central Repositoryが落ちた時に備えるとか、ネットワーク的に近いところにキャッシュしてビルド時間を短縮したいとか、いろんな理由で使われます。

インハウスリポジトリを使用する場合、開発端末の ~/.m2/settings.xml に書くことが多いと思います。

Central Repository

Central Repsitoryは、Sonatypeが運営しており、世界中で使われ、あちこちでミラーリングされてたりするリポジトリです。

「セントラルリポジトリ」の他、「Mavenセントラルリポジトリ」とか、「セントラル」とか、先にも書きましたが単に「Mavenリポジトリ」と呼ばれたりします。いずれもCentral Repositoryのことです。JVM界隈では「ここで公開されているライブラリはだいたいどこでも使える」と言っていいです。

逆にここで公開されていないライブラリを使うには、個別の対応が必要になる認識です。

Central RepositoryのURL

Central RepotiroyのURLは https://repo1.maven.org/maven2https://repo.maven.apache.org/maven2 があり、公式で並列してアナウンスされているもの です。なのでどっちでもいい。

内容微妙に違うし(正確には「違うことがある」かな。この記事書いた時違ったけど、今みたら同じになってた。)、確かrepo1.maven.orgからrepo.maven.apache.orgに変更された経緯があった気がするんだけど、混乱したからどっちでもいいを維持することにしたんでしょうかね。Central Repotitoryを運営しているSonatypeの運営するインデックスサイト https://central.sonatype.com のリンクがrepo1向いてるんで、インターネットで示すのはrepo1がいいかなと思っています。リリースしたら両方に反映されるし。

Central Repositoryへの反映

CentralRepositoryへの反映は(個人の場合は)図のようにOpenSourceSoftwareRepositoryHostingというサービスを使用し、複数ステップで行われます。

デプロイとリリースは別ですが、プラグインでの自動化も可能。リリース時にjavadocやsourceがあるかとか署名の検証とかが行われ、ダメだったら跳ねられます。一度リリースしたものは削除できません。分かれてるのは最終確認みたいな感じですね。

Log4j 2はApacheなのでLarge Organization向けのものになるんでしょうかね。同期にどれくらい時間がかかるかはよくわかりません。

Central Repositoryへの反映ですが、少なくともOSSRHでは図で書いているように制御不可です。数時間かかる時もあれば、さきほどは10分くらいで反映されました。この時間が問題になることは少ないですが、JIGのようにGradlePluginPortal(ほぼ即時反映)とCentral Repositoryとの同時リリースすると、「GradlePluginPortalにはあるけどCentral Repositoryにないので使えない時間」があったり、単一リポジトリのマルチモジュールでなく複数リポジトリに分かれているライブラリはCentral Repositoryに反映されてからでないとリリース作業が行えない、とかがあったりします。

Central Repositoryでのライブラリ公開は一度やっておくとこの辺りの事情がイメージしやすくなるのかなと。

Gradleの話

GradleもなんだかんだでCentral Repositoryを使います。いっとき jcenter() を使う流れはありましたが、JCenterのサービス終了に伴い、Central Repositoryに戻りました。サービス終了が発表された時は騒ぎになりましたが、想定していたよりも混乱は少なかったように思います。今でもドキュメントに jcenter() と記述されたままのライブラリは多数ありますが、適宜読み替えましょう。

Gradleはbuild.gradleで以下のように書く必要があるので「Mavenセントラル」と言う言葉に馴染みはあるのではないでしょうか。

repositories {
    mavenCentral()
}

逆にデフォルトで使われるMavenの方がCentral Repsitoryは意識しないのかもしれないなぁ、と。

変更メモ

  • 2021-12-15T12:13
    • タイトルを 「Mavenリポジトリに最新版がある」とは から 「Mavenリポジトリに最新版がある」と言うときの基礎知識 に変えました。
  • 2021-12-15T15:50
    • さらにタイトルを Javaで「ライブラリの最新版がある」と言うときの基礎知識 に変えました。
  • 2023-04-20T14:50
    • s/セントラリリポジトリ/セントラルリポジトリ/g
    • jcenter() 周りを終了予定から終了後の内容に書き換え
    • https://search.maven.org の代わりに https://central.sonatype.com を使用する(画像は面倒だから変えてない)

Gradle+SpringBootでLog4j 2のバージョン更新(DependencyManagementPlugin不使用)

Log4j 2のバージョンアップのやりかた からの派生。 特化した内容なので別エントリにします。

2021-12-13追記: 今気づいたけどSpring公式ブログの[Log4J2 Vulnerability and Spring Boot](https://spring.io/blog/2021/12/10/log4j2-vulnerability-and-spring-boot)見るほうがシンプルでよいかもです。 本稿はバージョンダウンとか選択とかに興味あればって感じ。……`./gradlew dependencyInsight --dependency {}` なんてあったのね。
2021-12-20追記: `2.17.0` 出てますのでコピペしてそのままにせず適宜読み替えてくださいね。

条件

  • Gradle 7.3.1
  • SpringBoot 2.6.1
  • SpringBootで、Gradleで、SpringのDependencyManagementPluginを使わない。
  • Log4j 2のバージョンを一時的に上げたい(永続でなく、下げたいではない)

DependencyManagementPlugin使ってるなら素直に設定して終了です。

準備: SpringBootでlogbackじゃなくLog4j 2を使う

spring-boot-starter-log4j2 があるので、これを入れればOK・・・ではないんですよね。 単に追加するだけだと起動できません。

SLF4J: Class path contains multiple SLF4J bindings.
...
Caused by: org.apache.logging.log4j.LoggingException: log4j-slf4j-impl cannot be present with log4j-to-slf4j
    at org.apache.logging.slf4j.Log4jLoggerFactory.validateContext(Log4jLoggerFactory.java:49)
    at org.apache.logging.slf4j.Log4jLoggerFactory.newLogger(Log4jLoggerFactory.java:39)
    at org.apache.logging.slf4j.Log4jLoggerFactory.newLogger(Log4jLoggerFactory.java:30)
...

こんなログがでます。SLF4Jが複数あるからだめーって。 ちゃんとSpringBootのリファレンスには書いてるので、その通りしましょう。

ということで、Log4j 2を使う最小セットはこうなります。

dependencies {
    implementation platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-log4j2'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    modules {
        module('org.springframework.boot:spring-boot-starter-logging') {
            replacedBy 'org.springframework.boot:spring-boot-starter-log4j2'
        }
    }
}

./gradlew dependencies --configuration runtimeClasspath で一覧。

いい感じですね。……「いい感じ」と言われてもわからないかもなので一応。期待通りConstraintされている、Log4j 2が入っている、Logbackが入っていない、あたりです。あとLog4j 2もちゃんと2.14.1です。

もちろんMavenと同じくexcludeしてもいいです。選べるならお好きなやり方で。選べるだけの知識は必要になりますが。starter1つとかならいいけど、複数ある時に全部からexcludeするの案外めんどいよ。

Log4j 2のバージョンアップ

さあバージョンを上げましょう。 SpringBootのリファレンスに書いてある通り、ですがDependencyManagementPluginを使っていないのでlog4j2.versionプロパティを使用できません。残念。

書かれてることに従ってやるなら、以下の追加です。

dependencies {
    implementation platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-log4j2'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    modules {
        module('org.springframework.boot:spring-boot-starter-logging') {
            replacedBy 'org.springframework.boot:spring-boot-starter-log4j2'
        }
    }

    configurations.all {
        resolutionStrategy.eachDependency { DependencyResolveDetails details ->
            if (details.requested.group == 'org.apache.logging.log4j') {
                details.useVersion '2.15.0'
            }
        }
    }
}

./gradlew dependencies で見てもらったり、IDEの依存ライブラリでバージョン確認。

画像はIntelliJ IDEAさん。ライブラリ一覧を軽く見たい時はコマンドとかGradleウィンドウのDependenciesとかでなく、ProjectウィンドウのExternal Librariesで見ることが多い私です。 タイプしたらハイライトしてくれるしね。

やりたいことはできた。けれど、いくつかの問題があります。

  • build.gradle はシンプルに保ちたい。できればifなんて書きたくない。
  • Log4j 2のライブラリって全部org.apache.logging.log4jでいいんだっけ?他ってなかったっけ?他がこのGroup使ったりしないっけ?(最後は杞憂だけど。)
  • configurationsresolutionStrategyなんて初めて見た←

仕切り直し。 spring-boot-dependenciesでもplatformを使ってるので、こちらもplatformでやればいいです。Log4j 2は log4j-bom があります。

強制したい時はenforcedPlatformとかもありますが、今回はバージョンアップなのでplatformでもよいです。

ということで、最終的なbuild.gradledependenciesはこうなります。

dependencies {
    implementation platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
    implementation platform('org.apache.logging.log4j:log4j-bom:2.15.0')
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-log4j2'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    modules {
        module('org.springframework.boot:spring-boot-starter-logging') {
            replacedBy 'org.springframework.boot:spring-boot-starter-log4j2'
        }
    }
}

オーライ。

バージョン競合時の話

Gradleのバージョン解決は非常に細やかな制御ができるのですが、何もしない場合、競合したら 新しいバージョンが優先 されます。 このため「build.gradleに明示したバージョンが必ずしも使われる訳ではない」という意味です。今回のようなのだと新しいバージョンへの上書きなので問題ないのですが、古いバージョンへの上書きは期待通り動作しません。

仮に今回 2.14.1 -> 2.15.0 ではなく 2.14.0 へのバージョンダウンとかだと、platform ではこの方法ではうまくいきません。 たとえば以下のように log4j-core2.14.0 を明示しても、spring-boot-dependencies2.14.1が使用されます。

dependencies {
    implementation platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
    implementation 'org.apache.logging.log4j:log4j-core:2.14.0'
}

org.apache.logging.log4j:log4j-core:2.15.0 なら (log4j-corelog4j-apiは)2.15.0 が使用されます。 BOMが使えるなら先に挙げた enforcedPlatform でバージョンダウンも強制できますが、バージョンを下げるのは案外面倒ってだけ記憶の片隅に置いておくといいかなと思います。 他の解決方法もほんと色々あるのですが、ここでは割愛します。

Mavenだと<dependencyManagement>してても<dependency>で明示した方が優先されるので、注意が必要なところ。

ちゃんとした話はGradleのリファレンスを参照してください。 こちらでも書かれていますが、Mavenだと近さで選ばれます。

蛇足: SpringのDependencyManagementPluginを使わないわけ

たいそうな理由もないんですが、バージョン解決の仕組みが重なるのが嫌なんです。 「この設定Gradleだっけ、プラグインだっけ」とか、「Gradleのバージョンアップでこの辺変わったけどどう影響するんだろ」とか。

  • GradleのSpringBootPluginからDependencyManagementPluginが分離された
  • GradleにBOMサポートが入った

そこそこ長くGradleとSpringBootを使っているので、この2つのイベントをリアルタイムで見ています。 そしてGradleのバージョンもガンガン上がってる。「ついていくならGradleのほうかな」と、少なくとも依存解決の文脈ではそう思っているので、DependencyManagementPluginの使用は避けています。

別に使ってるのを無理矢理外したりはしないけどね。

あとがき

Gradleむずかしい(Mavenが簡単とは言っていない)。

BOMがあれば、BOMをplatformで使うのがいいと思います。log4j-bomspring-boot-dependenciespom.xml読んでたら見つけました。 複数のBOMを読ませたら競合バージョン解決が行われ、基本的には新しい方が使われます。Mavenの深さとどちらがいいかは好みが分かれるところ。

BOMがなければconfigurationsとかで対象ライブラリ判定して適用する感じになります。 スクリプト書けるんでそれなりに柔軟な判定は可能、でもbuild.gradleにあまり処理は書きたくない感もある。

Log4j 2のバージョンアップのやりかた

Log4j 2に脆弱性があるらしい、バージョンアップしたら治るらしい。」

本日話題のこのテーマで軽く書いておきます。 未完です。

f:id:irof:20211213114828p:plain

未完公開の言い訳。更新した内容は最後に書いてます。大きな間違いは(今のとこ)ないので、よかった。

2021-12-20追記: 2.17.0 出てますのでコピペしてそのままにせず適宜読み替えてくださいね。

とにかくバージョンを上げよう

……リリースできるかは別の話として。 バージョンを上げられないことには話になりません。ということでとにかくあげましょう。

Log4j 2のようなログライブラリは多くのプロダクトで使用されています。 意識する/しないに関わらず、ログライブラリは何かしら関連があると思うべきでしょう。

使用しているかの調べ方

常時依存ライブラリリストを出力するなどして管理しているのであればそれを見ればいいだけの話ですが、そうでなければ、 mvn dependency:tree(使ってるかだけなら mvn dependency:list の方がいいかもだけど) や gradle dependencies を叩いて log4j ワードを見るのが汎用的で取り早いでしょう。 プロダクトがたくさんあるなら、シェルスクリプトとかでやるといいかもですね。

出てこなかったら終了ですが、まず何かしら出てくると思います。

今回の話だと log4j-core が対象っぽい(log4j-apiだけなら影響ないんじゃ?と思ったりはしてますが調べてないです)ので、log4j-coreが無いならあげないのもありかもしれませんが、「Log4j 2は一部使っていますが、log4j-to-slf4jだけなんで上げなくていいんです」みたいな説明はそれはそれで面倒なので、一律「使ってるので更新しました」というのもありかなって。

pom.xmlbuild.gradleに書いていないから使っていない」は誤りです。

ログライブラリの構造

Javaのそれなりの規模のアプリケーションは、歴史的経緯もあり、このような構造になっています。

f:id:irof:20211213111413p:plain

この図ではログAPIが3種類ありますが、必要なだけ用意して、それを一つのロガー(実際にログを書き出すもの)に流し込む構造です。 これにより、異なるログAPIを使用しているライブラリやフレームワークでも共存できるようになっています。

色を塗っているところが依存ライブラリです。 自分達のコードが使用しているのは一番左だけですが、全てなければ実行時エラーになってしまいます。

下半分の色を変えているのは、ログライブラリが環境(アプリケーションサーバー)に提供される場合があるため。 今回のアップデート対象は「ロガー」のところなので、冒頭に挙げた「挙げなくていい」や、デフォルトではログAPIだけLog4j 2を使用しているSpringBootがLog4j 2に変えてなかったら影響ないよと言っているのはこのためです。

Spring Boot users are only affected by this vulnerability if they have switched the default logging system to Log4J2. The log4j-to-slf4j and log4j-api jars that we include in spring-boot-starter-logging cannot be exploited on their own. Only applications using log4j-core and including user input in log messages are vulnerable.

このアナウンスが出たので慌てて上げなくていいところは多そう。

上記の構造のため、本エントリなどの「バージョンアップ」は、providedの場合には通用しません。 アプリケーションサーバー側での対応が必要になると思います。(最近アプリケーションサーバー使わないから詳しくはわからない。)

どうでもいいけどcompile or providedじゃなくruntime or providedのが適切だと思うけど、大勢に影響ないし、画像直すの面倒だから直さない・・・。

ログライブラリの構造のもう少し詳しい情報は以下が参考になると思います。

バージョンアップのやり方

Log4j 2は複数のjarで構成されます。 MavenCentralRepositoryにあるものは https://search.maven.org/search?q=g:org.apache.logging.log4j で見られ、このブログを書いている現在、57個あります。 2.15.0 がリリースされているもので29個。いちいち見ていられません。

こういう複数jarのライブラリはBOMと呼ばれるバージョン管理用の一式が提供されていたりします。BOMはBill of Materials(部品表)のことで、Mavenでも公式に使用されている言葉です。

Log4j 2も log4j-bom があるので、これを使います。

MavenだとdependencyManagementタグです。

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-bom</artifactId>
            <version>2.15.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

Gradleならplatformです。

dependencies {
    implementation platform('org.apache.logging.log4j:log4j-bom:2.15.0')
}

BOMを使用するとバージョンがこちらで管理されるので、log4j-api などを直接dependencyに追加する場合などもバージョンを記述しません。 仮に記述しても新しいバージョンが優先される(Gradleの競合解決に依存する)ので注意が必要です。

注意: log4j-apiなどの個別バージョンを指定してはいけない

すべてのLog4j 2ライブラリのバージョンを指定したりその違いを担保できないのであれば、個別に指定してはいけません。

たとえば以下のように記述した場合

dependencies {
    implementation 'org.apache.logging.log4j:log4j-api:2.15.0'
    // ...他にもある
}

こんなことになったりします。

f:id:irof:20211210173025p:plain

Log4j 2のバージョン食い違い。運良く動作すればよいですが、メソッドシグネチャの変更などがあると実行時にメソッドが見つからない例外が出たりします。辛い。

SpringBootでのバージョンアップ

公式アナウンス見た方がいいかも。(書いてること同じだけど)

SpringBootを使う場合、意識しているかはさておき spring-boot-dependencies を使用しているはずです。 spring-boot-dependenciesソースはGradleだったりするんで、pomファイル を見た方がイメージしやすいでしょう。 Gradleを使っててもMavenの知識が必要になってくるのはこういうところ・・・。

バージョンを上書きすると、「SpringBootチームが検証していくれているバージョンの組み合わせと違う組み合わせ」を使うことになります。 もちろん上書きしなくても動かない可能性はあって、自分達の責任範囲ではあるのですが、より動かない確率が高まるのは事実です。 SpringBootに乗っかるのであれば、この辺りのバージョン上書きはしないに越したことはない、と思っていたりします。

2021-12-23にリリースされるSpringBoot 2.6.2 でLog4j 2の2.15.0を使うようになってくれるようなので、もし今回の対応で上書きしても、SpringBootのバージョン上げる時は忘れずに削除するようにしましょう。 でないとLog4j 2が古いバージョンに取り残される可能性が出てきます。

バージョン上書きに関する警告はSpringBootのリファレンスにも書かれています。

Each Spring Boot release is designed and tested against a specific set of third-party dependencies. Overriding versions may cause compatibility issues and should be done with care.

SpringBoot x Maven

Maven<properties> タグでバージョンが列挙されていて、これで使うバージョンを上書きできるようになっています。log4j2.version でテキスト検索してもらえれば log4j-bomが使用されているのがわかるかと思います。 (なんで log4j-to-slf4j も入ってるんだろ・・・何かとかちあってるのかな?他のBOMに入ってるのよりトップレベルで書いてるののほうが優先されたりする。)

なのでSpringBootをMavenで使用している場合などは、propertyを上書きするのが正道です。pom.xmlに以下のように書く。

<properties>
  <log4j2.version>2.15.0</log4j2.version>
</properties>

もしくはmvnコマンドのパラメタでも指定できます。バージョンアップを一時的に試したいとかならパラメタのが楽と言えば楽かもですね。

SpringBoot x Gradle

SpringDependenctManagementプラグインを使用している、要するに apply plugin: 'io.spring.dependency-management' と書いているなら、Mavenと同じプロパティが使用できます。 つまり、 ext['log4j2.version'] = '2.15.0'build.gradle に書いてあげればOKかと思います。

SpringのDependencyManagementPluginを使用していない場合については別に書きました。(2021-12-11T22:40)

irof.hateblo.jp

脱線: 「Mavenリポジトリ」の指すもの

MavenCentralRepositoryは何も設定しなければデフォルトで使用されるリポジトリと思っていただければOKです。なにも修飾せず「Mavenリポジトリ」と言えば、MavenCentralRepositoryを指します。

よりダイレクトに見るならば https://repo1.maven.org/maven2/org/apache/logging/log4j/ です。こちらになければ、リポジトリを明示的に指定するかローカルリポジトリに自身でインストールしない限り、 mvn コマンドで使用できません。 なお https://mvnrepository.com/ はMavenCentralRepositoryを含めたMavenリポジトリを横断的に検索する、個人が運営してくれているサービスです。

irof.hateblo.jp もう少しくわしくかきました。

検証とかリリースとか

2.15.0 に更新することで、なんらかの互換性を失っているかもしれません。最近はあまり踏みませんが。 今回は2.14.1 から 2.14.2でなく2.15.0 なので、そこそこ大きな変更が予想されます。

互換性を失っている場合、直接使用している部分はコンパイルエラーになりますが、大抵はそうではなく、使用しているライブラリが使用しているメソッドが無くなってたりシグネチャが変わっていたりなどです。 こういう時は実行時に NoSuchMethodException とかが出てきます。困る。 でもいちいち調べるの無理なんで、自動テストで担保しましょう。

動作レベルの検証はユニットテストでいけますが、ログライブラリは環境による出力に差があるもの(ユニットテストならコンソールにプレーンなテキストで出すけど、運用環境だとJSONで出したり、ファイルに出したり)なので、ユニットテストだけでは担保できません。 ログを監視する仕組みも含めた環境に乗っけてのテストが必須になってきます。

また、当然のように性能にも差が出てきます。 この辺りのテストも自動化しておくと、「負荷テストぶん回しながらリリースに関わる説明とか調整を行う」ができたりします。

あと本番環境へのリリース後もしばらくは検証とみなせます。 Blue/Greenデプロイなどで切り戻せるようにしておけば、負荷テストを回しながら(致命的な問題があったら戻す)、セキュリティリスクの高い状況を早期に脱するためにリリースをしてしまう。なんて選択肢も出てきます。

申し送り(2021-12-10T18:00)

ちょっと用事があるので、推敲もしていませんが公開しておきます。あとで追記したり、ガラッと書き換えるかも。

更新履歴

  • 2021-12-11T19:30
  • 2021-12-13T11:20
    • ログライブラリの構造のセクション追加
    • BOMの説明追加
    • SpringBootでのバージョンアップに関する記述をMavenの項から引き上げ、リファレンスへのリンクと引用追加
  • 2021-12-14T12:40
    • Log4j 2のjar関連を示すFAQへのリンク追加
    • Gradleの「書いたバージョンが使われる」を「最新が使われる」に修正