ロガーをシングルトンにするのは良い習慣ですか?


81

次のように、ロガーをコンストラクターに渡す習慣がありました。

public class OrderService : IOrderService {
     public OrderService(ILogger logger) {
     }
}

しかし、それは非常に面倒なので、私はしばらくの間、これをプロパティとして使用しました。

private ILogger logger = NullLogger.Instance;
public ILogger Logger
{
    get { return logger; }
    set { logger = value; }
}

これも面倒になります-乾いていないので、すべてのクラスでこれを繰り返す必要があります。基本クラスを使用することもできますが、Formクラスを使用しているので、FormBaseなどが必要になります。したがって、ILoggerを公開したシングルトンを使用することのデメリットは何でしょうか。

    Infrastructure.Logger.Info("blabla");

更新:マーリンが正しく気づいたように、最初と2番目の例ではDIを使用していることを言及する必要があります。



1
私が見ている答えから、人々はあなたがそれらの両方の例で注入していることに気づいていなかったようです。質問でこれをより明確にできますか?その一部を処理するためにタグを追加しました。
Merlyn Morgan-Graham 2011

1
この記事のコメントを読んでください。依存性注入の神話:テストと依存性注入にかなり精通しているGoogleのMiškoHeveryによるリファレンスパス。ロガーの出力をテストする必要がない限り(この場合はDIを使用)、ロギングは一種の例外であり、シングルトンは問題ないと彼は言います。
ユーザー

回答:


35

これも迷惑になっています-それはDRYではありません

それは本当だ。しかし、あなたが持っているすべてのタイプに蔓延する横断的関心事に対してできることはたくさんあります。どこでもロガーを使用する必要があるため、これらのタイプのプロパティが必要です。

それで、私たちがそれについて何ができるか見てみましょう。

シングルトン

シングルトンはひどい<flame-suit-on>です。

2番目の例で行ったように、プロパティインジェクションを使用することをお勧めします。これは、魔法に頼らずにできる最高のファクタリングです。シングルトンを介して非表示にするよりも、明示的な依存関係を設定することをお勧めします。

しかし、シングルトンがあなたがしなければならないすべてのリファクタリング(水晶玉の時間!)を含めてあなたにかなりの時間を節約するなら、私はあなたがそれらと一緒に暮らすことができるかもしれないと思います。シングルトンの使用があったとしたら、これはそれかもしれません。あなたがあなた考えを変えたい思うならば、それが得るのと同じくらい高くなるであろうコストを覚えておいてください。

これを行う場合はRegistryパターン(説明を参照)を使用して他の人の回答を確認し、シングルトンロガーインスタンスではなく(リセット可能な)シングルトンファクトリを登録している人を確認してください。

それほど妥協することなく同様に機能する可能性のある他の選択肢があるので、最初にそれらをチェックする必要があります。

VisualStudioコードスニペット

Visual Studioコードスニペットを使用して、反復的なコードの入力を高速化できます。のようなものを入力できるようloggertabになり、コードが魔法のように表示されます。

AOPを使用してドライオフ

PostSharpのようなアスペクト指向プログラミング(AOP)フレームワークを使用してその一部を自動生成することにより、そのプロパティインジェクションコードの一部を削除できます。

完了すると、次のようになります。

[InjectedLogger]
public ILogger Logger { get; set; }

また、メソッドトレースサンプルコードを使用して、メソッドの開始コードと終了コードを自動的にトレースすることもできます。これにより、ロガープロパティの一部をまとめて追加する必要がなくなる可能性があります。クラスレベルまたは名前空間全体で属性を適用できます。

[Trace]
public class MyClass
{
    // ...
}

// or

#if DEBUG
[assembly: Trace( AttributeTargetTypes = "MyNamespace.*",
    AttributeTargetTypeAttributes = MulticastAttributes.Public,
    AttributeTargetMemberAttributes = MulticastAttributes.Public )]
#endif

3
シングルトンの+1はひどいです。複数のクラスローダーでシングルトンを使用してみてください。つまり、ディスパッチャーがクライアントであり、ダブルロギングの恐ろしい例を経験したアプリケーションサーバー環境です(ただし、一部のC ++プログラマーが教えてくれた間違った場所からのロギングステートメントほど恐ろしいものではありません)
Niklas R.

1
@NickRosencrantz +1(C ++ホラーストーリーの場合)。シングルトンの恐ろしさを考えながら、下にスクロールして「ハハ笑、少なくとも問題はない」と数分間考えるのが大好きです。
ドメニック2011

32
</flame-suit-on> あなたが5年間炎のスーツを着ていた方法はわかりませんが、これがお役に立てば幸いです。
Brennen Sprimont 2016年

39

依存性注入コンテナにロガーインスタンスを配置します。これにより、ロガーが必要なクラスに注入されます。


8
もっと具体的に教えていただけますか?問題の1つと2つのコードサンプルでそれが私がした/したことだと思うので?
Giedrius 2011

2
「using」クラスは、基本的にはすでに持っているものと同じように見えます。ただし、DIコンテナが自動的に行うため、実際の値を手動でクラスに指定する必要はありません。これにより、静的シングルトンロガーを使用する場合と比較して、テストでロガーを置き換えるのがはるかに簡単になります。
ダニエルローズ

2
これは正解(IMO)ですが、OPはすでにこれを行っていると言っているため、賛成しません。また、これを使用する理由とシングルトンを使用する理由は何ですか?OPが繰り返しコードで抱えている問題はどうですか?この回答によってどのように対処されますか?
マーリンモーガン-グラハム2011

1
@Merlyn OPは、コンストラクターの一部として、またはパブリックプロパティとしてロガーを持っていると述べました。彼は、DIコンテナが正しい値を注入しているとは述べていません。だから私はこれが当てはまらないと思います。はい、いくつかの繰り返しコードがあります(ただし、属性付きのautoプロパティを使用すると、たとえば、非常に少なくなります)が、(グローバル)シングルトンを介して非表示にするよりも、依存関係を明示的に表示する方がはるかに優れています。
ダニエルローズ

1
@DanielRose:そして、「(グローバル)シングルトンを介して依存関係を非表示にするよりも、依存関係を明示的に表示する方がはるかに優れている」という考えが変わりました。+1
マーリンモーガン-グラハム2011

25

良い質問。ほとんどのプロジェクトのロガーはシングルトンだと思います。

いくつかのアイデアが頭に浮かびます。

  • 使用のServiceLocator(または他の依存性注入すでにいずれかを使用している場合、コンテナ)シングルトンだろう明らかであるあなたがのServiceLocator経由でロガーや複数の異なるロガーとシェアをインスタンス化することができ、このように、サービス/クラス全体で共有ロガーにあなたを可能に、ある種の制御反転。このアプローチにより、ロガーのインスタンス化および初期化プロセスに大きな柔軟性がもたらされます。
  • ほとんどどこでもロガーが必要な場合-Objectタイプの拡張メソッドを実装して、各クラスが、、などのロガーのメソッドを呼び出せるようLogInfo()にしますLogDebug()LogError()

より具体的に、拡張メソッドについて何を考えていましたか?それが良いだろう理由のServiceLocatorを使用してについては、私は、試料2のように、プロパティ注射よりも、思ったんだけど
Giedrius

1
@Giedrius:拡張メソッドに関して-public static void LogInfo(this Object instance, string message)各クラスがそれを取得するように拡張メソッドを作成できます。についてServiceLocator-これにより、ロガーをシングルトンではなく通常のクラスインスタンスとして使用できるため、柔軟性が大幅に向上します
sll

@GiedriusコンストラクターインジェクションによってIoCを実装し、使用しているDIフレームワークにコンストラクターをスキャンして、依存関係を自動的にインジェクションする機能がある場合(DIフレームワークのブートストラッププロセスでこれらの依存関係を構成した場合)、以前の方法それはうまくいくでしょうか。また、ほとんどのDIフレームワークでは、各オブジェクトのライフサイクルスコープを設定できます。
GR7 2011

7
ServiceLocatorはDIコンテナではありません。そして、それはアンチパターンと見なされます。
Piotr Perak 2011

1
OPはすでにDIコンテナでDIを使用しています。最初の例はctorインジェクションで、2番目の例はプロパティインジェクションです。彼らの編集を参照してください。
Merlyn Morgan-Graham 2011

16

シングルトンは良い考えです。さらに良いアイデアは、レジストリパターンを使用することです。これにより、インスタンス化をもう少し制御できます。私の意見では、シングルトンパターンはグローバル変数に近すぎます。オブジェクトの作成または再利用を処理するレジストリを使用すると、インスタンス化ルールを将来変更する余地があります。

レジストリ自体を静的クラスにして、ログにアクセスするための簡単な構文を与えることができます。

Registry.Logger.Info("blabla");

6
初めてレジストリパターンに出くわしました。基本的にすべてのグローバル変数と関数が1つの傘の下にあると言うのは単純すぎますか?
Joel Goodwin 2011

最も単純な形式では単なる傘ですが、中央に配置すると、要件が変更されたときに別の場所(オブジェクトのプール、使用ごとのインスタンス、スレッドローカルインスタンスなど)に変更できます。
アンダースアベル

5
レジストリとServiceLocatorはほとんど同じです。ほとんどのIoCフレームワークは、その中核にレジストリの実装です。
マイケルブラウン

1
@MikeBrown:違いは、DIコンテナ(内部的にはサービスロケーターパターン)がインスタンス化されたクラスである傾向があるのに対し、レジストリパターンは静的クラスを使用しているようです。それは正しいですか?
Merlyn Morgan-Graham

1
@MichaelBrown違いは、レジストリ/サービスロケーターを使用する場所です。コンポジションルートにある場合は問題ありません。多くの場所で使用する場合、それは悪いことです。
Onur 2015年

10

単純なシングルトンは良い考えではありません。ロガーの交換が難しくなります。私はロガーにフィルターを使用する傾向があります(一部の「ノイズの多い」クラスは警告/エラーのみをログに記録する場合があります)。

ロガーファクトリのプロキシパターンと組み合わせたシングルトンパターンを使用します。

public class LogFactory
{
    private static LogFactory _instance;

    public static void Assign(LogFactory instance)
    {
        _instance = instance;
    }

    public static LogFactory Instance
    {
        get { _instance ?? (_instance = new LogFactory()); }
    }

    public virtual ILogger GetLogger<T>()
    {
        return new SystemDebugLogger();
    }
}

これにより、コードを変更せずに(したがって、オープン/クローズの原則に準拠して)、FilteringLogFactoryまたはを作成できますSimpleFileLogFactory

サンプル拡張

public class FilteredLogFactory : LogFactory
{
    public override ILogger GetLogger<T>()
    {
        if (typeof(ITextParser).IsAssignableFrom(typeof(T)))
            return new FilteredLogger(typeof(T));

        return new FileLogger(@"C:\Logs\MyApp.log");
    }
}

そして、新しい工場を使用するには

// and to use the new log factory (somewhere early in the application):
LogFactory.Assign(new FilteredLogFactory());

ログに記録する必要があるクラス:

public class MyUserService : IUserService
{
    ILogger _logger = LogFactory.Instance.GetLogger<MyUserService>();

    public void SomeMethod()
    {
        _logger.Debug("Welcome world!");
    }
}

私の以前のコメントを気にしないでください。私はあなたがここで何をしているのかわかると思います。これに実際にログを記録するクラスの例を提供できますか?
Merlyn Morgan-Graham

+1; 裸のシングルトンを確実に打ち負かします:)静的オブジェクトを使用しているため、まだ若干の結合と静的スコープ/スレッドの問題の可能性がありますが、それらはまれだと思います(Instanceアプリケーションルートに設定したいのですが、わかりませんリセットする理由)
Merlyn Morgan-Graham

1
@ MerlynMorgan-Graham:工場を設定した後に工場を変更する可能性はほとんどありません。変更は実装ファクトリで行われ、それを完全に制御できます。このパターンをシングルトンのユニバーサルソリューションとしてはお勧めしませんが、APIがほとんど変更されないため、工場ではうまく機能します。(
つまり

3

.NETには依存性注入という本があります。必要なものに基づいて、傍受を使用する必要があります。

この本には、コンストラクター注入、プロパティ注入、メソッド注入、アンビエントコンテキスト、インターセプトを使用するかどうかを決定するのに役立つ図があります。

これが、この図を使用する理由の1つです。

  1. 依存関係がありますか、それとも必要ですか?- それが必要
  2. 横断的関心事ですか?- はい
  3. あなたはそれからの答えが必要ですか?- 番号

インターセプトを使用する


最初の推論の質問に間違いがあると思います(「依存関係がありますか、それとも必要ですか?」とすべきだと思います)。とにかく、インターセプトについて言及し、ウィンザーでの可能性を研究するための+1であり、非常に興味深いように見えます。あなたがそれがここにどのように収まると想像するかについてさらに例をあげることができれば、それは素晴らしいことです。
giedrius 2011

+1; 興味深い提案-基本的に、タイプに触れることなくAOPのような機能を提供します。私の意見非常に限られた露出に基づいています)は、プロキシ生成(AOP属性ベースのライブラリとは対照的にDIライブラリが提供できるタイプ)を介した傍受は黒魔術のように感じられ、何であるかを理解するのがやや難しくなる可能性があるということです起こっています。これがどのように機能するかについての私の理解は正しくありませんか?誰かがそれで別の経験をしましたか?思ったより怖くないですか?
マーリンモーガン-グラハム2011

2
インターセプトが多くの「魔法」であると感じる場合は、デコレータデザインパターンを使用して自分で実装できます。しかしその後、あなたはおそらくそれが時間の無駄であることに気付くでしょう。
Piotr Perak 2011

この提案では、クラス自体をログに記録させる(作成する)のではなく、各呼び出しをログに自動的にラップすると想定しました。あれは正しいですか?
Merlyn Morgan-Graham 2011

はい。それがデコレータが行うことです。
Piotr Perak 2011

0

私が個人的に最も簡単だと思うもう1つの解決策は、静的Loggerクラスを使用することです。プロパティインジェクションの追加など、クラスを変更せずに、任意のクラスメソッドから呼び出すことができます。非常にシンプルで使いやすいです。

Logger::initialize ("filename.log", Logger::LEVEL_ERROR); // only need to be called once in your application

Logger::log ("my error message", Logger::LEVEL_ERROR); // to be used in every method where needed

-1

あなたがログインするための良い解決策を見てみたいなら、私はあなたがロギングが同じくらい簡単ようであるのpythonでのGoogleのApp Engineで見てくださいimport logging、その後、あなたができるばかりlogging.debug("my message")か、logging.info("my message")それが必要として本当に簡単として、それを維持しています。

Javaにはロギングに適したソリューションがありませんでした。つまり、log4jは、ここで回答されているように「ひどい」シングルトンを使用することを実際に強制するため、避ける必要があります。ダブルロギングの理由が、同じ仮想マシンの2つのクラスローダーに1つのシングルトンのロギングオブジェクトがあるためだと思われる場合(!)

C#にそれほど固有ではないことをお詫びしますが、C#を使用したソリューションは、log4jがあったJavaに似ているので、シングルトンにする必要があります。

だから私はGAE / pythonのソリューションが本当に好きでしたそれは可能な限りシンプルで、クラスローダーについて心配する必要はなく、ダブルロギングステートメントやデザインパターンを取得する必要もありません。

この情報の一部があなたに関連していることを願っています。実際のシングルトンを持つことができないためにシングルトンが疑われる問題の量をいじめる代わりに、私が推奨するIロギングソリューションを見てみたいと思います。いくつかのクラスローダーでインスタンス化されている必要があります。


C#の同等のコードはシングルトン(または静的クラスであり、柔軟性がさらに低いため、同じである可能性があり、さらに悪い可能性があります)ではありませんか? using Logging; /* ... */ Logging.Info("my message");
Merlyn Morgan-Graham 2011

また、依存性注入を使用してロガーを注入すると、log4jパターンでシングルトンを回避できます。多くの図書館がこれを行っています。また、汎用インターフェースを提供するラッパーを使用して、netcommon.sourceforge.netなどのロギング実装や、Castle.WindsorやNinject
MerlynMorgan-Graham

それが問題です!logging.info("my message")helloworldよりも複雑なプログラムで使用することはありません。通常、ロガーの初期化にはかなりの量の定型文があります。フォーマッター、レベルの設定、ファイルハンドラーとコンソールハンドラーの両方の設定です。それは決してありませんlogging.info("my message")
ゴッドファーザー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.