アプリケーションロギングのパターンとアンチパターンにはどのようなものがありますか?[閉まっている]


66

最近、大規模なエンタープライズアプリケーションのフィールドの問題を調査する必要がありました。問題を見つけるために調べなければならなかったログに恐怖を覚えましたが、結局のところ、ログはバグの特定/隔離にはまったく役立ちませんでした。

注:すべてのバグがログで発見できるわけではないことを理解しています。これにより、ログが恐ろしいという事実は変わりません。

ロギングには、修正を試みることができる明らかな問題がいくつかあります。ここにリストしたくありません。ログファイルを単純に表示することはできません。

代わりに、ロギングの面で私たちがどれだけ悪いかを評価するために、私は知りたい:

  1. アプリケーション(特に大規模アプリケーション)のロギングに関するガイドライン(ある場合)を教えてください。
  2. 従うべきパターン注意すべきアンチパターンはありますか?
  3. これは修正する重要なことですか、それとも修正することもできますか、またはすべてのログファイルが単に巨大であり、それらを分析するために補足スクリプトが必要ですか?

補足:log4jを使用します。

回答:


55

私の実践が有用であると判明したいくつかのポイント:

  • すべてのログコードを運用コードに保持します。できればサブシステムごとに、プログラムを再起動せずに、本番環境でより詳細/詳細でないロギングを有効にする機能があります。

  • grep目でログを解析しやすくします。各行の先頭にあるいくつかの一般的なフィールドに固執します。各行の時間、重大度、およびサブシステムを特定します。メッセージを明確に定式化します。すべてのログメッセージをソースコード行に簡単にマップできます。

  • エラーが発生した場合は、できるだけ多くの情報を収集して記録してください。時間がかかる場合がありますが、とにかく通常の処理が失敗したため問題ありません。デバッガーが接続された実稼働環境で同じ条件が発生したときに待機する必要がないことは、貴重です。

ログは主に監視とトラブルシューティングに必要です。トラブルシューティングの靴に身を置き、何かが間違っているか、または深夜に起こったときにどのようなログを持ちたいかを考えてください。


10
私はこの答えが好きですが、決定点でどのような選択が行われたかを記録することが重要だと付け加えます。多くのジャンクが記録されたが、重要な決定が記録されなかった多くのシステムを見てきました。したがって、ロギングの95%は基本的に役に立ちません。また、要求/応答タイプのシステムでは、サブシステムごとよりも要求ごとにログを記録できることが重要です。
ケビン

4
+1。トラブルシューターの靴に身を置くことについてのあなたのポイントが好きです。ログステートメントには、私たちがやっていることよりもはるかに質の高いメッセージが含まれているように
思え

1
エラーログは、適切なイベントログとアプリケーションログに記録する必要があることに注意することが重要です。
スティーブンエバーズ

2
@SnOrfus:ログを保存する方法は複数ありますが、本質は、システムがクラッシュした最後の1秒までログメッセージを利用できるようにする必要があることです-航空機のブラックボックスのように。何らかの種類のバッファリングを使用する場合は、それをバイパス/すべてのメッセージをフラッシュするオプションを提供します。
rwong

1
@Rig:一方、多くの自家製のロガーはバッファリングを実装せず(そしてすべてのメッセージを忠実にフラッシュします)、パフォーマンスが非常に低下しました。これがオプションにする必要がある理由です。
rwong

28

私は安全性が重要なリアルタイムシステムを使用しており、ドリフトが発生した場合、53日ごとに満月になるとブルームーンに1回現れる珍しいバグをキャッチするための唯一の方法はロギングであることがよくあります。この種のことは、あなたが主題に夢中になるので、口で泡立ち始めたら私は今謝罪します。以下はネイティブコードのデバッグログ用に書かれたものですが、そのほとんどはマネージドワールドにも適用可能です...

テキストログファイルを使用します。当たり前のように見えますが、一部の人々はバイナリログファイルを生成しようとします。それは、私がフィールドにいるときにリーダーツールを探す必要がないからです。さらに、テキストであり、デバッグが冗長な場合、フィールドエンジニアがファイルを読み取って問題を診断できる可能性が高くなります。みんなが勝ちます。

ほとんどすべてをログに記録できるシステムを設計していますが、デフォルトではすべてをオンにするわけではありません。デバッグ情報は非表示のデバッグダイアログに送信され、タイムスタンプが付けられてリストボックスに出力されます(削除前は約500行に制限されています)。接続されたデバッガ。その流用により、複数のアプリケーションからのデバッグ出力をすべてきちんとシリアル化することができます。以前は数値ログレベルを使用していました(レベルを高く設定するほど、より多くキャプチャします)。

off
errors only
basic
detailed
everything

しかし、これはあまりにも柔軟性がありません-バグに向かって進むにつれて、大量の残骸を歩き回る必要なく、必要なものだけにログインを集中させることがはるかに効率的であり、特定の種類のトランザクションまたは操作である可能性がありますエラーが発生します。すべてをオンにする必要がある場合は、自分の仕事を難しくしているだけです。より細かいものが必要です。

だから今、私はフラグシステムに基づいてロギングに切り替えるプロセスにいます。ログに記録されるすべてのものには、それがどのような操作であるかを詳細に示すフラグがあり、ログに記録されるものを定義できるチェックボックスのセットがあります。通常、そのリストは次のようになります。

#define DEBUG_ERROR          1
#define DEBUG_BASIC          2
#define DEBUG_DETAIL         4
#define DEBUG_MSG_BASIC      8
#define DEBUG_MSG_POLL       16
#define DEBUG_MSG_STATUS     32
#define DEBUG_METRICS        64
#define DEBUG_EXCEPTION      128
#define DEBUG_STATE_CHANGE   256
#define DEBUG_DB_READ        512
#define DEBUG_DB_WRITE       1024
#define DEBUG_SQL_TEXT       2048
#define DEBUG_MSG_CONTENTS   4096

このロギングシステムはリリースビルドに付属しており、デフォルトでオンになってファイルに保存されます。バグが平均して6か月に1回しか発生せず、再現する方法がない場合、バグの発生後にログを記録しておくべきであると判断するのは遅すぎます。デバッグビルドでのみ機能するロギングはまさにです。プレーン。ダム。

ソフトウェアは通常、ERROR、BASIC、STATE_CHANGE、EXCEPTIONがオンになった状態で出荷されますが、デバッグダイアログ(またはこれらが保存されるレジストリ/ ini / cfg設定)を介してフィールドで変更できます。

ああ、1つ-私のデバッグシステムは1日に1つのファイルを生成します。要件が異なる場合があります。ただし、デバッグコードは、日付、実行しているコードのバージョン、可能であれば顧客ID、システムの場所などのマーカーですべてのファイルを開始するようにしてください。現場から入ってくるログファイルのミッシュマッシュを手に入れることができ、どこから来たのか、彼らが実際にデータ自体にあるシステムのどのバージョンから来たのかの記録が必要であり、顧客を信頼することはできません/フィールドエンジニアが持っているバージョンを教えてくれます-彼らは自分が持っていると思うバージョンを教えてくれるかもしれません。さらに悪いことに、ディスク上にあるexeバージョンを報告することもありますが、交換後に再起動するのを忘れたため、古いバージョンは引き続き実行されています。コードに自分自身を教えてもらいます。

最後に、コードに独自の問題を発生させたくないので、何日も何週間も経過した後にログファイルをパージするタイマー機能を追加します(現在の時刻とファイル作成の時刻の違いを確認してください)。これは、常に実行されるサーバーアプリでは問題ありません。クライアント側のアプリでは、起動時に古いデータを削除することで取得できます。通常、エンジニアが頻繁に訪問することのないシステムでは、30日程度後にパージします。明らかにこれはログファイルのサイズにも依存しています。


1
+1一般的に優れた答えですが、特にアプリケーションIDとバージョン情報をログファイルに記録する場合、残念ながらこれは非常に頻繁に見逃されます。
バイナリウォリアー

27

ロギングガイドラインに関する私のお気に入りの公開リソースは、Apache JCL Best Practicesです。

JCLのベストプラクティスは、一般とエンタープライズの2つのカテゴリに分けられます。一般原則はかなり明確です。企業の慣行はもう少し複雑であり、それらが重要である理由について常に明確ではありません。

エンタープライズベストプラクティスの原則は、「エンタープライズ」レベルの環境での実行が期待されるミドルウェアコンポーネントとツールに適用されます。これらの問題は、国際化としてのロギング、および障害検出に関連しています。企業はより多くの労力と計画を必要としますが、生産レベルのシステムでは(必要でない場合)強く推奨されます。さまざまな企業/環境にはさまざまな要件があります。そのため、柔軟性があると常に役立ちます...

JCLをターゲットにしているにもかかわらず、これらは一般的なロギングに採用されるのに十分な汎用性があるようです。

  • ロギングの個人的な「ガイドライン」は、デバッグレベルで、理解しやすいロジックと十分な(ただし過負荷ではない)詳細で、ログをストーリーのように読もうとすることです。

最も有名なアンチパターンは、おそらく「例外を飲み込む」ことです-それをウェブで検索してください。

巨大なログファイルに関しては、私の実践では、これはほとんど通常のケースでした。そして、はい、あなたがそれらを呼び出すときの補足スクリプトおよび/またはChainsawのようなツールも私には普通に見えます。

  • 上記のことは、すべてのログを常に盲目的に1つの巨大なファイルに入れなければならないという意味ではありません。一部のログを個別のファイルに書き込み/コピーすると便利な場合があります。たとえば、私の最近のプロジェクトQA担当者は、メトリックとタイミングデータ用の専用ファイルと、システム操作に関する簡単なレポートを求めました。彼らはそれから恩恵を受けるだろうと言っており、開発者はそれをやった(簡単なレポートファイルからの恩恵は本当に重要であることが判明した)。

PS。アンチパターンに関して、思い浮かぶのは「あふれる」無意味なメッセージです。

  • 繰り返しの多いループから複数の同様のメッセージが送信されるのを見て、フラッディングと呼びます。私にとって、フラッディングはソースコードで検出したときに駆除しようとするのに十分迷惑です。通常、それを改善するにはいくつかの技術が必要です-なぜなら、ループ内で起こることは興味深いかもしれないからです。より深く改善する時間がないときは、少なくともそのようなメッセージのログレベルを最低のものに変更して、フィルターで除外しやすくするようにします。

  • 無意味なメッセージは、かなり一般的なゴミのようです。これらはソースコードで読むと無害に見えます-デバッグ出力を次のように分析する苦労をしなければならないと思います...

    step #1
    step #2
    step #3
    

    ...彼らの固有のさを深く感謝します。ソースコードレベルでこの種の問題を検出するための私のお気に入りのヒューリスティック(過去のプロジェクトの1つで同僚によって提案された)は、ロギングで使用される文字列リテラルでのスペースシンボルの出現回数を計算することです。私の経験では、スペースがゼロであることは基本的にロギングステートメントが無意味であることを保証し、1つのスペースは潜在的な問題の良い指標でもあります。


4
フラッディングを回避するために、通常、ループのヒューリスティックを収集し、ループ後に出力します。ループ内で発生する興味深いことは、変数(などsomethingSpecialHappenedCount)に格納してから、ロガーに出力する必要があります。
スポイケ

@スポイケ良い点!変数に格納することは確かに洪水戦うために私の個人的な好みのトリックの一つである
ブヨ

1
ループが終了した後、簡単に比較できるように、ログのASCIIテーブルとしてすべての異なるカウンターをロガーに出力します。テーブルのアイデアは、SpringのStopWatch.prettyPrint()が生成するアイデアに触発されました。それ以外は、ログテキストを読みやすく、関連性のあるものにすることは、回答で前述したように、依然として「芸術」です。
11

@Spoike:(および@gnat)これは興味深いです。それでは基本的に、ロギングの目的だけで実際のコードをビジネスロジックに追加しますか?私はこれを聞いたことも、これを行ったこともありませんし、同僚にそれを正当化する方法がわかりません。私たちがこれを始めたら、開発者の何人かは、ビジネスロジックが複雑で読みにくくなるほどソースコードを混乱させることを恐れています。単純にステートメントを記録するだけで、ソースは見苦しくなります。
c_maker

2
@c_makerロギングとビジネスロジックの混合についてのあなたの主張は、熱心な質問に値します。個人的には、これらの問題について強い意見はまだありません。理論的には、AOPとiircを使用したいくつかの分離の改善が想像できます。このアプローチには実用的なアプリケーションさえあります。しかし、実際には、「混合」アプローチに固執し、これまでのところ大きな問題はありませんでした。ソースコードの乱雑さは本当の危険ですが、繰り返しますが、これまでのところ、ロギングコードと「平和的に」共存させることができました。もちろん、これには一定の努力が必要です。
-gnat

11

例外を1回だけ記録してください!

私が気づいた共通の問題点の1つは、例外のログ記録と再スローです。その結果、ログファイルには、いくつかのスタックレベルで同じ例外が数回含まれています。


5

アンチパターンは次のとおりです。データベーステーブルに20個の「genericvariable」フィールドを作成して考えられるすべてを追跡し、さまざまな種類のログに対して88(およびカウント)の列挙値を設定します。


+1-これを見た。string1、string2、string3、string4、string5のような列を持つ「エラーテーブル」。すべての列を連結すると、どのドキュメントでも参照されないエラーコードになります。その結果、ロギングは混乱を招き、役に立たなくなります。「サードパーティのエンタープライズアプリ、カスタム開発、デバッグ、地獄」とも呼ばれます。
モーガンハーロッカー

私の場合、「ロギングが実際に何を含むのか全くわからない手動のロギングシステム」
ウェインモリナ

4

私のログの経験は優れているほど優れてますが、マシンでフィルタリングできるように一貫性あり、アプリケーションのすべてのコンポーネントの重大度レベルを個別に構成できます。

また、将来のバグを見つけるために必要なロギングを予測することは非常に困難です。バグを記録する明らかな場所のほとんどは、製品が出荷される前に修正されます。バグレポートの結果が、再度発生した場合に診断するためにロギングを追加したということは珍しくありません。


2

ここの家の運用側からのメモのカップル:

1)ログがローカルで設定可能であることを確認してください。できればテキストエディタよりも重いツールを使用しないでください。ほとんどの場合、トレースレベルのログを取得したくありませんが、それを有効にできることが大好きです。

2)可能な場合は、テキストエディターよりも重いツールを使用してログを読み取れることを確認してください。実稼働システムで障害が発生した奇数時間にツールハントを実行することほど悪いことはありません。


1

Webアプリケーションの使用経験から:

(現在、ストレージは非常に安くなっています)

  • (その瞬間に)利用可能な限り多くの情報を記録します。
  • ログ文字列には常にDateTime.Nowを含めます。
  • 私は常に(可能な場合)特定の「アクション」の時間を記録します。
  • ログ文字列と一貫性を保ってください。私はいつもこの種のパターンを使用しているので:

    • 「[情報X] [情報Y] [情報Z] [など]」

1

スタックトレースとは別に、現在のアプリケーションの状態と入力を記録します。

ソフトウェアは決定論的であり、バグを再現するために必要なのは通常これら2つだけです。完全な状態を保存するのは面倒な場合があるため、たとえば以前の入力によって現在の状態を再現する方法も有効です。

もちろん、より多くのデータが常に優れていますが、少なくともこれらの2つは、最も簡単なクラッシュの良い出発点です。


3
「ソフトウェアは決定論的です」 =>残念ながら常にではありません。たとえば、並行性のバグを考えてください。
-assylias
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.