簡體   English   中英

使用自定義參數記錄log4net數據庫

[英]log4net database logging with custom parameters

我使用AdoNetAppender進行數據庫日志記錄。 我想要做的是在每個日志語句上記錄用戶身份。 但是,我不想使用標准的log4net%identity參數有兩個原因:

  1. log4net警告它非常慢,因為它必須查找上下文標識。
  2. 在某些服務組件中,標准標識是服務帳戶,但我們已經在變量中捕獲了用戶標識,我想使用它。

我見過一些代碼,其中一些人使用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()方法中的斷點。

我已經搜索了大量與此有關的東西,但還沒有找到任何答案。 有沒有人設法做到這一點,或者我是在錯誤的軌道上。 PS我也試過擴展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>

經過一些實驗,我終於得到了這個。 確保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);

如果查看該類,它將使用data [0]作為用戶名,因此它依賴於遵循約定。 但是,它確實將用戶名放入其自己的參數並進入日志數據庫表中的單獨字段,而無需將其臨時填充到不安全的ThreadContext中。

是的,線程敏捷性意味着您可能無法獲得正確的數據。 對於log4net,您需要將其粘貼在HttpContext的Items集合中

麻煩的是你需要做一些工作,以便在將這些值寫入數據庫時​​將其恢復原狀,因為我總是使用Marek的Adaptive Property Provider類來為我做咕嚕咕嚕的工作。 使用它非常簡單,因為您需要做的就是以下內容:

log4net.ThreadContext.Properties["UserName"] = AdaptivePropertyProvider.Create("UserName", Thread.CurrentPrincipal.Identity.Name);

當log4net請求時,adaptive屬性將知道檢索值的適當位置。

替代選擇

如果您沒有堅持使用log4net, NLog會使ASP.NET網站的日志記錄變得更加簡單,因為它們原生支持ASP.NET應用程序。 用法甚至配置幾乎與log4net相同!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM