Joh*_*ith 8 c# database log4net
我使用AdoNetAppender进行数据库日志记录.我想要做的是在每个日志语句上记录用户身份.但是,我不想使用标准的log4net%identity参数有两个原因:
我见过一些代码,其中一些人使用log4net.ThreadContext来添加其他属性,但我知道由于线程交错这是"不安全"(并且它也是性能消耗).
我的方法是扩展AdoNetAppenderParameter类:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
然后以编程方式将其添加到当前的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");
Run Code Online (Sandbox Code Playgroud)
这里的目的是使用消息的标准格式并解析字符串的第一部分,该部分应该始终是用户名.然后,自定义appender参数的FormatValue()方法应仅使用该字符串的那一部分,以便可以将其写入日志数据库中的单独字段.
我的问题是没有日志语句写入数据库.奇怪的是,在调试时,只有在我停止服务时才会触发FormatValue()方法中的断点.
我已经搜索了大量与此有关的东西,但还没有找到任何答案.有没有人设法做到这一点,或者我是在错误的轨道上.PS我也试过扩展AdoNetAppender,但它不能让你访问设置参数值.
我还需要记录结构化数据,并喜欢使用这样的日志记录界面:
log.Debug( new {
SomeProperty: "some value",
OtherProperty: 123
})
Run Code Online (Sandbox Code Playgroud)
所以我也编写了自定义AdoNetAppenderParameter类来完成这项工作:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
现在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>
Run Code Online (Sandbox Code Playgroud)
经过一些实验,我终于让它发挥作用了。确保 log4net 的内部日志记录有助于识别错误,下载 log4net 源代码并查看 AdoNetAppenderParameter 类,了解应如何使用 FormatValue() 方法。因此,这是修改后的自定义附加器参数:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
要使用它,我将其添加到 log4net 配置文件中,如下所示:
<parameter type="MyAssembly.Logging.UserAdoNetAppenderParameter, MyAssembly">
<parameterName value="@username" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout" value="%message" />
</parameter>
Run Code Online (Sandbox Code Playgroud)
按照惯例,我的日志语句将如下所示:
if (log.IsDebugEnabled)
log.DebugFormat("{0}~{1}~{2}", username, someOtherParameter, message);
Run Code Online (Sandbox Code Playgroud)
如果您查看该类,它使用 data[0] 作为用户名,因此它依赖于遵循约定。但是,它确实将用户名放入其自己的参数中,并放入日志数据库表中的单独字段中,而无需将其临时填充到不安全的 ThreadContext 中。