アプリケーションでnloggerを使用したいのですが、将来的には、ログシステムを変更する必要があります。だから私はロギングファサードを使いたいです。
既存の例の推奨事項を、それらの記述方法を知っていますか?または、この分野のベストプラクティスへのリンクを教えてください。
アプリケーションでnloggerを使用したいのですが、将来的には、ログシステムを変更する必要があります。だから私はロギングファサードを使いたいです。
既存の例の推奨事項を、それらの記述方法を知っていますか?または、この分野のベストプラクティスへのリンクを教えてください。
回答:
Common.Loggingなどのロギングファサードを使用していました(自分のCuttingEdge.Loggingライブラリを非表示にする場合でも)が、現在は依存関係注入パターンを使用しているため、両方の依存関係に準拠する独自の(単純な)抽象化の背後にロガーを非表示にできます逆転の原理とインターフェース分離の原理(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.
}
インターフェイスには単一のメソッドしか含まれていないため、log4net、Serilog、Microsoft.Extensions.Logging、NLog、またはその他のログライブラリのプロキシとなるILogger
実装を簡単に作成し、DIコンテナーを構成して、コンストラクタ。ILogger
単一のメソッドを持つインターフェースの上に静的な拡張メソッドを持つことは、多くのメンバーを持つインターフェースを持つこととはかなり異なることに注意してください。拡張メソッドは、LogEntry
メッセージを作成し、それをILogger
インターフェース上の唯一のメソッドに渡すヘルパーメソッドにすぎません。拡張メソッドは、消費者のコードの一部になります。抽象化の一部ではありません。これにより、抽象化、拡張メソッド、およびLogEntry
ロガーがスタブ化またはモック化されている場合でも、ロガー抽象化が使用されると、コンストラクターは常に実行されます。これにより、テストスイートで実行した場合のロガーへの呼び出しの正確さがより確実になります。1メンバーのインターフェースにより、テストもはるかに簡単になります。多くのメンバーを持つ抽象化があると、実装(モック、アダプター、デコレーターなど)の作成が難しくなります。
これを行う場合、ロギングファサード(またはその他のライブラリ)が提供する静的な抽象化はほとんど必要ありません。
私はhttps://github.com/uhaciogullari/NLog.Interfaceの小さなインターフェイスラッパー+アダプターを使用しました。
PM> Install-Package NLog.Interface
現在、最善の策は、Microsoft.Extensions.Loggingパッケージを使用することです(Julianによって指摘されています)。ほとんどのロギングフレームワークはこれで使用できます。
Stevenの回答で説明されているように、独自のインターフェースを定義することは、単純なケースでは問題ありませんが、私が重要だと考えるいくつかのことを欠いています。
IsEnabled(LogLevel)
パフォーマンス上の理由から、希望する条件付きチェックをもう一度おそらく、これらすべてを独自の抽象化で実装できますが、その時点で車輪を再発明することになります。
一般的に私は次のようなインターフェースを作成することを好みます
public interface ILogger
{
void LogInformation(string msg);
void LogError(string error);
}
ランタイムでは、このインターフェイスから実装される具象クラスを注入します。
LogWarning
およびLogCritical
メソッドとそのすべてのオーバーロードを忘れないでください。これを行うと、インターフェース分離の原則に違反します。ILogger
単一のLog
メソッドでインターフェースを定義することをお勧めします。
LogEntry
、つまりへの依存を導入していLoggingEventType
ます。ILogger
実装は、これらに対処しなければならないLoggingEventTypes
だろうが、case/switch
である、コードのにおい。なぜLoggingEventTypes
依存関係を隠すのですか?実装はとにかくログレベルを処理する必要があるため、一般的な引数を持つ単一のメソッドの後ろにそれを隠すよりも、実装が何をすべきかについて明示する方が良いでしょう。
ICommand
がHandle
をとるを想像してくださいobject
。実装はcase/switch
、インターフェースのコントラクトを満たすために、可能なタイプを超える必要があります。これは理想的ではありません。とにかく処理する必要のある依存関係を隠す抽象化はありません。代わりに、何が期待されているかを明確に示すインターフェイスを用意します。「すべてのロガーが警告、エラー、致命的エラーなどを処理することを期待しています」。これは「すべてのロガーが警告、エラー、致命的エラーなどを含むメッセージを処理することを期待しています」よりも望ましい方法です。
LoggingEventType
必要があるため、これらを呼び出す必要がありますLoggingEventLevel
。私にとって、インターフェースメソッドを使用しないことと、対応するenum
値を使用しないことの間に違いはありません。代わりErrorLoggger : ILogger
にInformationLogger : ILogger
、すべてのロガーが独自のレベルを定義するを使用します。次に、DIはおそらくキー(列挙型)を介して必要なロガーを挿入する必要がありますが、このキーはインターフェースの一部ではなくなりました。(あなたは今、固いです)。
この問題に対する優れた解決策は、LibLogプロジェクトの形で現れました。
LibLogは、Serilog、NLog、Log4net、Enterpriseロガーなどの主要なロガーを組み込みでサポートするロギング抽象化です。NuGetパッケージマネージャーを介して、.dll参照ではなくソース(.cs)ファイルとしてターゲットライブラリにインストールされます。このアプローチにより、ライブラリーに外部依存関係を強制することなく、ロギングの抽象化を組み込むことができます。また、ライブラリの作成者が、使用するアプリケーションにライブラリにロガーを明示的に提供させることなく、ロギングを含めることができます。LibLogはリフレクションを使用して、どの具体的なロガーが使用されているかを把握し、ライブラリプロジェクトの明示的な配線コードなしでそれに接続します。
したがって、LibLogは、ライブラリプロジェクト内のロギングに最適なソリューションです。メインのアプリケーションまたはサービスで具象ロガー(勝利のためのSerilog)を参照して構成し、LibLogをライブラリに追加するだけです!
独自のファサードを作成する代わりに、Castle Logging ServicesまたはSimple LoggingFaçadeを使用できます。
どちらにもNLogとLog4netのアダプターが含まれています。
2015年以降、.NETコアアプリケーションを構築する場合は、.NETコアロギングを使用することもできます。
NLogがフックするパッケージは次のとおりです。