web-dev-qa-db-ja.com

log4netでユーザー名をキャプチャする

現在、すべてのlog4netイベントをデータベースに書き込んでいますが、問題なく動作しているようです。ログインしたユーザーアカウントをキャプチャするには、次のコードを使用します。

HttpContext context = HttpContext.Current;
if (context != null && context.User != null && context.User.Identity.IsAuthenticated)
{
    MDC.Set("user", HttpContext.Current.User.Identity.Name);
}

ユーザーコンテキストが関連付けられていないイベント(つまり、公開Webページのユーザー)を除いて、コードは問題ないようです。その場合、log4netキャプチャは、最後にログインしたユーザーアカウントを書き込む場合(悪い)とnullを書き込む場合(良い)のように見えます。誰もがこの機能をすべての場合に確実に機能させることができましたか? MDCは推奨されていない機能であるというメモを見たと思いますが、推奨されている代替機能を見つけることができませんでした。

注:MDCにアカウント名が設定されているのは奇妙だと思いますが、アクティブなユーザーがいない場合はクリアされません。それは問題の一部である可能性があります。ただし、ユーザー名もクリアするMDCコード抽出は見つかりませんでした。

19

HttpContextで利用できる情報で十分な場合、つまり、投稿したサンプルコードで正しい答えが得られ(MDCの問題を除く)、次のように記述しない場合。

HttpContext context = HttpContext.Current; 
if (context != null && context.User != null && context.User.Identity.IsAuthenticated)
{     
  MDC.Set("user", HttpContext.Current.User.Identity.Name); 
} 

多くの場合、log4net用に独自のカスタムPatternLayoutConverterを作成することで、ユーザー名を「自動的に」ログに追加できる場合があります。それらは非常に簡単に記述でき、組み込みのものと同じようにlog4netロギング構成で構成できます。

カスタムPatternLayoutConverterを作成する方法の一例については、次の質問を参照してください。

カスタムlog4netプロパティPatternLayoutConverter(インデックス付き)

そのリンクの例を使用すると、次のようなことができる可能性があります。

namespace Log4NetTest
{
  class HttpContextUserPatternConverter : PatternLayoutConverter
  {
    protected override void Convert(System.IO.TextWriter writer, LoggingEvent loggingEvent)
    {
      string name = "";
      HttpContext context = HttpContext.Current;
      if (context != null && context.User != null && context.User.Identity.IsAuthenticated)
      {
        name = context.User.Identity.Name;
      }
      writer.Write(name);
    }
  }
}

これをlog4netで次のように構成します。

  //Log HttpContext.Current.User.Identity.Name
  <layout type="log4net.Layout.PatternLayout">
    <param name="ConversionPattern" value="%d [%t] %-5p [User = %HTTPUser] %m%n"/>
    <converter>
      <name value="HTTPUser" />
      <type value="Log4NetTest.HttpContextUserPatternConverter" />
    </converter>
  </layout>

さらに、Optionパラメーター(上記のリンクの例を参照)を使用して、HttpContext.Current.ItemsまたはHttpContext.Current.Sessionコレクションから特定のアイテムをプルする他のパターンコンバーターを構築できます。

何かのようなもの:

namespace Log4NetTest
{
  class HttpContextSessionPatternConverter : PatternLayoutConverter
  {
    protected override void Convert(System.IO.TextWriter writer, LoggingEvent loggingEvent)
    {
      //Use the value in Option as a key into HttpContext.Current.Session
      string setting = "";

      HttpContext context = HttpContext.Current;
      if (context != null)
      {
        object sessionItem;
        sessionItem = context.Session[Option];
        if (sessionItem != null)
        {
          setting = sessionItem.ToString();
        }
        writer.Write(setting);
      }
    }
  }
}


namespace Log4NetTest
{
  class HttpContextItemPatternConverter : PatternLayoutConverter
  {
    protected override void Convert(System.IO.TextWriter writer, LoggingEvent loggingEvent)
    {
      //Use the value in Option as a key into HttpContext.Current.Session
      string setting = "";

      HttpContext context = HttpContext.Current;
      if (context != null)
      {
        object item;
        item = context.Items[Option];
        if (item != null)
        {
          setting = item.ToString();
        }
        writer.Write(setting);
      }
    }
  }
}

また、これらのリンクが役立つ場合があります。

http://piers7.blogspot.com/2005/12/log4net-context-problems-with-aspnet.html

ここで、ブロガーは、HttpContextから値をログに記録するための別のソリューションを提案しています。ブログ投稿を読んで、問題の説明と解決策を確認してください。ソリューションを要約すると、彼はオブジェクトをGlobalDiagnosticContext(MDCのより新しい名前)に格納します。 log4netがオブジェクトの値をログに記録するとき、ToString()を使用します。彼のオブジェクトの実装は、HttpContextから値を取得します。

だから、あなたはこのようなことをするかもしれません:

public class HttpContextUserNameProvider
{
  public override string ToString()
  {
    HttpContext context = HttpContext.Current;  
    if (context != null && context.User != null && context.User.Identity.IsAuthenticated)
    {
      return context.Identity.Name;
    }
    return "";
  }
}

このオブジェクトのインスタンスは、プログラムの早い段階でGlobalDiagnosticContext(MDC)に配置でき、HttpContext.Currentにアクセスしているため、常に正しい値を返します。

MDC.Set("user", new HttpContextUserNameProvider());

これは私が提案したものよりもはるかに簡単なようです!

完全を期すために、誰かがNLogで同じことを行う方法を知りたい場合、NLogはほとんど/すべてのHttpContext情報を「aspnet-*」LayoutRenderersから利用できるようにしているようです。

https://github.com/nlog/nlog/wiki/Layout-Renderers

27
wageoghe

Log4Net 公式APIドキュメント によると、MDCは非推奨です:

MDCは非推奨になり、プロパティに置き換えられました。現在のMDC実装は、ThreadContext.Propertiesに転送されます。

それ以外のMDC.Setは値として文字列のみを受け入れるため、@ wageogheからの最後のソリューション(HttpContextUserNameProviderを使用するソリューション)は機能しません。

私の解決策は、log4net.GlobalContextでHttpContextUserNameProviderを使用することでした。これは、 公式APIドキュメント でも提案されています。

  • Log4netの初期化後にこの即時を追加します(たとえば、Global.Application_Startで)

    log4net.GlobalContext.Properties["user"] = new HttpContextUserNameProvider();
    
  • このクラスを追加する

    public class HttpContextUserNameProvider
    {
        public override string ToString()
        {
            HttpContext context = HttpContext.Current;
            if (context != null && context.User != null && context.User.Identity.IsAuthenticated)
            {
                return context.User.Identity.Name;
            }
            return "";
        }
    }
    
  • 「user」プロパティ値を追加して、log4net構成を変更します。次に例を示します。

    <layout type="log4net.Layout.PatternLayout" value="%property{user}"/>
    
21

Log4Netバージョンの時点で 1.2.11 アペンダーパターンを使用して、ASP .NETリクエストを介して許可されたユーザーを取得できます。例:.

%aspnet-request{AUTH_USER}
10
Ben Smith

これは純粋な推測ですが、共有リクエストスレッド、つまりThreadPoolスレッドに関連する問題である可能性が非常に高いようです。 MDC値を設定すると、それは現在のスレッドに関連付けられ、そのスレッドはリクエストの最後にThreadPoolに返され、その後のリクエストで再利用されます。値が上書きされない場合は、新しいリクエストで古い値が表示される可能性があります。

このデータをリクエストの開始イベントと終了イベントで管理することを検討してください。リクエストの開始時にユーザー名を設定し、リクエストの終了時にデータをクリアできます。これにより、このデータに正しい有効期間、つまりリクエストの有効期間が与えられます。

2
Tim Lloyd

あなたがやりたいことをする2つの異なる方法があります%identityおよび%username

これらは、アペンダーパターンで使用できます。

私は以前にそれをしましたが、私がそれをクリアした次の投稿を読むまで、予期しない結果がありました。

この投稿を参照してください: アペンダーでファイルに名前を付けると、Log4Netは%usernameプロパティを見つけることができません

2
phillip