AOPの助けを借りて、ロギングコードをビジネスロジックから削除できます。しかし、私はそれが単純なものをログに記録するためにのみ使用できると思います(つまり、ロギングメソッドの入り口/出口とパラメーター値)。
ただし、ビジネスロジックに何かを記録する必要がある場合はどうなりますか?例えば.
public void SomeDomainMethod(string id)
{
//Get user by Id
User user = Users.Get(id);
if (user == null)
{
Log.Warn("user is not existed"); //<----------------- Log A
throw new InvalidOperationException("user is not existed");
}
//Step 1
while(true)
{
//do something
}
Log.Info("Step 1 is completed"); //<----------------- Log B
//Step 2
while(true)
{
//do something
}
Log.Info("Step 2 is completed"); //<----------------- Log C
}
上記のサンプルメソッドは十分に明確ではない可能性があります。ここで説明したいのは、メソッドをドメインの観点から最小単位として処理する必要があることです。これを小さな部分に分割しないでください。 =
メソッドから3つ以上のロギングコードを移動することは可能ですか?そのような状況のベストプラクティスは何ですか?
もちろん!
しかし、私の経験では、便利ロギングには2つの一般的なタイプがあります。
Everything logs:プロファイリングAPIを介して構築されたログ。パフォーマンスの問題を特定し、例外を報告するのに適しています。うるさい。
ビジネスイベントログ:ビジネスロジックで呼び出されたログ。ビジネスが気にかけるかもしれないもの。最小限のノイズ。注目に値する、論理的な「ビジネス」イベント。監査およびKPIに適しています...
したがって、私は2つのことを強くお勧めします。まず、New Relicなどの他の監視ツールと同じことを実行します 。NETプロファイリングAPIを使用1。次に、論理ビジネスイベントをビジネスロジックに記録します。特定のイベントの記録を保持することは、 is ビジネスロジックです。
そして、私は通常、どちらの種類のロギングについてもAOPを提案しません2。私の経験では、 everything 、つまりプロファイラーを使用している、または論理/ビジネスイベントが必要です。後者の場合、ビジネスロジックでロガーを呼び出すだけの方が簡単だと思います。
1.しかし、真剣に、何千時間もの労力を節約し、既存のプロファイラーツールを使用してください...
2.もちろん、これはあなたが aspect はビジネスルールを隠すのに最適な場所ではないという私の意見を共有することを前提としています!
もちろん、これにはAOPを簡単に使用できます。単にパーツをリファクタリングする
intoseparate methods(コードをよりクリーンにするためにどちらかを実行する必要があったので)。これで、選択したメソッド呼び出しをログに記録するようにAOPフレームワークを簡単に構成できます( ここに示すように )。例外は呼び出し元が直接ログに記録でき、これをビジネスロジックから取得するためにAOPを使用する必要はありません。
あなたの編集へ:
ここで示したいのは、メソッドはドメインの観点から最小単位として扱われるべきだということです。細かく分割しないでください
なぜそれをすべきではないのですか? 「ビジネスロジックコンテキスト」で、ログに値する「何か」をログに記録する場合、この「何か」にわかりやすい名前を付けることができれば、ほとんどの場合、コードをメソッドにリファクタリングすることは意味があります。それ自身。 AOPを使用する場合は、ロギングの要件に関係なく、コードを構造化する必要があると思われる方法でコードを構造化する必要があります。これをAOPの欠点として解釈することも、コード構造を改善できるフィードバックを提供するため、これを利点として解釈することもできます。
Loggingの要素がビジネス要件の一部でない限り、コードから完全に除外するのが最善です。
つまり、「ステップ1完了」のようなものをログに記録したくないのです。最初はデバッグに役立つかもしれませんが、本番環境では決して見ないギガバイトのゴミを生成するだけです。
Step1Completeが何らかのアクションを必要とするある種のビジネスイベントである場合、ILoggerなどをクラスに注入することを強制せずに、古き良き昔ながらのイベントを通じて公開できます。
いくつかの一般的なパターンを利用して、ビジネスロジックからロギングコードを引き出すことができます。ただし、そうする価値はないかもしれません。
たとえば、リスナーを使用すると(1つを作成するか、イベントバスを使用するなど)、コードは次のようになります。
public void SomeDomainMethod(string id)
{
//Get user by Id
User user = Users.Get(id);
if (user == null)
{
listener.OnUserNotFound(userId);
throw new InvalidOperationException("user is not existed");
}
//Step 1
while(true)
{
//do something
}
listener.OnStep1Finished(......);
...
}
リスナーにロギングを実装することにより、ロギングロジックはビジネスロジックに含まれなくなります。
ただし、ロジックの意味のあるイベントを常に定義できるとは限らないため、これが常に現実的であるとは限りません。
別のアプローチは、実行中のプロセスに注入できるようにするSolarisのDtraceのようなメカニズムによるものです(C#で同様のことを行う方法はあると思いますか?)ロギングと統計収集を実行時に定義できます。それでも他の欠点があります。
別のアプローチは、ビジネスログと技術ログを区別することです。次に、ビジネスロギングを「監査」と呼び、ストレージ期間などの特定のビジネスルールと、ビジネスアクティビティモニタリングなどの処理ルールを適用できます。
一方、技術的なロギング、または単に「ロギング」は、技術的な問題の痕跡を残す最後の手段です。ログメッセージの永続化の失敗に対して、非同期、高速、耐性が必要です。また、ログメッセージは、問題の原因に近づくために、可能な限り少ない数のプロキシを通過する必要があります。
ロギングのロジックはかなり可変であり、実装と密接に結びついているので、本当にそれをコードから分離する必要がありますか?
監査のロジックはドメインロジックと見なされ、それに応じて処理されます。
たとえば、ヘキサゴナルアーキテクチャでは、クライアント、ストレージ、およびMQ(および場合によってはメトリックとコントロール)ポートとともに監査ポートが存在する可能性があります。これはセカンダリポートになります。つまり、このポートでのアクティビティは、外部システムではなくビジネスコアによってトリガーされます。
クラスまたはメソッドに直接ログを記録しないようにする方法:
例外をスローし、呼び出しツリーのさらに上のcatchブロックでログを記録します。ログレベルをキャプチャする必要がある場合は、カスタム例外をスローできます。
ロギング用にすでに装備されているメソッドを呼び出します。
ロギングをビジネスロジックから分離することが本当に必要ですか?実行されるロギングは、記述されたビジネスロジックに対応しているため、同じクラス/関数内にあることが理にかなっています。さらに重要なことは、コードを読みやすくすることです。
ただし、ロギングをビジネスロジックから分離したい場合は、カスタム例外をスローし、それらの例外をロギングに渡すことを検討する必要があります。
いいえ、c#にはありません
OP、特定の質問に対する答えは「いいえ」ではなく、C#ではありません。他にももっとネイティブなAOP言語があるかもしれませんが、私が見たc#のAOPへのすべてのアプローチは、アスペクトされた動作を join point のコンテキストでのみ適用できます。つまり、フローが必要です。あるコードブロックと別のコードブロック間の制御。当然ながら別のメソッドを呼び出すことを除いて、アスペクト動作はメソッドの途中で実行されません。
ロギングの特定のビットを「apsect-ize」することができます
そうは言っても、ログの書き込みではなく、ログに関連する特定の懸念を抽出できます。たとえば、メソッドの入り口で実行されたカットポイントは、ロギングコンテキストを設定してすべての入力パラメーターを出力し、出口で例外をキャッチしたり、ログを永続的なストレージにコミットしたりすることができます。
Logwritingはアスペクトではありません
とにかく、ログの書き込みは実際には分野横断的な問題ではないことを付け加えておきます。少なくともログをデバッグしないでください。これについての私の証拠は、この側面が何をするかを完全に説明する横断的な要件を書くことができなかったことです-ログを書き込む目的は、何が起こっているかを反映することであるので、すべてのケースに固有ですロジック、および各メソッドのロジックは合理的に一意である必要があります( [〜#〜] dry [〜#〜] を参照)。
言い換えれば、ログの書き込みと書き込まれているものとの間に不可解な論理的な依存関係があります。あなたはそれを一般化することはできません。
しかし監査は
ある種の機能的なロギング要件がある場合(例: non-repudiation 要件をサポートする監査ロギング)は、これらのログ書き込みを実行する必要があると判断した場合(そして私は同意します)メソッドの途中で、アスペクト指向の考え方と一貫した方法でコードを構造化していない。これが発生した場合、必要なレベルの細分性が得られるまで、コードを個別のメソッドに抽出する必要があります。