[解決済み] ロガーラッパーのベストプラクティス
質問
アプリケーションでnloggerを使用したいのですが、将来的にロギングシステムを変更する必要があるかもしれません。 そこで、私はロギングファサードを使用したいと思います。
それらをどのように書くか、既存の例のための任意の推奨事項を知っていますか? または、この分野におけるいくつかのベストプラクティスへのリンクを教えてください。
どのように解決するのですか?
私は以前、以下のようなロギングファサードを使っていました。 共通.ロギング を使用していました (私自身の CuttingEdge.Logging(カッティングエッジ.ロギング ライブラリを隠すこともできます)、しかし最近は Dependency Injectionパターン . これにより、ロガーをアプリケーション定義の抽象化の背後に隠すことができます。 依存性逆転の原則 と インターフェース分離の原則 (ISP)の原則に従ったものです。
アプリケーションの中核部分が外部ライブラリの存在について持つ知識を最小化することは、たとえロギングライブラリを決して置き換えるつもりがないとしても、より良いことです。外部ライブラリへのハード依存は、コードのテストをより困難にし、アプリケーションのために特別に設計されていない API でアプリケーションを複雑にしてしまいます。
私のアプリケーションでは、抽象化はしばしばこのように見えます。
public interface ILogger
{
void Log(LogEntry entry);
}
public sealed class ConsoleLogger : ILogger
{
public void Log(LogEntry entry)
}
public enum LoggingEventType { Debug, Information, Warning, Error, Fatal };
// Immutable DTO that contains the log information.
public struct LogEntry
{
public LoggingEventType Severity { get; }
public string Message { get; }
public Exception Exception { get; }
public LogEntry(LoggingEventType severity, string msg, Exception ex = null)
{
if (msg is null) throw new ArgumentNullException("msg");
if (msg == string.Empty) throw new ArgumentException("empty", "msg");
this.Severity = severity;
this.Message = msg;
this.Exception = ex;
}
}
オプションとして、この抽象化はいくつかの簡単な拡張メソッドで拡張することができます(インターフェイスが狭いままで、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 ex) =>
logger.Log(new LogEntry(LoggingEventType.Error, ex.Message, ex));
// More methods here.
}
このインターフェイスにはメソッドが1つしかないので、簡単に
ILogger
という実装を簡単に作ることができます。
log4net へのプロキシ
,
からSerilogへ
,
Microsoft.Extensions.Logging
NLog などのロギングライブラリを使用し、DIコンテナ内で
ILogger
を持つクラスに注入するように DI コンテナを設定します。また、以下のリストのように、コンソールに書き込む実装や、ユニットテストに使用できる偽の実装を簡単に作成することができます。
public class ConsoleLogger : ILogger
{
public void Log(LogEntry entry) => Console.WriteLine(
$"[{entry.Severity}] {DateTime.Now} {entry.Message} {entry.Exception}");
}
public class FakeLogger : List<LogEntry>, ILogger
{
public void Log(LogEntry entry) => this.Add(entry);
}
単一のメソッドを持つインターフェースの上に静的な拡張メソッドを持つことは、多くのメンバーを持つインターフェースとは全く異なります。拡張メソッドは単なるヘルパーメソッドであり、このメソッドによって
LogEntry
メッセージの唯一のメソッドである
ILogger
インターフェース上の唯一のメソッドに渡します。これらの拡張メソッド自体はそれ自身のVolatile Behaviorを含まないので、テスト容易性を妨げることはありません。望むなら簡単にテストできますし、抽象化の一部ではなく、消費者のコードの一部となります。
これによって、抽象化を変更することなく拡張メソッドを進化させることができるだけでなく、拡張メソッドと
LogEntry
コンストラクタは、ロガーがスタブ/モック化されている場合でも、ロガー抽象化が使用されるときに常に実行されます。これにより、テストスイートで実行するときに、ロガーへの呼び出しの正しさについてより確実なものになります。使用されるサードパーティのロガー抽象化への私の呼び出しがユニットテストの間に成功したが、実稼働で実行されたときにまだ失敗したとき、私はこれで何度も自分自身を撃ってきました。
多くのメンバーを持つ抽象化は、(モック、アダプター、およびデコレーターのような)実装を作成することを難しくします。
このようにすると、ロギングファサード(または他のライブラリ)が提供するかもしれない静的な抽象化の必要性はほとんどありません。
それでも、この
ILogger
への依存を必要とするクラスが少なくなるようにアプリケーションを設計することをお勧めします。
ILogger
への依存を必要とするクラスが少なくなるようなアプリケーションを設計することが望ましいです。
この回答
はこのことについて詳しく述べています。
関連
-
[解決済み] メンバー '<メンバー名>' にインスタンス参照でアクセスできない
-
[解決済み】「namespace x already contains a definition for x」エラーの修正方法は?VS2010にコンバートした後に発生しました。
-
[解決済み] 2つのリストを結合する
-
[解決済み】画像のペイントにTextureBrushを使用する方法
-
[解決済み] C#のオートプロパティに初期値を与える最良の方法は何ですか?
-
[解決済み] なぜList<T>を継承しないのですか?
-
[解決済み] C#で文字を繰り返し表示する最適な方法
-
[解決済み] すべてのサーバーサイドのコードでConfigureAwaitを呼び出すためのベストプラクティス
-
[解決済み】非同期処理の待ち時間、Wait()でプログラムがフリーズする原因はここにある
-
[解決済み】例外処理にtry catchを使うのはベストプラクティスなのか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】エラー。「戻り値を変更できません」 C#
-
[解決済み】指定されたキャストが有効でない?
-
[解決済み】コンパイルエラー「未割り当てのローカル変数を使用しています」が発生したのはなぜですか?
-
[解決済み】C#で四捨五入する方法
-
[解決済み】C#におけるtypedefの等価性
-
[解決済み】Visual Studio: 操作を完了できませんでした。パラメータが正しくありません
-
[解決済み】値をNULLにすることはできません。パラメータ名:source
-
[解決済み】パラメータ付きRedirectToAction
-
[解決済み】IntPtrとは一体何なのか?
-
[解決済み】別のスレッドがこのオブジェクトを所有しているため、呼び出し側のスレッドはこのオブジェクトにアクセスできない