高負荷に最適化されたERPシステムを開発しています 。 その結果、システムにクラスタリングが存在します。 また、各クラスターノードは独自のログを保持します。 エラーメッセージ、アプリケーション開発者からのプログラムの進行状況に関するさまざまなメッセージなどがログに書き込まれます。
ジャーナリングの実装方法-削減中。
そもそも先祖の教えによると、彼らは熊手を歩いた。
持っていた
-複数のクラスター
-複数のノードのそれぞれ
-開発者の群衆
-何百ものクライアントデスクトップアプリケーション。
-20〜30 MBのアプリケーションおよびシステムコードとトレースおよびデータダンプ。
NLog経由のすべてのアプリケーションはファイルに書き込みました。
ファイルへのログ記録には1つの利点しかありませんでした-管理が非常に簡単で、それでも疑わしいです-ログファイルもローテーションする必要があります。
他のすべての意味で、これは失敗です。
- データ構造
多くの場合、アプリケーションをデバッグするには、このオブジェクトまたはそのオブジェクトを保存する必要があります。 彼に加えて、明確なメッセージやその他の情報を保存してください。 例:
var parameters = new Dictionary<string, object>(); parameters["id"] = id; parameters["limit"] = maxValues; ... Logger.Verbose("Executing command: {SQL} with parameters: {@Parameters}", sqlCommand, parameters); NLog , .
- 情報の入手可能性
ファイルにログインすると、すべてのサーバー上のログファイルを反復処理して、情報のビットを検索します。 開発者の数を掛けると、誰もがすべてのサーバーにアクセスでき、管理者にとって永続的な頭痛の種になります。
そのため、さまざまなソースに基づいてメッセージを簡単に検索し、メッセージを簡単に理解し、オブジェクトを読みやすい構造化形式で保存できるロギングソリューションが必要でした。
いくつかのオプション(Elasticsearchおよび他の同様のメカニズム)を試した後 、 SeriLogとMongoDBの束に落ち着きました。
SeriLogは、構造化されたイベントストレージの問題を完全に解決しました。 MongoDBは、十分な人気、使いやすいインターフェイス、C#サポートを備えた管理しやすいベースであり、積極的に開発中です。
MongoDBは、C#のドライバードキュメントの品質にやや不満を感じていたため、アドホックサポートを探すためにそれをいじる必要がありました。 ただし、重要ではありません。
その結果、すべての開発者が利用できるイベントを表示するためのこのようなインターフェイスが得られました。
単純なケースでは、最初のタブでフィルターを使用できます。 そして2つ目は、本格的なクエリウィンドウで、任意のクエリを記述できます。
すばらしい機能は、SeriLogがメッセージテンプレートを保存することです。これにより、(プログラムコードを目の前に置いて)関連するすべてのメッセージを見つけることができます。
SourceContextを使用すると、サーバー、クライアント、またはSQLクエリからのメッセージソースでフィルタリングできます。
後者は、最適化するためにデータベースに対して実行されるSQLクエリを追跡する最も便利な手段です。
小さな余談。
一般に、プラットフォームにはSQLクエリをトレースするための2つのツールがあります
- セッションをオンラインでトレース
メッセージは、適切なウィンドウにオンラインで届きます。 クラスター内の任意のセッションに接続できます。
次のようになります。
2番目のタブでは、すべてのパラメーターがラベルにリストされます。
- すべてのSQLクエリとパラメーターを含む集中ログ
SourceContext SqlStatementの集中ログビューアーですべてのメッセージを利用できます
上記の例に戻ります。
var parameters = new Dictionary<string, object>(); parameters["id"] = id; parameters["limit"] = maxValues; ... Logger.Verbose("Executing command: {SQL} with parameters: {@Parameters}", sqlCommand, parameters);
この場合、パラメーターのリスト全体がログイベントに添付され、
ログで、特定の値を持つコマンドの実行に関する記録を見つけることができます
希望のパラメーター。
すべてのSQLクエリがログに記録されるため、この例はやや不自然です。
より現実的なものは次のとおりです。
Logger.Debug("Brand #{BrandID} contents: {@Brand}", brand.ID, brand);
そして、ログエントリのタイプ:
Brand #12 contents: Brand { ID: 12, Name: "Pineapple" }
文書または他の複雑なオブジェクトの状態をログに記録するとき、その内容をメッセージに保存しない方が良いです:
Logger.Debug("Document #{DocumentID} is saved. Document contents: {@Document}", doc.ID, doc); ....
メッセージは次のようになります。
Document #101187 is saved. Document contents: SaleDocument { ActualInvoiceDocumentID: null, AgentID: 636, AllowPartialRelease: False, Amount: 5500, AmountDistributionTypeID: null, ArticlesQuantity: 3, ClientDueOnDelivery: 5500, Comments: "noreply", Convertation: null, CreationDate: 02/06/2015 22:21:53, CreatorID: 7, CustomerSupplyContractID: null, DeadDate: 02/11/2015 22:21:53, Deleted: False, DeliveryActive: False, Description: "Sales (Picking) #101187, 2/6/2015" ... 50kb of JSON follows
メッセージ内の文書番号のみを表示し、パラメーター内のコンテンツを転送する方が適切です。 すべてのパラメーターはJSONで表示できます。
またはPropertyGridの構造化された形式で:
例外をログに記録するとき、次のような内部構造が保持されます
HResult、CallStack、およびInnerExceptionチェーン全体(残念ながらこれを含む
この機能はSerilogに組み込まれていないため、個別に実装する必要がありました。
例外をログに記録するには、メソッドに最初のパラメーターを渡すだけです
ロギング:
Logger.Error(ex, "Cannot execute user task: {Message}", ex.Message);
ログ分析ツールを使用すると、PropertyGridでメッセージ構造を表示できます。
未処理の例外はすべて自動的に記録されます。 ちなみに、このような事件はすべて考慮されるべきであり、本当に間違いがある場合は修正されます。 アプリケーションの品質を向上させるための優れたツール。
結論として。
説明した機能は、タスクに選択できる最高のものです。 柔軟性、管理の容易さ、使いやすさ、イベントを構造化された形式でオブジェクトとともに保存する。
この記事が、他のデスクトップアプリケーション開発者が独自のジャーナリング実装を選択するのに役立つことを願っています。