使用log4net捕获用户名

Sim*_*com 19 c# asp.net logging log4net

我目前将所有log4net事件写入数据库,它似乎工作正常.要捕获登录的用户帐户,我使用以下代码:

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

代码似乎没问题,除了没有与之关联的用户上下文的事件(即我们公共网页上的用户).在这种情况下,log4net捕获似乎有时写入最后登录的用户帐户(坏),有时写入空(好).任何人都有这个功能在所有情况下都可靠地工作?我相信我看到一条说明MDC不再是推荐使用的功能,但我无法找到任何推荐的替代品.

注意:我发现MDC设置了帐户名称很奇怪,但如果没有用户处于活动状态,则永远不会清除.这可能是问题的一部分.但是,我没有找到任何也清除用户名的MDC代码提取.

wag*_*ghe 27

如果HttpContext中可用的信息足够,也就是说,如果您发布的示例代码为您提供了正确答案(MDC问题除外),您只是不想写:

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

通常,您可以通过为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);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

您可以在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>
Run Code Online (Sandbox Code Playgroud)

此外,您可以构建其他使用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);
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

您可能还会发现这些链接很有用:

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 "";
  }
}
Run Code Online (Sandbox Code Playgroud)

您可以在程序的早期将此对象的实例放在GlobalDiagnosticContext(MDC)中,并且它将始终返回正确的值,因为它正在访问HttpContext.Current.

MDC.Set("user", new HttpContextUserNameProvider());
Run Code Online (Sandbox Code Playgroud)

这似乎比我提出的要容易得多!

为了完整性,如果有人想知道如何在NLog中做同样的事情,NLog似乎通过其"aspnet-*"LayoutRenderers使大部分/全部HttpContext信息可用:

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


Gia*_*rdi 22

根据Log4Net 官方API文档,MDC已被弃用:

MDC已弃用,已被"属性"替换.当前的MDC实现转发到ThreadContext.Properties.

除此之外MDC.Set只接受字符串作为值,所以@wageoghe的最后一个解决方案无法工作(使用HttpContextUserNameProvider的解决方案)

我的解决方案是使用HttpContextUserNameProvider log4net.GlobalContext,也在官方API文档中建议:

  • 在log4net初始化之后添加此immediatley(例如在Global.Application_Start中)

    log4net.GlobalContext.Properties["user"] = new HttpContextUserNameProvider();
    
    Run Code Online (Sandbox Code Playgroud)
  • 添加此课程

    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 "";
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 通过添加"user"属性值来修改log4net配置,例如:

    <layout type="log4net.Layout.PatternLayout" value="%property{user}"/>
    
    Run Code Online (Sandbox Code Playgroud)

  • 此解决方案缺乏对ado net appender等缓冲appender的支持:默认情况下,属性提供程序在缓冲区刷新时进行评估.因此,它将使用导致刷新的请求的HTTP上下文,而不是发出日志的请求.幸运的是,提供程序只需要实现`log4net.Core.IFixingRequired`以便在日志时而不是缓冲区刷新时进行评估.有关示例,请参阅[我的回答](http://stackoverflow.com/a/32308262/1178314). (3认同)

Ben*_*ith 10

从Log4Net 1.2.11开始,您现在可以简单地使用appender模式通过ASP .NET请求获取授权用户,例如

%aspnet-request{AUTH_USER}
Run Code Online (Sandbox Code Playgroud)

  • 这将获得AUTH_USER服务器变量,该变量本身是从请求标头设置的.因此,此格式仅适用于有限的场景,例如使用Windows身份验证的ASP.NET应用程序. (2认同)