使用log4net捕获用户名

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

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

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

注意:我发现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的示例,请参阅此问题:

自定义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        

此外,您可以构建使用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

根据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(); 
  • 添加此课程

     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配置,例如:

      

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

 %aspnet-request{AUTH_USER} 

这是纯粹的猜测,但这听起来非常像它可能是与共享请求线程相关的问题,即ThreadPool线程。 设置MDC值时,它与当前线程关联,并且该线程将在请求结束时返回到ThreadPool,然后重新用于后续请求。 如果未覆盖该值,则可以在新请求中看到旧值。

考虑在请求开始和结束事件中管理此数据,您可以在请求开始时设置用户名,然后在请求结束时清除它。 这应该为该数据提供正确的生命周期,即请求的生命周期。

有两种不同的方法可以做你想要的%identity%username

这些可以用在你的appender模式中。

我之前已经完成了但是有意想不到的结果,直到我看到以下post为我清理了它。

看到这篇post: 当我在我的appender中命名文件时,Log4Net找不到%username属性

我使用了bothoghe和Gian Marco Gherardi的解决方案,但是在记录消息之前,GlobalContext没有设置线程上下文immediatley:

 ThreadContext.Properties["user"] = new HttpContextUserNameProvider();