構造化ロギングと基本ロギングの利点


110

新しいアプリを作成しています。構造化されたログを含めたいと思います。私の理想的なセットアップはSerilog、C#コードとBunyanJSのようなものです。これらはfluentd、いろいろなものに食い込んでから出て行くことができると、最初は考えていましたelasticsearch + kibana。MySQLデータベースはすでにあるので、短期的にはSerilog + Bunyanのセットアップと開発者がそれを使用することに興味があり、もう少し時間をかけてfluentdと残りを取り込む間にMySQLにログインできます。

:しかし、私たちの経験豊富なプログラマーの一つは、単にような何かを好むlog.debug("Disk quota {0} exceeded by user {1}", quota, user);使用log4netして、同じようにMySQLのに対してSELECT文を実行します:SELECT text FROM logs WHERE text LIKE "Disk quota";

そうは言っても、どのアプローチがより良いか、ロギングシステムのタイプを選択する際にどのようなことを考慮する必要がありますか?


行った編集に同意します。構造化されたロギングと基本的なロギングの利点と違いを理解しようとしているので、誰かに何かを証明しようとはしていません。私の考えでは、構造化されていると、特にログのソースでの柔軟性が高まり、データの表示方法がより柔軟になります。私の理解の時点では、なぜ基本的なロギングとMySQLの検索が構造化ロギングよりも優れている/悪いのかを説明することはできません。
DTI-マット

2
@ DTI-Matt serilogの構造化されたログは、基本的なログであり、印刷するオブジェクトをフォーマットするだけです。ToStringを非常に簡単にオーバーライドすることで、自分でできることです。より重要な側面は、ログファイルの構成と管理であり、文字列を別の文字列にフォーマットする方法ではなく、パフォーマンスです。開発者がlog4net(優れたロギングライブラリ)を使用したい場合、serilog(かっこいい)の選択は、「問題を探す解決策」の1つです。
-gbjbaanb

@ DTI-Matt serilogを見ると、log4netと非常によく似ています。log4netは、configで構造化ログを作成します。追加情報を構成してテーブルに書き込むことができるため、ログメッセージを検索する必要はありません。また、fluentd tipstuff.org/2014/05/の
RubberChickenLeader

ここで概念的な質問の考えを理解していない愚か者がいることに注意してください。ETL機能とコードのハンドルを取得しようとして、データベースアプリケーションの方向性を尋ねると、深刻なダウンボートが発生します。あなたの質問もチョッピングブロックにあると思います。
-user3916597

2
@gbjbaanb Serilogは、イベントをテキストとしてレンダリングする場合、log4netと同じように機能しますが、ログを保存するために構造化フォーマットを使用する場合、名前付きプロパティを通過する引数に関連付けます(つまり、正規表現などのない検索/フィルタリングをサポートするため) )HTH!
ニコラス・ブルムハルト

回答:


140

構造化されたアプローチには2つの基本的な進歩があり、追加の努力なしで(場合によっては極端なレベルの)テキストログを使用してエミュレートすることはできません。

イベントの種類

log4net次のような2つのイベントを作成する場合:

log.Debug("Disk quota {0} exceeded by user {1}", 100, "DTI-Matt");
log.Debug("Disk quota {0} exceeded by user {1}", 150, "nblumhardt");

これらは同様のテキストを生成します:

Disk quota 100 exceeded by user DTI-Matt
Disk quota 150 exceeded by user nblumhardt

しかし、マシン処理に関する限り、それらは異なるテキストの2行にすぎません。

すべての「ディスククォータ超過」イベントを検索することもできますが、イベントを探すという単純なケースは、次のlike 'Disk quota%'ような別のイベントが発生するとすぐに失敗します。

Disk quota 100 set for user DTI-Matt

テキストロギングは、イベントのソースについて最初に持っていた情報を破棄します。通常は、より複雑な一致表現でログを読み取るときに、これを再構築する必要があります。

対照的に、次の2つのSerilogイベントを作成する場合:

log.Debug("Disk quota {Quota} exceeded by user {Username}", 100, "DTI-Matt");
log.Debug("Disk quota {Quota} exceeded by user {Username}", 150, "nblumhardt");

これらは、log4netバージョンと同様のテキスト出力を生成しますが、舞台裏では、"Disk quota {Quota} exceeded by user {Username}" メッセージテンプレートは両方のイベントによって伝達されます。

適切なシンクを使用すると、後でクエリwhere MessageTemplate = 'Disk quota {Quota} exceeded by user {Username}'を記述し、ディスククォータを超えたイベントを正確に取得できます。

すべてのログイベントでメッセージテンプレート全体を保存することは必ずしも便利ではないため、シンクによってはメッセージテンプレートを数値EventType(たとえば0x1234abcd)にハッシュするか、ログパイプラインにエンリッチャーを追加して自分でこれ行うことができます

以下の次の違いよりも微妙ですが、大量のログボリュームを処理する場合は非常に強力な違いです。

構造化データ

ここでも、ディスク領域の使用に関する2つのイベントを考慮すると、テキストログを使用してを使用して特定のユーザーを照会するのは簡単like 'Disk quota' and like 'DTI-Matt'です。

しかし、生産診断は必ずしも簡単ではありません。ディスククォータを超過したイベントが125 MB未満であるイベントを見つける必要があると想像してください。

Serilogでは、これはほとんどのシンクで次のバリアントを使用して可能です。

Quota < 125

正規表現からこの種のクエリを構築すること可能ですが、手間がかかり、通常は最終手段の手段になります。

次に、これにイベントタイプを追加します。

Quota < 125 and EventType = 0x1234abcd

ここで、これらの機能がどのように簡単な方法で組み合わされて、ログを使用した実稼働デバッグが最高の開発アクティビティのように感じられるかを確認し始めます。

もう1つの利点は、おそらく前もって簡単に防ぐことはできませんが、生産デバッグが正規表現ハッカーの土地から取り除かれると、開発者はログをより重視し、それらを記述する際により多くの注意と配慮を行使し始めます。より良いログ->より質の高いアプリケーション->あらゆる場所でより幸せに。


4
私はこの答えが大好きです。非常によく書かれており、何らかの理由で説明できませんが、私は席の端にいます。
ジョカブ

16

処理のためにログを収集している場合、データベースに解析したり、処理されたログを後で検索したりするために、構造化されたログを使用すると、処理の一部がより簡単/より効率的になります。パーサは、公知の構造(を利用することができます例えば JSON、XML、ASN.1、何でも)と正規表現とは対照的に、(計算上高価になることができます(比較的)コンパイルして実行するために)、解析のためのステートマシンを使用します。そのようなあなたの同僚によって提案されるような自由形式のテキスト、の解析、正規表現に依存し、傾向そのテキストに依存して変化していません。これにより、自由形式のテキストの解析がかなり脆弱になる可能性があります(つまり、解析はコード内の正確なテキストに密接に結合されます)。

検索/ルックアップのケースも考慮してください。

SELECT text FROM logs WHERE text LIKE "Disk quota";

LIKE条件では、すべてのtext行の値との比較が必要です。繰り返しますが、特にワイルドカードが使用される場合、これは比較的計算コストが高くなります。

SELECT text FROM logs WHERE text LIKE "Disk %";

構造化ログでは、ディスクエラー関連のログメッセージはJSONで次のようになります。

{ "level": "DEBUG", "user": "username", "error_type": "disk", "text": "Disk quota ... exceeded by user ..." }

この種の構造のフィールドは、たとえば SQLテーブルの列名に非常に簡単にマッピングできます。これは、ルックアップをより具体的/詳細にできることを意味します。

SELECT user, text FROM logs WHERE error_type = "disk";

それらの列の値に句を使用しない限りLIKE、値が頻繁に検索/検索されると予想される列にインデックスを配置できます。ログメッセージを特定のカテゴリに細分化すればするほど、ターゲットを絞り込めます。たとえば、error_type上記の例のフィールド/列に加えて、さらにそのようにすることもできます"error_category": "disk", "error_type": "quota"

あなたのログメッセージにあなたが持っているより多くの構造は、(のようなより多くのあなたの構文解析/検索システムではfluentdelasticsearchkibana)その構造を利用し、より迅速かつより少ないCPU /メモリで自分のタスクを実行することができます。

お役に立てれば!


1
+1速度と効率だけではないことを付け加えます。構造化されたログを使用して、「構造化されたクエリ」を使用すると、検索結果の関連性がはるかに高くなります。異なるコンテキストで発生する単語を検索しないと、無関係なヒットが大量に発生します。
マルジャンヴェネ

1
私からの+1も、これは釘だと思う。イベントタイプのケースを拡張するために、以下に若干異なる定式化を追加しました。
ニコラス・ブルムハルト

8

アプリが1日に数百のログメッセージを作成する場合、構造化されたログのメリットはあまりありません。デプロイされたさまざまなアプリから1秒あたり数百のログメッセージが送信される場合は、間違いなくそうなります。

関連して、ログメッセージがELKスタックに配置されるセットアップは、SQLへのロギングがボトルネックになるスケールにも適しています。

私は「基本的なログと検索」のセットアップとSQL select .. likeと正規表現がバラバラになった限界に押し込まれているのを見てきました-誤検出、省略、保守が難しく、誰も触れたくない、バグのある恐ろしいフィルターコードがあります。フィルタの仮定に従わない新しいログメッセージ、レポートに違反しないようにコード内のロギングステートメントに触れたくないなど。

そのため、この問題をより良い方法で処理するために、いくつかのソフトウェアパッケージが登場しています。Serilogがあり、NLogチームがそれを見ていると聞きました。また、Nlogについて書いたのStructuredLogging.Jsonですが、新しいASP.Netコアロギングアブストラクションは「ロギングプロバイダーが...構造化されたロギングを実装できるようにする」こともわかります。

StructuredLoggingの例。次のようにNLogロガーにログインします。

logger.ExtendedError("Order send failed", new { OrderId = 1234, RestaurantId = 4567 } );

この構造化データはkibanaに送られます。値1234OrderIdログエントリのフィールドに保存されます。次に、kibanaクエリ構文を使用して、たとえば、すべてのログエントリを検索できます@LogType:nlog AND Level:Error AND OrderId:1234

Messageこれは、OrderId必要に応じて完全一致または不正確な一致を検索したり、カウントを集計したりできる単なるフィールドです。これは強力で柔軟です。

StructuredLoggingベストプラクティス

記録されるメッセージは毎回同じである必要があります。IDや数量などのデータ値を含むようにフォーマットされた文字列ではなく、定数文字列である必要があります。その後、簡単に検索できます。

ログに記録されるメッセージは明確である必要があります。つまり、無関係なログステートメントによって生成されるメッセージとは異なります。次に、それを検索しても、無関係なものと一致しません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.