NLogのドキュメントによると:
ほとんどのアプリケーションは、クラスごとに1つのロガーを使用します。ロガーの名前はクラスの名前と同じです。
これは、log4netの動作と同じです。なぜこれが良い習慣ですか?
Log4netでは、クラスごとに1つのロガーを使用すると、ログメッセージのソース(つまり、クラスがログに書き込む)を簡単にキャプチャできます。クラスごとに1つのロガーがなく、アプリ全体に1つのロガーがある場合、ログメッセージの送信元を知るために、より多くのリフレクショントリックに頼る必要があります。
以下を比較してください。
using System.Reflection;
private static readonly ILog _logger =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public void SomeMethod()
{
_logger.DebugFormat("File not found: {0}", _filename);
}
Logger.DebugFormat("File not found: {0}", _filename); // Logger determines caller
-- or --
Logger.DebugFormat(this, "File not found: {0}", _filename); // Pass in the caller
2番目の例を使用すると、ロガーはスタックトレースを作成して、それを呼び出しているユーザーを確認する必要があります。そうしないと、コードは常に呼び出し元を渡す必要があります。クラスごとのロガースタイルでは、これを行うことができますが、呼び出しごとに1回ではなく、クラスごとに1回行うことができ、深刻なパフォーマンスの問題を排除できます。
NLogで「ファイルごとのロガー」を使用する利点:名前空間とクラス名でログを管理/フィルタリングする可能性があります。例:
<logger name="A.NameSpace.MyClass" minlevel="Debug" writeTo="ImportantLogs" />
<logger name="A.NameSpace.MyOtherClass" minlevel="Trace" writeTo="ImportantLogs" />
<logger name="StupidLibrary.*" minlevel="Error" writeTo="StupidLibraryLogs" />
<!-- Hide other messages from StupidLibrary -->
<logger name="StupidLibrary.*" final="true" />
<!-- Log all but hidden messages -->
<logger name="*" writeTo="AllLogs" />
NLoggerには、これを行うための非常に便利なコードスニペットがあります。 nlogger
スニペットは次のコードを作成します。
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
そのため、キーストロークはほとんどなく、クラスごとにロガーがあります。ロガーの名前として名前空間とクラス名を使用します。クラスロガーに別の名前を設定するには、これを使用できます:
private static NLog.Logger logger = NLog.LogManager.GetLogger("MyLib.MyName");
そして、@ JeremyWiebeが言ったように、メッセージをログに記録しようとしているクラスの名前を取得するためにトリックを使用する必要はありません:ロガーの名前(通常はクラスの名前)はファイルに簡単に記録できます(または他のターゲット)レイアウトで${logger}
を使用して。
この選択にはいくつかの理由があります。
NLogの場合にもパフォーマンス上の利点があります。ほとんどのユーザーが使用します
Logger logger = LogManager.GetCurrentClassLogger()
スタックトレースから現在のクラスをルックアップするには、ある程度のパフォーマンスが必要になります(ただし、それほどではありません)。
ほとんどの場合、クラスの名前はロガーに適切な名前を提供します。ログファイルをスキャンすると、ログメッセージが表示され、コード行に直接関連付けることができます。
これが最良のアプローチではない良い例は、HibernateのSQLログです。 「Hibernate.SQL」などの名前の共有ロガーがあります。このロガーでは、多数の異なるクラスが生のSQLを単一のロガーカテゴリに書き込みます。
開発の観点からは、毎回ロガーオブジェクトを作成する必要がない場合が最も簡単です。一方、そうしない場合は、リフレクションを使用して動的に作成すると、パフォーマンスが低下します。これを解決するために、ロガーを動的に非同期的に作成する次のコードを使用できます。
using NLog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WinForms
{
class log
{
public static async void Log(int severity, string message)
{
await Task.Run(() => LogIt(severity, message));
}
private static void LogIt(int severity, string message)
{
StackTrace st = new StackTrace();
StackFrame x = st.GetFrame(2); //the third one goes back to the original caller
Type t = x.GetMethod().DeclaringType;
Logger theLogger = LogManager.GetLogger(t.FullName);
//https://github.com/NLog/NLog/wiki/Log-levels
string[] levels = { "Off", "Trace", "Debug", "Info", "Warn", "Error", "Fatal" };
int level = Math.Min(levels.Length, severity);
theLogger.Log(LogLevel.FromOrdinal(level), message);
}
}
}
すぐに思い浮かぶ2つの理由:
おそらく、カプセル化を壊さずにクラスにのみ表示されるメソッドをログに記録したいため、これにより、ロギング機能を壊さずに別のアプリケーションでクラスを簡単に使用できるようになります。
名前空間またはクラスごとにアペンダーを簡単に構成できます。
NLOGを使用している場合は、構成で呼び出しサイトを指定できます。これにより、ロギングステートメントが配置されたクラス名とメソッドが記録されます。
<property name="CallSite" value="${callsite}" />
次に、ロガー名またはアセンブリ名に定数を使用できます。
免責事項:NLOGがこの情報を収集する方法はわかりません。私の推測では反映されるため、パフォーマンスを考慮する必要があるかもしれません。 NLOG v4.4以降を使用していない場合、非同期メソッドにはいくつかの問題があります。