web-dev-qa-db-ja.com

ロギングパラダイム、DI / IoC、オブジェクト階層

次のシナリオ例をサポートする方法を理解しようとしています

サンプル言語としてC#を使用しているわけではありません。

シナリオ

NASAはRoversSatellitesを別のプラネット

これらの車両は多くのログを必要とし、乱雑さを減らすために、次の方法でこれらのログをフィルタリングできるようにしたいと考えています。

私はこれらのうちの1つを組み合わせる/選ぶことができるはずです:

  • ログのみRoversinMercury
  • すべてをログオンMars
  • すべて記録衛星

実装

オブジェクトグラフ

Object Graph

したがって、Planet、Rover、Satelliteの3つのオブジェクトタイプがあります。

地球には3つの衛星が含まれています

火星には1つのサテライトと2つのローバーが含まれています。

水星には2つのサテライトと1つのローバーが含まれています。

ログをオブジェクトに追加する通常の方法は次のとおりです。

public class Mars : Planet {

 private static ILog Log = LogManager.GetCurrentClassLogger();

}

これは通常、「NASA​​.Mars」という名前のロガーを返します。これにより、すべての「NASA​​.Mars」をログに記録するようにフレームワークを簡単に構成でき、ローバーでも同じことが起こります。

public class Rover {
 private static ILog Log = LogManager.GetCurrentClassLogger();
}

「NASA​​.Rover」という名前のロガーを取得します

しかし、どうすればローバーが水銀中にあることを知ることができますか?これはロギングサブシステムの要件であるため、Roverクラスのプロパティとして存在してはなりません。

考え

依存性注入

コンストラクターでILogインスタンスを受け入れるようにクラスを設計すると、理論的には、子オブジェクトのログ名を制御できます

(親Planetクラスを使用できます。簡潔にするために省略されています)

public class Mars : Planet {
  private static ILog Log = LogManager.GetCurrentClassLogger();

  void Mars() {
    this.Rovers.Add(new Rover(LogManager.GetLogger(Log.Name + ".Rover"));
  } 
}

public class Rover {
 private ILog Log;

 void Rover(ILog log) {
   Log = log;
 }
}

このアプローチの私の問題:

  • 子オブジェクトごとにロガーを作成すると、おそらくアンチパターンでさえ「オフ」に感じるかもしれません
  • これは、階層のより深いレベルを処理するときに非常に面倒になる可能性があります(惑星->エリア->ステーション->ローバーと考えてください)
  • ロガー名文字列(NASA.Mars.Rover)からオブジェクト階層を解読することは問題があります(ワイルドカードでは十分でない場合があり、名前によっては一意性が不十分な場合があります)。

コードを雑然とさせることなく、これを解決するための良いアイデアを探しています

4
Aviel

情報量が限られているので、私は惑星自体はロギングを必要としないと仮定しています。それは、ローバーと衛星からの測定値のみです(それらは監視デバイスです)。その場合、ローバーとサテライトのそれぞれにロガーの独自のインスタンスが必要ですが、おそらく同じログファイルにログを記録する必要があります。

惑星がローバーとサテライトを所有する代わりに設計を反転させ、サテライトとローバーが原点を追跡することで、この問題を単純化できます。

public class Satellite
{
    private ILog Log;
    void Satellite(Planet Origin)
    {
        //include logic here on when not to instantiate logger e.g. Origin.Name != "Mercury"
        Log = LogManager.GetLogger("Satellite");
    }
}

各惑星での衛星とローバーの数を追跡したい場合は、登録されたもののコレクションを追跡し、それらが発生した惑星でフィルターに掛ければ、Linqクエリでそれを行うことができます。

2
Lorena72

オブジェクトにロギング動作が必要な場合、はい、ロガーは注入する依存関係です。ロガーの例の多くは、サービスロケーターアプローチを使用していますが、それが最良の方法であるとは限りません。

「.Rover」という名前がクラスの名前に関連付けられていないことについて、私はワイルドではありません。文字列を追跡することなくクラス名をリファクタリングできるのが好きです。リフレクションとスタックピークトリックを使用して、ハードコードされた文字列に名前を付けないようにしました。これらのトリックは安くはないことを覚えておいてください。だから、適切なタイミングでそれらを行います。

ロギング動作がクラス名、呼び出されたメソッド、および渡されたパラメーターについてのみ知る必要がある場合は、アスペクト指向プログラミングを調べてください。私は、監査対象のオブジェクトからすべての監査コードを削除できるいくつかの命名規則を中心とした監査システム全体を開発しました。

問題の核心は、ローバーをログに記録する必要があるかどうかを知る方法が必要なことです。ローバーがどこにあるかについての保存された知識がない場合、これらの情報をログ設定の形式で保持できます。つまり、すべての火星ローバーはdev/nullにログを記録するように設定できます。

最もデータが限定的でCPUを集中的に使用するソリューションは検索です。ローバーがロギングを必要とする場合、それを含む可能性のある世界でローバーを検索する必要があります。それが高価な場合、これは簡単に禁止される可能性があります。

AOPはそのすべてを実行できます。ローバーとは異なるクラスで必要なものを構築するだけです。ローバーはどういうわけか火星に行かなければならないことを思い出してください。それはどういうわけかAOPによって検出することができます。次に、それを覚える方法を決定する必要があります。

0
candied_orange