8

AdoNetAppender を使用してデータベースのログを記録しています。私がやりたいのは、各ログ ステートメントにユーザー ID を記録することです。ただし、次の 2 つの理由から、標準の log4net %identity パラメーターを使用したくありません。

  1. log4net は、コンテキスト ID を検索する必要があるため、非常に遅いと警告しています。
  2. 一部のサービス コンポーネントでは、標準の ID はサービス アカウントですが、ユーザー ID は既に変数に取り込まれており、それを使用したいと考えています。

一部の人々が 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;
    }

}

次に、プログラムでこれを現在のアペンダーに次のように追加します。

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");

ここでの意図は、メッセージに標準形式を使用し、常にユーザー名である必要がある文字列の最初の部分を解析することです。カスタム アペンダー パラメーターの FormatValue() メソッドは、ログ データベースの別のフィールドに書き込むことができるように、文字列のその部分のみを使用する必要があります。

私の問題は、ログ ステートメントがデータベースに書き込まれないことです。奇妙なことに、デバッグ時に FormatValue() メソッドのブレークポイントがヒットするのは、サービスを停止したときだけです。

私はこれに関連する多くのものを調べましたが、まだ答えが見つかりません. 誰かがこれをやったことがありますか、それとも私は間違った道を進んでいますか? PS AdoNetAppender の拡張も試みましたが、パラメーター値を設定するためのアクセス権はありません。

4

3 に答える 3

5

また、構造化データをログに記録する必要があり、次のようなログ インターフェイスを使用するのが好きでした。

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>
于 2014-11-25T10:25:59.830 に答える
4

いくつかの実験の後、私はついにこれを機能させることができました。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;
    }

}

これを使用するには、次のように 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 に一時的に詰め込むことなく、ユーザー名を独自のパラメーターとログ データベース テーブルの別のフィールドに取得します。

于 2012-07-17T20:09:09.983 に答える
2

はい、スレッドの機敏性とは、正しいデータが返されない可能性があることを意味します。log4net の場合、HttpContext の Items コレクションに貼り付けます。

問題は、これらの値をデータベースに書き込むときに、それを元に戻すためにいくつかの作業を行う必要があることです。これは、私が常にマレクの適応型プロパティ プロバイダー クラスを使用して単調な作業を行ってきたためです。使い方は非常に簡単で、次のことを行うだけです。

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

適応プロパティは、log4net が要求したときに値を取得する適切な場所を認識します。

代替オプション

log4net に行き詰まっていない場合、NLogは ASP.NET アプリケーションをネイティブにサポートするため、ASP.NET Web サイトのログ記録をより簡単にします。使い方も設定もlog4netとほぼ同じ!

于 2012-07-13T15:55:07.967 に答える