ロガーがクラスごとにロガーの使用を推奨するのはなぜですか?


88

NLogのドキュメントによると:

ほとんどのアプリケーションは、クラスごとに1つのロガーを使用します。ここで、ロガーの名前はクラスの名前と同じです。

これは、log4netが動作するのと同じ方法です。なぜこれが良い習慣なのですか?


1
うーん。ここには2つの問題があるようです。1つはクラスごとに実際のログオブジェクトを持っていること、もう1つはログの名前をクラスと同じにすることです。
Peter

回答:


59

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);
}

アプリごとに1つのロガー(または同様のもの)

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回行うことができ、深刻なパフォーマンスの問題を排除できます。


おかげで、それは物事を明確にするのに役立ちます。クラス名とメソッドを手動でメッセージに挿入していました(つまり、「ImageCreator.CreateThumbnail()が呼び出されました」)が、ロガーがそれを処理できる方がよいでしょう。
ダニエルT.

1
参考までに、スレッド情報などの情報を簡単にキャプチャできるため、クラスごと(つまり静的)ではなく、インスタンスごとにロガーを使用する方が「より良い」方法になります。明らかにそれは好みの問題であり、「厳格なルール」ではありませんが、私はそれを捨てたかったのです。
Hartung 2010

7
@will、もう少し説明してもらえますか?クラスごとのロガーを使用してログを記録する場合、ロガーが現在のスレッド情報を取得できるように、常にスレッドIDをログに記録します。他のスレッド情報もロガーが利用できます。
Jeremy Wiebe 2010

@Jeremy Wiebe:これが唯一の理由ですか?アプリ全体にロガー型の単一のグローバル変数を使用しても機能的に問題はありませんか?
giorgim 2015年

1
@Giorgiいいえ、そうは思いません。最近では、CallerInformation属性を使用してこの情報を大量に取得できるため、クラスごとに1つのロガーの関連性が少し低くなります-msdn.microsoft.com/en-us/library/hh534540.aspx
Jeremy Wiebe 2015年

15

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}レイアウトで使用する。


5

私はこの選択のいくつかの理由を見ることができます。

  • ログ出力形式にロガーの名前を含めると、特定のログステートメントがどこから来たのかを常に知ることができます。
  • 特定のロガーをオンまたはオフにするか、それらのレベルを設定することにより、きめ細かいレベルで表示するログステートメントを制御できます。

4

NLogの場合、パフォーマンス上の利点もあります。ほとんどのユーザーは

Logger logger = LogManager.GetCurrentClassLogger()

スタックトレースから現在のクラスを検索すると、ある程度の(ただしそれほどではない)パフォーマンスが必要になります。


3

ほとんどの場合、クラスの名前はロガーに適切な名前を提供します。ログファイルをスキャンすると、ログメッセージが表示され、コード行に直接関連付けることができます。

これが最善のアプローチではない良い例は、HibernateのSQLログです。「Hibernate.SQL」などの名前の共有ロガーがあり、さまざまなクラスが生のSQLを単一のロガーカテゴリに書き込みます。


2

開発の観点からは、毎回ロガーオブジェクトを作成する必要がない場合が最も簡単です。一方、そうではなく、リフレクションを使用して動的に作成すると、パフォーマンスが低下します。これを解決するには、ロガーを動的に非同期で作成する次のコードを使用できます。

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);

        }
    }
}

1

2つの理由がすぐに思い浮かびます。

  1. クラスごとに個別のログがあると、特定のクラスに関連するすべてのログメッセージ/エラーを簡単にグループ化できます。
  2. クラス内にログを作成すると、クラス外ではアクセスできない可能性のある内部の詳細(たとえば、プライベート状態、クラスの実装を処理する情報など)をログに記録できます。

2
クラスレベルで定義されているかグローバルに定義されているかに関係なく、クラスにロガーがあります。グローバルロガーは、可視性の観点からクラスの「外部」ではありません。問題のクラスからグローバルロガーを参照しているので、完全に可視化できます。
ロバート

0

おそらく、カプセル化を壊すことなくクラスにのみ表示されるメソッドをログに記録できるようにしたいため、これにより、ログ機能を壊すことなく、別のアプリケーションでクラスを簡単に使用できるようになります。


1
そのクラスを別のアプリケーションで使用するのが難しくなります。好むと好まざるとにかかわらず、ロギングライブラリを参照する必要があります。
Piotr Perak 2011

0

名前空間またはクラスごとにアペンダーを簡単に構成できます。


0

NLOGを使用している場合は、構成でcallsiteを指定できます。これにより、ロギングステートメントが配置されたクラス名とメソッドが記録されます。

<property name="CallSite" value="${callsite}" />

次に、ロガー名またはアセンブリ名に定数を使用できます。

免責事項:NLOGがこの情報を収集する方法がわかりません。私の推測は反映であるため、パフォーマンスを考慮する必要がある場合があります。NLOG v4.4以降を使用していない場合、非同期メソッドにはいくつかの問題があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.