2010-12-15 5 views
6

私は、既存のロギングメッセージのすべてのメッセージにカテゴリプレフィックスを追加するのが好きです。しかし、この接頭辞をすべての既存のロギングメッセージに1つずつ追加するのは面倒です。方法はありますか?クラスレベルに属性を追加するだけで、このクラスのすべてのメッセージが特定のカテゴリに記録されますか?代わりに、今、以下のような方法のカテゴリプレフィックスをlog4netメッセージに追加するには?

Log.Info("[Ref] Level 1 Starts ..."); 

私は本当にこのようなものかlog4net.ILogを定義する別の方法にしたいです。

[LoggingCategory("Ref")] 
public class MyClass 
{ 
    public void MyMethod() 
    { 
     Log.Info("Level 1 Starts ..."); 
    } 
} 
+1

このような機能のロガー階層を使用したlog4net –

+0

実際に表示されるカテゴリは何ですか?つまり、同じカテゴリにログを記録するオブジェクトはどのように関係しますか?理論的には、それぞれのカテゴリに対応するロガーとアペンダ間の関連付けを通じてconfig内でこれをすべて行うことができます。例えば、名前空間X、Y、ZのオブジェクトからのすべてのロギングはカテゴリAを表すアペンダを使用します。負担になるかもしれない質問。 –

+0

私がここで達成しようとしているのは、特定のクラスに対してログファイルを解析しやすくすることです。たとえば、クラスAとクラスBが関連していて、クラスAとBに関連するすべての行をメインログファイルから取得したいと思うでしょう。私の経験は、通常この方法で見つかったので、私にとって非常に役に立ちました。 – tonyjy

答えて

4

興味深い問題、ラフな試み...

Log4NetLogger - ログアダプタ

public class Log4NetLogger 
{ 
    private readonly ILog _logger; 
    private readonly string _category; 

    public Log4NetLogger(Type type) 
    { 
     _logger = LogManager.GetLogger(type); 
     _category = GetCategory(); 
    } 

    private string GetCategory() 
    { 
     var attributes = new StackFrame(2).GetMethod().DeclaringType.GetCustomAttributes(typeof(LoggingCategoryAttribute), false); 
     if (attributes.Length == 1) 
     { 
      var attr = (LoggingCategoryAttribute)attributes[0]; 
      return attr.Category; 
     } 
     return string.Empty; 
    } 

    public void Debug(string message) 
    { 
     if(_logger.IsDebugEnabled) _logger.Debug(string.Format("[{0}] {1}", _category, message)); 
    } 
} 

LoggingCategoryAttribute - クラスに適用

[AttributeUsage(AttributeTargets.Class)] 
public class LoggingCategoryAttribute : Attribute 
{ 
    private readonly string _category; 

    public LoggingCategoryAttribute(string category) 
    { 
     _category = category; 
    } 

    public string Category { get { return _category; } } 
} 

LogTester - テスト実装

[LoggingCategory("LT")] 
public class LogTester 
{ 
    private static readonly Log4NetLogger Logger = new Log4NetLogger(typeof(LogTester)); 

    public void Test() 
    { 
     Logger.Debug("This log message should have a prepended category"); 
    } 
} 
+0

これは非常にクールですが、すべてのログ呼び出しでGetCategoryを呼び出すのにはコストがかかるかもしれません。 – wageoghe

+0

GetCategoryをコンストラクタに移動できますか?次に、すべてのログ呼び出しでGetCategoryを呼び出さないようにします。 – tonyjy

+0

@tonyjy、@wageoghe - 良いキャッチ、コードを変更して、一度作成時にGetCategoryを呼び出すようにしました。 – Jonathan

7

あなたは属性を使ってこれを行う方法を尋ねています。 @ジョナサンの提案はおそらくうまくいくようですが、log4netの組み込み機能を使って十分な結果を得ることができるかもしれません。

クラスを「カテゴリ」にグループ化する場合は、クラス名ではなくカテゴリ名に基づいてロガーを取得できます。出力形式を設定するときは、ロガーフォーマットトークンを使用してlog4netに出力にロガー名を書き込ませることができます。

典型的には、1つのこのようなクラス名に基づいてロガーを取得するであろう:

public class Typical 
{ 
    private static readonly ILog logger = 
     LogManager.GetLogger 
      (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 

    public void F() 
    { 
    logger.Info("this message will be tagged with the classname"); 
    } 
} 

なお、このような任意の名前に基づいてロガーを取得するために、完全に許容可能である。前述の例で

public class A 
{ 
    private static readonly ILog logger = LogManager.GetLogger("REF"); 

    public void F() 
    { 
    logger.Info("this message will be tagged with REF"); 
    } 
} 

public class B 
{ 
    private static readonly ILog logger = LogManager.GetLogger("REF"); 

    public void F() 
    { 
    logger.Info("this message will be tagged with REF"); 
    } 
} 

public class C 
{ 
    private static readonly ILog logger = LogManager.GetLogger("UMP"); 

    public void F() 
    { 
    logger.Info("this message will be tagged with UMP"); 
    } 
} 

クラスAとクラスBは同じ「カテゴリ」にあるとみなされるため、同じ名前のロガーを取得します。クラスCは異なるカテゴリにあるため、別の名前のロガーを取得しました。

あなたがあなた自身の「カテゴリ」階層で(設定ファイルで)あなたのロガーを設定できます。

App 
App.DataAccess 
App.DataAccess.Create 
App.DataAccess.Read 
App.DataAccess.Update 
App.DataAccess.Delete 
App.UI 
App.UI.Login 
App.UI.Query 
App.UI.Options 

また、完全修飾ロガー名の一部だけをログに記録するロガーの出力形式を設定することができます。このようなもの:

%logger:2 

完全修飾名の最後の2つの部分を取得します。あなたのクラスの完全修飾名がある場合たとえば、:

NameSpaceA.NameSpaceB.NameSpaceC.Class 

その後、上記の形式で出力これは、ロガー名としてです:

NameSpaceC.Class 

ため、私は私の避難所」構文約100%を確認していませんそれを使用して、私は今すぐ良い例を見つけることができません。

このアプローチの欠点の1つは、カテゴリが何であるかを定義して覚えておかなければならず、各クラスに適切なカテゴリが何であるかを決定する必要があるということです(各クラスをそのカテゴリ)。また、同じカテゴリに複数のクラスがある場合、ログのオン/オフや、それらのクラスのサブセットのロギングレベルの変更はできません。

多分名前空間の階層内から単一の名前空間をログに記録することは有用であろう:

たぶん、あなたは自分の名前空間に基づいてクラスを「分類」することができます。したがって、クラスの直近の親ネームスペースをカテゴリとしてログに記録することができます。

したがって、上記の完全修飾クラス名の場合、「カテゴリ」またはロガー名として「NameSpaceC」を記録することができます。

あなたはlog4netでこれを実行できるかどうかはわかりませんが、Logger名を取得してクラス名と「上位レベル」の名前空間を取り除くために、PatternLayoutConverterを簡単に記述できます。

ここに、カスタムPatternLayoutConverterの例へのリンクがあります。私の場合、辞書の値を参照するために使用したいパラメータをとります。この場合、パラメータは完全修飾ロガー名のENDからのオフセットを表すことができます(log4netの組み込みロガー名レイアウトオブジェクトへのパラメータと同じ解釈)。しかし、追加のコードを追加して、インデックス。この完全修飾クラス名を与えられ再び

Custom log4net property PatternLayoutConverter (with index)

は、:

NameSpaceA.NameSpaceB.NameSpaceC.Class 

あなたは直接の親の名前空間は、 "カテゴリ" であることを考えるかもしれません。カスタムPatternLayoutConverter、categoryを定義し、それがパラメータを取った場合は、ご使​​用の構成は次のようになります。デフォルトでは

%category 

をそれが'.'文字を最後に、最後と次の間の部分文字列を返します。パラメータを指定すると、チェーン上の任意の離散名前空間を返すことができます。

class CategoryLookupPatternConverter : PatternLayoutConverter 
    { 
    protected override void Convert(System.IO.TextWriter writer, LoggingEvent loggingEvent) 
    { 
     //Assumes logger name is fully qualified classname. Need smarter code to handle 
     //arbitrary logger names. 
     string [] names = loggingEvent.LoggerName.Split('.'); 
     string cat; 
     if (Option > 0 && Option < names.Length) 
     { 
     cat = names[names.Length - Option]; 
     } 
     else 
     { 
     string cat = names[names.Length - 1]; 
     } 
     writer.Write(setting); 
    } 

    } 

アイデア:(エンドに対して)N番目の名前空間名を取得するにはオプションのプロパティを使用して、

class CategoryLookupPatternConverter : PatternLayoutConverter 
    { 
    protected override void Convert(System.IO.TextWriter writer, LoggingEvent loggingEvent) 
    { 
     //Assumes logger name is fully qualified classname. Need smarter code to handle 
     //arbitrary logger names. 
     string [] names = loggingEvent.LoggerName.Split('.'); 
     string cat = names[names.Length - 1]; 
     writer.Write(setting); 
    } 
    } 

または:

PatternLayoutConverterは(未テスト)のようになります@Jonathanはとてもクールですが、新しいロガーラッパーを定義して維持するために、あなたのコーディングを追加します(しかし、多くの人がそれを行い、特に厄介な負担ではありません)。もちろん、私のカスタムPatternLayoutConverterのアイデアも、あなたのカスタムコードを必要とします。

もう1つの欠点は、GetCategoryがすべてのロギングコールで非常に高価になる可能性があることです。

関連する問題