Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 带有自定义参数的log4net数据库日志记录_C#_Database_Log4net - Fatal编程技术网

C# 带有自定义参数的log4net数据库日志记录

C# 带有自定义参数的log4net数据库日志记录,c#,database,log4net,C#,Database,Log4net,我使用AdonNetAppender进行了数据库日志记录。我想做的是在每个日志语句中记录用户标识。但是,出于两个原因,我不想使用标准的log4net%标识参数: log4net警告说它的速度非常慢,因为它必须查找上下文标识 在一些服务组件中,标准标识是服务帐户,但我们已经在变量中捕获了用户标识,我想使用它 我见过一些人使用log4net.ThreadContext添加额外属性的代码,但我知道这是“不安全”的,因为线程交错(这也是一种性能消耗) 我的方法是扩展AdonNetAppenderPara

我使用AdonNetAppender进行了数据库日志记录。我想做的是在每个日志语句中记录用户标识。但是,出于两个原因,我不想使用标准的log4net%标识参数:

  • log4net警告说它的速度非常慢,因为它必须查找上下文标识
  • 在一些服务组件中,标准标识是服务帐户,但我们已经在变量中捕获了用户标识,我想使用它
  • 我见过一些人使用log4net.ThreadContext添加额外属性的代码,但我知道这是“不安全”的,因为线程交错(这也是一种性能消耗)

    我的方法是扩展AdonNetAppenderParameter类,因此:

    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能够处理它。谢谢你的建议。