我使用AdoNetAppender进行数据库日志记录.我想要做的是在每个日志语句上记录用户身份.但是,我不想使用标准的log4net%identity参数有两个原因:
> log4net警告它非常慢,因为它必须查找上下文标识.
>在某些服务组件中,标准标识是服务帐户,但我们已经在变量中捕获了用户标识,我想使用它.
我见过一些代码,其中一些人使用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;
- }
- }
然后以编程方式将其添加到当前的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()方法中的断点.
我已经搜索了大量与此有关的东西,但还没有找到任何答案.
有没有人设法做到这一点,或者我是在错误的轨道上.
附:我也试过扩展AdoNetAppender,但它不能让你访问设置参数值.
解决方法
我还需要记录结构化数据,并喜欢使用这样的日志记录界面:
- log.Debug( new {
- SomeProperty: "some value",OtherProperty: 123
- })
所以我也编写了自定义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;
- }
- }
现在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>