web-dev-qa-db-ja.com

ロガーをシングルトンとして持つことは良い習慣ですか?

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

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

しかし、それは非常に迷惑ですので、私はこれをしばらくの間これをプロパティとして使用しました:

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

これも厄介です-乾燥していません。すべてのクラスでこれを繰り返す必要があります。基本クラスを使用できますが、もう一度-Formクラスを使用しているので、FormBaseなどが必要になります。

    Infrastructure.Logger.Info("blabla");

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

75
Giedrius

これも厄介です-それは [〜#〜] dry [〜#〜] ではありません

それは本当だ。しかし、あなたが持っているすべてのタイプに浸透する横断的関心事に対してできることは、それだけです。ロガーはどこでもuseする必要があるため、これらのタイプのプロパティが必要です。

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

シングルトン

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

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

しかし、シングルトンを使用すると、必要なすべてのリファクタリング(クリスタルボールの時間!)を含めて大幅な時間を節約できれば、それらと一緒に暮らせるかもしれません。シングルトンの使用があった場合、これはそれかもしれません。あなたがeverあなたの心を変えたい場合のコストは、それが得るほど高くなることに留意してください。

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

妥協することなく同じように機能する可能性のある他の代替手段がありますので、最初にチェックアウトする必要があります。

Visual Studioコードスニペット

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

AOPを使用してDRY off

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
34

依存関係注入コンテナーにロガーインスタンスを配置し、それが必要なクラスにロガーを注入します。

37
Daniel Rose

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

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

  • ServiceLocator (または既に使用している場合は他の Dependency Injection コンテナー)を使用して、サービス/クラス間でロガーを共有できます。この方法で、ロガーをインスタンス化できます。複数の異なるロガーとServiceLocatorを介した共有は、明らかにシングルトンであり、何らかの種類の Inversion of Control です。このアプローチにより、ロガーのインスタンス化および初期化プロセスよりもはるかに柔軟になります。
  • ほとんどすべての場所でロガーが必要な場合-各クラスがLogInfo()LogDebug()LogError()などのロガーのメソッドを呼び出せるように、Object型の拡張メソッドを実装します。
23
sll

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

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

Registry.Logger.Info("blabla");
14
Anders Abel

単純なシングルトンはお勧めできません。ロガーの交換が難しくなります。ロガーにフィルターを使用する傾向があります(「ノイズの多い」クラスの中には、警告/エラーのみを記録するものがあります)。

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

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!");
    }
}
9
jgauffin

.NETには、Dependency Injectionという本があります。必要なものに基づいて、インターセプトを使用する必要があります。

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

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

  1. 依存関係があるか、必要ですか? - それが必要
  2. それは横断的な懸念ですか? - はい
  3. 答えが必要ですか? -いいえ

傍受を使用する

3
Piotr Perak

ロギングの適切なソリューションを確認したい場合は、pythonでロギングがimport loggingと同じくらい簡単で、logging.debug("my message")またはlogging.info("my message")で、必要なだけシンプルになります。

Javaにはロギングの良い解決策がありませんでした。つまり、log4jは避けるべきです。ここで答えられた「ひどい」シングルトンを使用せざるを得ず、同じロギング文を一度だけロギング出力しようとする恐ろしい経験がありましたダブルロギングの理由が疑われる場合、同じ仮想マシンの2つのクラスローダーに1つのシングルトンのロギングオブジェクトがあるためです(!)

私はC#にそれほど固有ではないことをご容赦ください。しかし、C#での解決策が似ているように見えたことから、Java log4jがあり、シングルトンにする必要があります。

GAE/pythonを使用したソリューション が本当に好きだったのは、それができる限り簡単であり、クラスローダーを心配したり、二重ロギングステートメントを取得したり、設計パターンを取得したりする必要がないためです。 。

この情報の一部があなたに関連していることを望み、あなたが私が推奨するロギングソリューションの代わりに私が推奨するロギングソリューションを見たいと思うことを望みます。複数のクラスローダーでインスタンス化する必要があります。

0

私が個人的に最も簡単だと思う別の解決策は、静的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
0
Erik Kalkoken