理系学生日記

おまえはいつまで学生気分なのか

Loggerはstatic 変数にすべきか、インスタンス変数にすべきか

Logger は static 変数にすべきか、インスタンス変数にすべきかという話があります。

  • static 変数
public class Foo {
  private static Log log = LogFactory.getLog(Foo.class);
  ....
}
  • インスタンス変数
public class Foo {
  private Log log = LogFactory.getLog(Foo.class);
  ....
}

これについては諸説あり、宗教戦争を呼びかねない話ではありますが、ぼくの結論としては、

  1. おまえの作っているアプリケーションがどこで誰に使われる/デプロイされるか分かんないんだったら、とりあえずインスタンス変数にしとけ、な。
  2. おまえしか使わなくて、マジでパフォーマンスをバリバリにチューニングしたいんだったら、static 変数にするのを止めはしません

ということになります。

ロギング Facade 系ライブラリ 二台巨頭の見解

ロギング Facade 系ライブラリの巨頭である SLF4J、Apache Commons Logging それぞれの見解としては、

  • SLF4J
    • メリットとデメリットはまとめましたが、どちらが推奨とかは言いません><
  • Apache Commons Logging
    • 両者でメリットとデメリットがありますが、ほとんどの場合はインスタンス変数の方が良い

ということだと読み取りました。このあたりの議論についてはちょっと後段で触れますが、それぞれ SLF4J FAQhttps://wiki.apache.org/commons/Logging/StaticLog を参照してください。

static Logger の何が悪いのか

前述したとおり、このあたりのメリット、デメリットは [http://www.slf4j.org/faq.html:title=SLF4JのFAQ」にまとめられています。
f:id:kiririmode:20150526164219p:plain
もちろん static Logger にすることで、ロガーを取得する都度必要になるインスタンス生成コストから開放されますし、メモリ効率も良いです。だって、クラスのインスタンス間でロガーは共有されるのですから。
一方で、この「共有」というワードが、デメリットに対して大きく関与してきます。それは、「アプリケーション、および、コンテナ間の共有」です。

このあたりの議論は、むしろ Apache Commons Logging 側のドキュメントに詳しいので参照して頂ければと思うのですが、static 変数として Logger を持つことで対象 Logger がアプリケーション間、および、アプリケーション・コンテナ間で共有されてしまった、以下のようなことが起こり得ると警告されています。

  • To reference an underlying Log object which is part of a hierarchy configured from information at the "container" level, ie not associated with any particular "application"
  • To reference an underlying Log object which is part of a hierarchy configured from information somehow related to the current application
  • To reference a "proxy" object which will determine what the "current application" is each time a log method is invoked, and delegate to the appropriate underlying Log object at that time.
https://wiki.apache.org/commons/Logging/StaticLog

このあたりの内容については、少し補足が必要だと思います。

Java の世界においては、アプリケーションの多くはコンテナ上で動作します。Jetty、Tomcat あたりを想像してください。これらのコンテナは、内部に階層化されたクラスローダを持ち、(基本的には)上位のクラスローダでロード済のクラスは、下位のクラスローダではロードしないという仕組みになっています。*1

ここでもし、static な Logger を持つライブラリ A が、アプリケーション横断のクラスローダ(上の図だと、Common のクラスローダ) でロードされたと仮定しましょう。これにより、static な Logger は、そのコンテナで動作するアプリケーション全てにおいて「ロード済」とみなされます。結果、アプリケーションはすべて同じ Logger を共有し、(例えば)全てのログが同じログファイルに出力されてしまったり、というような悲劇を生みます。

どういう結果を呼ぶかは環境依存です。とある環境では問題がないかもしれませんし、べつの環境では致命的な問題になるかもしれません。このような予測性の欠如は、極めて運用を混乱させるので、安全側、インスタンス変数として Logger を持つという判断をした方が良いんじゃないかなと思います。

*1:Tomcat はちょっと違うけど