web-dev-qa-db-ja.com

ロガーラッパーのベストプラクティス

アプリケーションでnloggerを使用したいのですが、将来的にはロギングシステムを変更する必要があります。それで、私はロギングファサードを使いたいです。

既存の例の推奨事項は、それらの書き方を知っていますか?または、この分野のベストプラクティスへのリンクを教えてください。

86
Night Walker

以前は、 Common.Logging などのロギングファサードを使用していました(自分の CuttingEdge.Logging ライブラリを非表示にすることもできました) 、しかし今では Dependency Injectionパターン を使用しており、これにより、ロガーを の両方に従う独自の(単純な)抽象化の背後に隠すことができます。依存関係反転の原理 および Interface Segregation Principle (ISP)には1つのメンバーがあり、インターフェースはアプリケーションによって定義されているため。外部ライブラリではありません。アプリケーションのコア部分が外部ライブラリの存在について持っている知識を最小限に抑えることが望ましい。ロギングライブラリを置き換えるつもりがない場合でも。外部ライブラリに強く依存しているため、コードのテストが難しくなり、アプリケーション専用に設計されていないAPIでアプリケーションが複雑になります。

私のアプリケーションでは、これが抽象化のように見えることがよくあります。

public interface ILogger
{
    void Log(LogEntry entry);
}

public enum LoggingEventType { Debug, Information, Warning, Error, Fatal };

// Immutable DTO that contains the log information.
public class LogEntry 
{
    public readonly LoggingEventType Severity;
    public readonly string Message;
    public readonly Exception Exception;

    public LogEntry(LoggingEventType severity, string message, Exception exception = null)
    {
        if (message == null) throw new ArgumentNullException("message");
        if (message == string.Empty) throw new ArgumentException("empty", "message");

        this.Severity = severity;
        this.Message = message;
        this.Exception = exception;
    }
}

必要に応じて、この抽象化をいくつかの単純な拡張メソッドで拡張できます(インターフェイスを狭く保ち、ISPに準拠し続けることができます)。これにより、このインターフェイスのコンシューマー向けのコードが非常に簡単になります。

public static class LoggerExtensions
{
    public static void Log(this ILogger logger, string message) {
        logger.Log(new LogEntry(LoggingEventType.Information, message));
    }

    public static void Log(this ILogger logger, Exception exception) {
        logger.Log(new LogEntry(LoggingEventType.Error, exception.Message, exception));
    }

    // More methods here.
}

インターフェイスには1つのメソッドしか含まれていないため、 log4net へのプロキシ、 toというILogger実装を簡単に作成できます。 SerilogMicrosoft.Extensions.Logging 、NLogまたはその他のロギングライブラリ、およびILoggerコンストラクター。

単一のメソッドを持つインターフェイスの上に静的な拡張メソッドを持つことは、多くのメンバーを持つインターフェイスを持つこととはまったく異なることに注意してください。拡張メソッドは、LogEntryメッセージを作成し、ILoggerインターフェイスの唯一のメソッドに渡すヘルパーメソッドです。拡張メソッドは、消費者のコードの一部になります。抽象化の一部ではありません。これにより、抽象メソッドを変更することなく拡張メソッドを進化させることができるだけでなく、拡張メソッドとLogEntryコンストラクターは、ロガーの抽象化が使用される場合、ロガーがスタブ/モックされている場合でも常に実行されます。これにより、テストスイートで実行する場合のロガーの呼び出しの正確性についてより確実になります。 1メンバーのインターフェイスにより、テストもはるかに簡単になります。多くのメンバーで抽象化すると、実装(モック、アダプター、デコレーターなど)を作成するのが難しくなります。

これを行う場合、ロギングファサード(またはその他のライブラリ)が提供する静的な抽象化の必要性はほとんどありません。

194
Steven

https://github.com/uhaciogullari/NLog.Interface の小さなインターフェイスラッパー+アダプターを使用しました。これは NuGetで利用可能

PM> Install-Package NLog.Interface 
9
Jon Adams

一般的に私は次のようなインターフェースを作成することを好みます

public interface ILogger
{
 void LogInformation(string msg);
 void LogError(string error);
}

ランタイムに、このインターフェイスから実装される具体的なクラスを注入します。

4
crypted

LibLog プロジェクトの形式で、この問題の優れた解決策が明らかになりました。

LibLogは、Serilog、NLog、Log4net、Enterpriseロガーなどの主要なロガーをサポートする組み込みのロギング抽象化です。 NuGetパッケージマネージャーを介して、.dll参照の代わりにソース(.cs)ファイルとしてターゲットライブラリにインストールされます。このアプローチにより、ライブラリに外部依存関係を強制することなく、ロギングの抽象化を含めることができます。また、ライブラリの作成者は、消費側アプリケーションにロガーを明示的にライブラリに提供させることなく、ロギングを含めることができます。 LibLogはリフレクションを使用して、どの具体的なロガーが使用されているかを把握し、ライブラリプロジェクトで明示的な配線コードを使用せずに接続します。

そのため、LibLogは、ライブラリプロジェクト内のログを記録するための優れたソリューションです。メインアプリケーションまたはサービスで具体的なロガー(勝利のためのSerilog)を参照して構成し、LibLogをライブラリに追加するだけです!

4
Rob Davis

独自のファサードを記述する代わりに、 Castle Logging Services または Simple LoggingFaçade を使用できます。

両方とも、NLogおよびLog4net用のアダプターが含まれています。

2
Jonas Kongslund

今のところ、最善の策は Microsoft.Extensions.Logging パッケージを使用することです( Julianが指摘したように )。ほとんどのロギングフレームワークはこれで使用できます。

Steven's answer で説明されているように、独自のインターフェースを定義することは簡単な場合には問題ありませんが、重要だと思ういくつかのことを見逃しています:

  • 構造化されたロギングおよび非構造化オブジェクト(SerilogおよびNLogの@表記)
  • 遅延文字列の構築/フォーマット:文字列を取得するため、しきい値を下回ったためにイベントがログに記録されない場合でも、呼び出されたときにすべてを評価/フォーマットする必要があります(パフォーマンスコスト、前のポイントを参照)
  • パフォーマンス上の理由で、必要なIsEnabled(LogLevel)のような条件付きチェックをもう一度

おそらくこれらすべてを独自の抽象化で実装できますが、その時点で車輪を再発明することになります。

2
Philippe

2015年以降、.NETコアアプリケーションを構築している場合は、 。NET Core Logging も使用できます。

NLogがフックするパッケージは次のとおりです。

1
Julian