- 現代の分散システムでは、従来のログ方式は真実を伝えられない構造的限界を抱えている
- ログは依然として2005年式の単一サーバー環境を前提に設計されており、複数のサービス・データベース・キャッシュをまたぐリクエストのコンテキストを失ってしまう
- 単純な文字列検索では構造・関係・相関性を理解できず、問題の原因を見つけにくくする
- 解決策は、各リクエストごとに**あらゆる文脈を含んだ単一の「Wide Event(または Canonical Log Line)」**を残すこと
- これにより、ログは単なるテキストではなく分析可能なデータ資産へと変わる
ロギングの根本的な問題
- 従来のログはモノリシックサーバー時代を前提に作られており、現代の分散サービス構造を反映できていない
- 1つのリクエストが複数のサービス・DB・キャッシュ・キューを通過するのに、ログは依然として単一サーバー基準で記録される
- 例のログでは1リクエストあたり13行が生成され、同時ユーザー10,000人なら毎秒130,000行が流れ込むが、その大半は無意味な情報である
- 問題発生時に必要なのは**コンテキスト(context)**だが、現在のログにはそれが欠けている
文字列検索の限界
- ユーザーが「決済できない」と報告したとき、メールアドレスや user_id でログを検索しても一貫した構造がないため有効な結果を得にくい
- 同じユーザー ID でも
user-123、user_id=user-123、{"userId":"user-123"} のように数十種類の形式で記録される
- サービス間でログフォーマットが異なるため、関連イベントの追跡が不可能
- 核心的な問題は、ログが**書き込み(write)**中心に設計されており、**検索(query)**には最適化されていないこと
重要概念の定義
- Structured Logging: 文字列の代わりに**キー・バリュー(JSON)**形式で記録する方式
- Cardinality(カーディナリティ): フィールドの一意な値の数。たとえば
user_id は非常に高い
- Dimensionality(次元数): ログイベント内のフィールド数。多いほど分析可能性が高い
- Wide Event / Canonical Log Line: リクエストごとに1つの文脈が豊富な単一ログイベント
- ほとんどのロギングシステムは高カーディナリティデータをコストの問題で制限するが、実際にはそれこそがデバッグに最も有用である
OpenTelemetry の限界
- OpenTelemetry(OTel)はプロトコルと SDK のセットであり、データ収集・転送の標準だけを提供する
- しかし OTel は次のことを行わない
- 何をログに記録するかを決めない
- ビジネスコンテキスト(例: サブスクリプション等級、カート金額など)を自動で追加しない
- 開発者のロギングに対する考え方を変えない
- 同じライブラリを使っていても、コンテキストを意図的に追加した計測と単純な計測とでは、デバッグ体験に大きな差が出る
- OTel は単なる**伝送手段(plumbing)**であり、何を流すかは開発者が決める必要がある
Wide Event / Canonical Log Line 方式
- 従来の「コードが何をするか」中心のロギングから離れ、「リクエストに何が起きたか」を記録すべきである
- 各リクエストごとに、サービス単位で1つの広範なイベントを生成する
- リクエスト・ユーザー・決済・エラー・環境など50以上のフィールドを含められる
- 例の JSON には
user_id、subscription_tier、service_version、error_code などあらゆるデバッグ用コンテキストが含まれる
- これにより、単一の検索で「プレミアムユーザーの決済失敗原因」のような即時分析が可能になる
Wide Event のクエリ活用
- Wide Event は単純なテキスト検索ではなく、構造化データのクエリとして扱う
- 高カーディナリティ・高次元データを基盤に、リアルタイム分析レベルのデバッグが可能
- 例: 「過去1時間のプレミアムユーザーの決済失敗率を、エラーコード別に集計する」といったクエリを即座に実行できる
実装パターン
- リクエストのライフサイクル全体を通じてイベントを構築し、最後に一度だけ出力する
- ミドルウェアで
request_id、timestamp、method、path などの基本フィールドを初期化
- ハンドラーでユーザー・カート・決済・エラー情報を段階的に追加
- 最終的に
logger.info(event) で単一の JSON イベントを記録する
サンプリングによるコスト制御
- 1リクエストあたり50フィールド以上を記録するとコストが急増するため、サンプリングが必要
- 単純なランダムサンプリングではエラーを見逃す危険がある
- Tail Sampling 戦略を提示
- エラー(500 など)は常に保存
- 遅いリクエスト(p99 以上)は常に保存
- VIP ユーザー・特定フラグのセッションは常に保存
- それ以外は 1〜5% だけランダムサンプリング
- これにより、コスト削減と重要イベントの保持を同時に達成できる
よくある誤解の整理
- Structured Logging ≠ Wide Event: JSON フォーマットだけでは不十分で、コンテキストこそが重要
- OpenTelemetry を使うこと ≠ 完全な可観測性の確保: 収集が標準化されるだけで、何を記録するかは開発者次第
- Tracing と同じではない: トレーシングはサービス間の流れを扱い、Wide Event はサービス内部のコンテキストを提供する
- ログはデバッグ用、メトリクスはダッシュボード用という区別は不要 — Wide Event は両方の用途を満たす
- 高カーディナリティデータは高価だという認識は古く、ClickHouse・BigQuery などの現代的な DB はこれを効率的に処理できる
Wide Event 導入の効果
- デバッグが**発掘作業(archaeology)から分析(analytics)**へと変わる
- 「ユーザーの決済失敗」を見つけるために50個のサービスログを grep していた方法から、
「プレミアムユーザーの決済失敗率をエラーコード別に照会する」単一クエリベースの分析へと変化する
- 結果として、ログは嘘をついていた道具から、真実を語るデータ資産へと転換される
1件のコメント
Hacker Newsの意見
文章が読みにくく、AIが手伝ったような印象があった。それでもメッセージには価値があり、もう少し簡潔ならよかったと思う。
最近考えていたことは次のとおり。
この話題なら Charity Majors に触れないわけにはいかない。彼女は「wide events」と「observability」という概念を10年以上広めてきて、Honeycomb.io をその哲学の上に築いた。
今ではさまざまなツールでこの方式を実装できる。structured logs や traces を使って wide event を捉え、時系列・ヒストグラムなど可視化が豊富なツールを使うことが重要だ
この記事の主張には一部同意するが、単一の wide event だけを残す方式には落とし穴がある。リクエストの途中で例外やタイムアウトが起きると、何も残らない。
言語の標準ロギングフレームワークや依存関係のログも取りこぼすことになる。
だからこれは既存ログの上に重ねる 追加レイヤー として使うのがよい。request/session 単位の ID を置き、ClickHouse のようなところで集計すればよい
log.error(data)でもwide_event.attach(error, data)でも本質は同じプレゼンテーションと インタラクティブな例 は素晴らしかった。だが結局は「ログに構造化タグを追加しろ」という話に要約される。
wide log は複雑さと 可読性の低下 のわりに、得られる利点が大きくないと感じる。
単に
grep "uid=user-123" application.logで十分なのに、わざわざユーザーの配送方法まで付ける必要があるのかと思う。(ちなみに Android の Brave ブラウザではチェックボックスが動作しなかった)
grep '"uid": "user-123"'で検索できる。--contextオプションで前後の行も見られる半導体製造の環境で、数千のメッセージバス参加者 がいるシステムを扱っていた。1時間あたり 300〜400MB のログが出ていたが、grep と CLI ツールだけでも十分に管理 できていた。
ログは単なるイベントの時系列であり、詳細な分析は Oracle クエリで処理していた。ログは事象の因果関係を把握するための道具だ
ログは「いつ、何が起きたか」を語り、「なぜ」はコードとデータとイベントの組み合わせから探すものだ
個人的には ELK スタック のようなインターフェースは直感的な探索に不向きだと思う。ログは直感的に追いながら読むことが重要だ
記事の最後にある「すべてのエラー・例外・遅いリクエストをログしろ」という助言は、危険な発想 だ。
たとえば依存先が遅くなると、ログ量が100倍に爆発する可能性がある。
障害時にはサービスはより少ない仕事をしたほうが復旧しやすいのに、ログの爆増はむしろ 連鎖障害 を引き起こす
ログ量が多いほどサンプリング比率が自動調整され、システムが過負荷にならない
現代のソフトウェアでは、単一のログだけで「何が起きたのか」を完全に説明するのは難しい。
だから 垂直相関(Vertical correlation) と 水平相関(Horizontal correlation) が必要になる。
スタック内の上下層のあいだでは同じ correlation 値を共有すべきで、システム間通信ではピア間の correlation が必要だ。
こうした値を API やプロトコルに追加するのは難しいが、トランザクション ID をあらかじめ設計しておけば全体追跡が可能になる
単一の記事のために ドメインを別に登録 するのは持続性が低いと思う。
毎年更新費用がかかるので、個人ブログや サブドメイン を使うほうがよい。
たとえば logging-sucks.boristane.com のような形がよい
「ログはモノリシック時代の遺物だ」という主張については、私は ローカルログは依然として有効 だと思う。
ローカルプロセスのやり取りを記録するのが本来の役割であり、別サーバーの状況を把握するには トランザクショントレーシング が必要だ。
適切な場所のログだけ見ても根本原因にたどり着ける
豊かなコンテキストを持つログは分析エンジンと組み合わせることで、製品改善にも活用できる
「コードが何をするかではなく、リクエストに何が起きたかをログしろ」という言葉には同意するが、著者は経験不足に見えた。
私はこれを 「bug parts logging」 と呼んでいて、処理経路・回数・時間といった 前兆シグナル を含めるべきだと考える。
ロギングはメトリクスや監査(audit)とは異なる。ロギングに失敗しても処理は継続されるべきだが、監査の失敗は致命的だ。
SCADA システムの「historian」という概念のように、観測(observables) と 評価(evaluatives) を区別すべきだ。
たとえば燃料センサーの細かなイベントは診断には有用だが、「目的地まで行けるか」という問いには不要だ。
結局重要なのは、何を観測し、何を評価するのか を明確にすることだ
保存・変換・照会の方法は異なっても、消費地点とメカニズム は同じように設計できる。
こうすればシステム設計が単純になり、長期保存されたログを後から再処理することもできる