我使用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>