Log4J:Loggerインスタンスを作成するための戦略
新しいJavaプロジェクトにLog4Jロギングフレームワークを使用することにしました。Loggerインスタンスの作成/管理にどの戦略を使用する必要があるのか、またその理由は?
クラスごとにLoggerの1つのインスタンス。
class Foo { private static final Logger log = Logger.getLogger(Foo.class); }
- スレッドごとにLoggerの1つのインスタンス
- アプリケーションごとにLoggerの1つのインスタンス
- 水平スライシング:アプリケーションの各レイヤー(ビューレイヤー、コントローラーレイヤー、永続化レイヤーなど)にLoggerの1つのインスタンス
- 垂直スライス:アプリケーションの機能パーティション内のLoggerの1つのインスタンス
注:この問題は、これらの記事ですでにある程度検討されています。
通常、クラスごとにロガーをセットアップします。これは、論理的なコンポーネントだからです。スレッドは既にログメッセージの一部であるため(フィルターで表示される場合)、そのようにロガーをスライスすることはおそらく冗長です。
アプリケーションまたはレイヤーベースのロガーに関して、問題はそのロガーオブジェクトを貼り付ける場所を見つけなければならないことです。それほど大したことではありません。より大きな問題は、いくつかのクラスが複数のアプリケーションからの複数のレベルで使用される可能性があることです...ロガーを正しくするのは難しいかもしれません。または少なくともトリッキーです。
...そして、あなたが望む最後のことは、あなたのロギング設定における悪い仮定です。
アプリケーションとレイヤーに関心があり、分離ポイントが簡単な場合は、NDCが最適です。コードは時々少し過剰になる場合がありますが、Foo.bar()がレイヤーYのアプリケーションXから呼び出されたことを示す正確なコンテキストスタックによって何回保存されたかわかりません。
最も使用される戦略は、クラスごとにロガーを作成することです。新しいスレッドを作成する場合、それらに便利な名前を付けてください。そのため、それらのロギングは簡単に区別できます。
クラスごとにロガーを作成すると、クラスのパッケージ構造でロギングのオン/オフを切り替えることができるという利点があります。
log4j.logger.org.Apache = INFO
log4j.logger.com.example = DEBUG
log4j.logger.com.example.verbose = ERROR
上記はすべてのApacheライブラリコードをINFO
レベルに設定し、詳細パッケージを除き、ロギングを独自のコードからDEBUG
レベルに切り替えます。
これはベストプラクティスではないことは確かですが、コード行を節約するために、アプリケーションの起動時間をある程度短縮しました。具体的には、貼り付ける場合:
Logger logger = Logger.getLogger(MyClass.class);
...開発者は「MyClass」を現在のクラス名に変更することを忘れることがよくあり、いくつかのロガーは常に間違った場所を指しています。これは悪いです。
私は時々書きました:
static Logger logger = LogUtil.getInstance();
そして:
class LogUtil {
public Logger getInstance() {
String callingClassName =
Thread.currentThread().getStackTrace()[2].getClass().getCanonicalName();
return Logger.getLogger(callingClassName);
}
}
そのコードの「2」は間違っているかもしれませんが、要点はそこにあります。 (クラスロード時に、静的変数として)パフォーマンスヒットを取得してクラス名を見つけます。これにより、開発者が実際にこれをタイプミスしたり、エラーを導入したりすることはありません。
一般に、実行時の開発者エラーを防ぐためにパフォーマンスを失うことに興奮していませんが、シングルトンとして発生した場合は、一度ですか?多くの場合、私にとって良い取引のように聞こえます。
他の人から言われているように、クラスごとにロガーを作成します:
private final static Logger LOGGER = Logger.getLogger(Foo.class);
または
private final Logger logger = Logger.getLogger(this.getClass());
ただし、過去に他の情報をロガーに保持しておくと便利です。たとえば、Webサイトがある場合、すべてのログメッセージにユーザーIDを含めることができます。そうすれば、ユーザーが行っているすべてをトレースできます(問題のデバッグなどに非常に役立ちます)。
これを行う最も簡単な方法は、MDCを使用することですが、ユーザーIDを含む名前を持つクラスのインスタンスごとに作成されたロガーを使用できます。
MDCを使用するもう1つの利点は、SL4Jを使用する場合、MDCの値に応じて設定を変更できることです。そのため、特定のユーザーのすべてのアクティビティをDEBUGレベルで記録し、他のすべてのユーザーをERRORのままにしておきたい場合は、可能です。 MDCに応じて、異なる出力を異なる場所にリダイレクトすることもできます。
便利なリンク:
http://logging.Apache.org/log4j/1.2/apidocs/org/Apache/log4j/MDC.html
- クラスごとに1つのロガーを作成します。
- Commons Loggingを必要とする依存関係がある場合(おそらく)、Commons Loggingにはslf4jの bridge を使用してください。 Commons Loggingインターフェースを使用して(クラスごとに)ロガーをインスタンス化します:
private static final Log log = LogFactory.getLog(MyClass.class);
- IDEショートカットを使用してこのパターンを明示します。この目的には、IDEAの liveテンプレート を使用します。
- [〜#〜] ndc [〜#〜] (文字列のスレッドローカルスタック)または [〜#〜] mdc [〜を使用して、コンテキスト情報をスレッドに提供します#〜] (文字列のスレッドローカルマップ→?)。
テンプレートの例:
private static final Log log = LogFactory.getLog($class$.class); // live template 'log'
if (log.isDebugEnabled())
log.debug(String.format("$string$", $vars$)); // live template 'ld', 'lw', 'le' ...
別のオプション:ロギングでAspectJクロスカットを試すことができます。 ここで:ロギングの単純化を確認してください。 ([〜#〜] aop [〜#〜]を使用したくない場合は、slf4j)
//Without AOP
Class A{
methodx(){
logger.info("INFO");
}
}
Class B{
methody(){
logger.info("INFO");
}
}
//With AOP
Class A{
methodx(){
......
}
}
Class B{
methody(){
......
}
}
Class LoggingInterceptor{
//Catched defined method before process
public void before(...xyz){
logger.info("INFO" + ...xyz);
}
//Catched defined method after processed
public void after(...xyz){
logger.info("INFO" + ...xyz);
}
.....
}
PS:[〜#〜] aop [〜#〜]の方が良いでしょう、それはDRY(Do n't Repeat Yourself)方法です。
クラス名にリンクされていないカスタムロガーを作成するための最良かつ最も簡単な方法は次のとおりです。
// create logger
Logger customLogger = Logger.getLogger("myCustomLogName");
// create log file, where messages will be sent,
// you can also use console appender
FileAppender fileAppender = new FileAppender(new PatternLayout(),
"/home/user/some.log");
// sometimes you can call this if you reuse this logger
// to avoid useless traces
customLogger.removeAllAppenders();
// tell to logger where to write
customLogger.addAppender(fileAppender);
// send message (of type :: info, you can use also error, warn, etc)
customLogger.info("Hello! message from custom logger");
今、同じクラスに別のロガーが必要な場合、問題ありません:)新しいロガーを作成するだけです
// create logger
Logger otherCustomLogger = Logger.getLogger("myOtherCustomLogName");
上記のコードを参照して、新しいfileappenderを作成して、出力が他のファイルで送信されるようにします
これは(少なくとも)2つの状況に役立ちます。
情報から個別のエラーが必要な場合、警告する
複数のプロセスを管理し、各プロセスからの出力が必要な場合
追伸質問がありますか?お気軽にお尋ねください! :)
複数のEAR/WARをデプロイする場合は、log4j.jarをクラスローダー階層の上位にパッケージ化する方が適切な場合があります。
i.e。 WARやEARではなく、コンテナのシステムクラスローダーで、そうでない場合、複数のLog4Jインスタンスが同じファイルに同時に書き込みを行うため、奇妙な動作が発生します。
一般的な規則は「ロガーprクラスであり、クラス名をその名前として使用する」です。これは良いアドバイスです。
私の個人的な経験では、このロガー変数は静的であると宣言されるべきではなく、新規ごとに取得されるインスタンス変数である必要があります。これにより、ロギングフレームワークは、発信元に応じて2つの呼び出しを異なる方法で処理できます。静的変数は、そのクラスのすべてのインスタンス(クラスローダー内)で同じです。
また、選択したロギングバックエンドですべての可能性を学ぶ必要があります。可能性を期待していなかった可能性があります。