c# - 使用 log4net 捕获用户名

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

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

注意:我觉得很奇怪,MDC 设置了一个帐户名,但如果没有用户处于事件状态,则永远不会清除。这可能是问题的一部分。但是,我没有找到任何同时清除用户名的 MDC 代码摘录。

最佳答案

如果 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 的示例,请参阅此问题:

Custom log4net property PatternLayoutConverter (with index)

使用该链接中的示例,您可以执行如下操作:

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 似乎通过其“aspnet-*”LayoutRenderers 使大部分/所有 HttpContext 信息可用:

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

关于c# - 使用 log4net 捕获用户名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4813242/

相关文章:

asp.net - 回发后 DropDownList 为空

java - GWT 日志记录仅显示级别 SERVE

database - 尝试使用持久性时没有 Control.Monad.Logger.MonadLogger 的实例

c# - 检查表达式中的 Null

C# System.Windows.Forms.WebBrowser 需要安装 Flash

c# - where 条件下的动态列

c# - LINQ 需要 40 秒才能结束查询 - 性能

asp.net - 发布 asp.net mvc 缺失图像

c# - 将 OnClick 事件添加到 ASP.NET 控件

python - 登录类(class) - Python