我目前将所有log4net事件写入数据库,它似乎工作得很好。要捕获登录的用户帐户,我使用以下代码:
HttpContext context = HttpContext.Current;
if (context != null && context.User != null && context.User.Identity.IsAuthenticated)
{
MDC.Set("user", HttpContext.Current.User.Identity.Name);
}
代码似乎没问题,除了没有与之关联的用户上下文的事件(即我们公共网页上的用户)。在这种情况下,log4net捕获似乎有时写入最后登录的用户帐户(坏),有时写入空(好)。任何人都有这个功能在所有情况下都可靠地工作?我相信我看到一条说明MDC不再是推荐使用的功能,但我无法找到任何推荐的替代品。
注意:我发现MDC设置了帐户名称很奇怪,但如果没有用户处于活动状态,则永远不会清除。这可能是问题的一部分。但是,我没有找到任何也清除用户名的MDC代码提取。
答案 0 :(得分: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);
}
经常,您可以通过为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信息:
答案 1 :(得分:20)
根据Log4Net official API docs,不推荐使用MDC:
MDC已弃用,已被“属性”取代。当前的MDC实现转发到ThreadContext.Properties。
除MDC.Set
之外只接受字符串作为值,因此@wageoghe的最后一个解决方案无效(使用HttpContextUserNameProvider的解决方案)
我的解决方案是将HttpContextUserNameProvider与log4net.GlobalContext
一起使用,也在official API docs中建议:
在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配置,例如:
<layout type="log4net.Layout.PatternLayout" value="%property{user}"/>
答案 2 :(得分:10)
从Log4Net版本1.2.11开始,您现在可以使用appender模式通过ASP .NET请求获取授权用户,例如。
%aspnet-request{AUTH_USER}
答案 3 :(得分:2)
这是纯粹的推测,但这听起来非常像它可能是与共享请求线程相关的问题,即ThreadPool线程。设置MDC值时,它与当前线程关联,并且该线程将在请求结束时返回到ThreadPool,然后重新用于后续请求。如果未覆盖该值,则可以在新请求中看到旧值。
考虑在请求开始和结束事件中管理此数据,您可以在请求开始时设置用户名,然后在请求结束时清除它。这应该为这些数据提供正确的生命周期,即请求的生命周期。
答案 4 :(得分:2)
有两种不同的方法可以执行您想要的%identity
和%username
。
可以在你的appender模式中使用它们。
我之前已经完成了,但结果出乎意料,直到我看到以下帖子为我清理了它。
查看此帖子:Log4Net can't find %username property when I name the file in my appender
答案 5 :(得分:-1)
我使用了bothoghe和Gian Marco Gherardi的解决方案,但是在记录消息之前,GlobalContext没有设置线程上下文immediatley:
ThreadContext.Properties["user"] = new HttpContextUserNameProvider();