C# 带有自定义参数的log4net数据库日志记录
我使用AdonNetAppender进行了数据库日志记录。我想做的是在每个日志语句中记录用户标识。但是,出于两个原因,我不想使用标准的log4net%标识参数:C# 带有自定义参数的log4net数据库日志记录,c#,database,log4net,C#,Database,Log4net,我使用AdonNetAppender进行了数据库日志记录。我想做的是在每个日志语句中记录用户标识。但是,出于两个原因,我不想使用标准的log4net%标识参数: log4net警告说它的速度非常慢,因为它必须查找上下文标识 在一些服务组件中,标准标识是服务帐户,但我们已经在变量中捕获了用户标识,我想使用它 我见过一些人使用log4net.ThreadContext添加额外属性的代码,但我知道这是“不安全”的,因为线程交错(这也是一种性能消耗) 我的方法是扩展AdonNetAppenderPara
public class UserAdoNetAppenderParameter : AdoNetAppenderParameter
{
public UserAdoNetAppenderParameter()
{
DbType = DbType.String;
PatternLayout layout = new PatternLayout();
Layout2RawLayoutAdapter converter = new Layout2RawLayoutAdapter(layout);
Layout = converter;
ParameterName = "@username";
Size = 255;
}
public override void Prepare(IDbCommand command)
{
command.Parameters.Add(this);
}
public override void FormatValue(IDbCommand command, LoggingEvent loggingEvent)
{
string[] data = loggingEvent.RenderedMessage.Split('~');
string username = data[0];
command.Parameters["@username"] = username;
}
}
然后以编程方式将其添加到当前appender,如下所示:
ILog myLog = LogManager.GetLogger("ConnectionService");
IAppender[] appenders = myLog.Logger.Repository.GetAppenders();
AdoNetAppender appender = (AdoNetAppender)appenders[0];
appender.AddParameter(new UserAdoNetAppenderParameter());
myLog.InfoFormat("{0}~{1}~{2}~{3}", userName, "ClassName", "Class Method", "Message");
这里的目的是为消息使用标准格式,并解析字符串的第一部分,该部分应始终为用户名。然后,自定义appender参数的FormatValue()方法应仅使用字符串的该部分,以便将其写入日志数据库中的单独字段
我的问题是没有日志语句写入数据库。奇怪的是,在调试时,FormatValue()方法中的断点只有在我停止服务时才会被命中
我浏览了大量与此相关的资料,但尚未找到任何答案。
有没有人能做到这一点,或者我走错了路。
另外,我也尝试过扩展AdonNetAppender,但它不允许您设置参数值。
是的,线程敏捷性意味着您可能无法获取正确的数据。对于log4net,您将希望将其粘贴到您的应用程序中
问题是,当需要将这些值写入数据库时,您必须做一些工作才能将其恢复,因为我一直都在为自己做一些繁重的工作。使用它非常简单,因为您只需执行以下操作:log4net.ThreadContext.Properties["UserName"] = AdaptivePropertyProvider.Create("UserName", Thread.CurrentPrincipal.Identity.Name);
当log4net请求时,adaptive属性将知道检索该值的适当位置
备选方案
如果您不坚持使用log4net,则会使ASP.NET网站的日志记录更加简单,因为它们本机支持ASP.NET应用程序。使用和配置几乎与log4net相同 经过一些实验,我终于让它起作用了。确保log4net的内部日志记录有助于识别错误,下载log4net源代码并查看AdoNetAppenderParameter类说明了应该如何使用FormatValue()方法。下面是修改后的自定义appender参数:
public class UserAdoNetAppenderParameter : AdoNetAppenderParameter
{
public override void FormatValue(IDbCommand command, LoggingEvent loggingEvent)
{
string[] data = loggingEvent.RenderedMessage.Split('~');
string username = string.Empty;
if (data != null && data.Length >= 1)
username = data[0];
// Lookup the parameter
IDbDataParameter param = (IDbDataParameter)command.Parameters[ParameterName];
// Format the value
object formattedValue = username;
// If the value is null then convert to a DBNull
if (formattedValue == null)
{
formattedValue = DBNull.Value;
}
param.Value = formattedValue;
}
}
为了使用它,我将其添加到log4net配置文件中,如下所示:
<parameter type="MyAssembly.Logging.UserAdoNetAppenderParameter, MyAssembly">
<parameterName value="@username" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout" value="%message" />
</parameter>
if (log.IsDebugEnabled)
log.DebugFormat("{0}~{1}~{2}", username, someOtherParameter, message);
log.Debug( new {
SomeProperty: "some value",
OtherProperty: 123
})
如果您查看该类,它使用数据[0]作为用户名,因此它依赖于以下约定。但是,它会将用户名放入自己的参数和日志数据库表中的一个单独字段中,而不会临时将其填充到不安全的ThreadContext中。我还需要记录结构化数据,喜欢使用这样的日志界面:
<parameter type="MyAssembly.Logging.UserAdoNetAppenderParameter, MyAssembly">
<parameterName value="@username" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout" value="%message" />
</parameter>
if (log.IsDebugEnabled)
log.DebugFormat("{0}~{1}~{2}", username, someOtherParameter, message);
log.Debug( new {
SomeProperty: "some value",
OtherProperty: 123
})
因此,我还编写了自定义AdonNetAppenderParameter类来完成这项工作:
public class CustomAdoNetAppenderParameter : AdoNetAppenderParameter
{
public override void FormatValue(IDbCommand command, LoggingEvent loggingEvent)
{
// Try to get property value
object propertyValue = null;
var propertyName = ParameterName.Replace("@", "");
var messageObject = loggingEvent.MessageObject;
if (messageObject != null)
{
var property = messageObject.GetType().GetProperty(propertyName);
if (property != null)
{
propertyValue = property.GetValue(messageObject, null);
}
}
// Insert property value (or db null) into parameter
var dataParameter = (IDbDataParameter)command.Parameters[ParameterName];
dataParameter.Value = propertyValue ?? DBNull.Value;
}
}
现在,log4net配置可用于记录给定对象的任何属性:
<?xml version="1.0" encoding="utf-8"?>
<log4net>
<appender name="MyAdoNetAppender" type="log4net.Appender.AdoNetAppender">
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<connectionString value="... your connection string ..." />
<commandText value="INSERT INTO mylog ([level],[someProperty]) VALUES (@log_level,@SomeProperty)" />
<parameter>
<parameterName value="@log_level" />
<dbType value="String" />
<size value="50" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%level" />
</layout>
</parameter>
<parameter type="yourNamespace.CustomAdoNetAppenderParameter, yourAssemblyName">
<parameterName value="@SomeProperty" />
<dbType value="String" />
<size value="255" />
</parameter>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="MyAdoNetAppender" />
</root>
</log4net>
我被log4net卡住了,解决方案中的Windows服务组件无法访问HttpContext,尽管我们将用户名作为字符串传递。我真的不想设置ThreadContext属性,每次需要记录一些东西时都记录并取消设置它。我希望扩展的AdoNetAppenderParameter能够处理它。谢谢你的建议。