我目前将所有 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 信息可用:
关于c# - 使用 log4net 捕获用户名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4813242/