オブザーバビリティ(可観測性)は、システムが出す情報から内部状態を調べられる性質です。既知の異常を検知するmonitoringだけでなく、事前に用意していなかった問いも調べられる状態を目指します。
メトリクス、ログ、トレースは「3本柱」と呼ばれますが、三種類を保存するだけでは十分ではありません。同じrequestやserviceを共通のcontextで結び、仮説に合うsignalへ移れることが重要です。

三つのsignalが答えやすい問い
| signal | 得意な問い | 代表例 |
|---|---|---|
| Metrics | いつから、どの程度、どこで広がったか | request数、error率、latency、queue長 |
| Logs | 特定時点に何が起き、どんな判断をしたか | error code、状態遷移、構造化event |
| Traces | 一つのrequestがどの処理を通り、どこで待ったか | service間のspan、DB・外部API時間 |
メトリクスは多数の測定を集約するため、全体傾向とalertに向きます。個別requestのbodyを調べるものではありません。
ログはeventの詳細を残せますが、量が多く、自由文だけでは検索しにくくなります。安定したfield名を持つstructured logsを使います。
トレースは一つのrequestの経路をspanの親子関係で表します。分散serviceで待ち時間の場所を探せますが、sampleされていないrequestは残らない場合があります。
共通contextでつなぐ
service AからBを呼ぶ時、trace IDと親span情報を伝播すると、Bのspanを同じtraceへ関連付けられます。logにもtrace_idとspan_idを入れると、traceから該当時刻のlogへ移れます。
{
"timestamp": "2026-04-10T09:31:15Z",
"level": "error",
"service": "checkout-api",
"event": "payment_request_failed",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"span_id": "00f067aa0ba902b7",
"error_code": "provider_timeout"
}
JSONであるだけではstructured logとは言えません。同じfieldが同じ型と意味を持つschemaが必要です。service名、環境、versionなど共通属性もそろえます。
利用者が送るtrace headerをそのまま信頼すると、衝突やlog汚染につながる場合があります。入口で形式を検証し、信頼境界に合わせて新しいtraceを開始するか判断します。
一つのincidentを調べる流れ
状況を「決済画面が時々遅い」とします。
1. Metricsで範囲を知る
checkout APIのrequest数、error率、latency分布を見ます。平均だけでなくp95やp99を確認し、開始時刻、対象region、versionを絞ります。
09:30から checkout latency p95 が0.8秒 -> 4.2秒
error率は変化なし
version=2026.04.10-2で増加
ここでは原因を断定しません。「一部requestの待ち時間が増えた」という観察です。
2. Tracesで遅い区間を探す
同じ時間帯の遅いtraceを選び、checkout全体、DB、payment providerなどのspanを比較します。payment provider spanだけが長ければ、外部呼び出しや接続待ちを仮説にします。
速いtraceと遅いtraceを一件ずつ比較し、特定regionやpayloadだけかも確認します。一つのtraceを全体傾向だと考えません。
3. Logsで判断と失敗条件を見る
遅いspanのtrace_idでlogを検索し、timeout設定、retry回数、providerのresponse分類を確認します。token、カード情報、request body全体を記録してはいけません。
4. 修正後に同じsignalで確認する
timeoutとretryを修正したなら、latency分布、外部span、timeout logを同じ条件で比較します。error率だけ改善しても、fallbackが誤った成功を返していないか、業務指標も見ます。
Metricsのcardinalityを制御する
metricの属性組み合わせ数をcardinalityと呼びます。user_id、request ID、生のURLのように値が増え続ける属性を付けると、集約状態と保存コストが急増します。
良い候補: method, route template, status class, region
避ける候補: user_id, email, raw URL, trace_id
/users/123と/users/456は、/users/{id}というroute templateへ正規化します。個別requestへ移る時はexemplarやtrace linkなど、backendが提供する関連付けを使います。
Logsで秘密情報を残さない
次はlogへ直接書きません。
- password、access token、cookie、Authorization header
- 決済情報、個人番号、秘密鍵
- 必要性のないemail、住所、request body全体
debugのため一時的に増やす場合も、環境、期間、access権、削除方法を決めます。error objectやHTTP clientの自動serializationがheaderを含めることもあるため、出力結果を確認します。
Tracingとsampling
すべてのtraceを保存すると費用が増えます。入口で割合を決めるhead samplingは単純ですが、結果を知る前に捨てるため、まれなerrorを失う可能性があります。収集後にerrorや遅いtraceを優先するtail samplingは、collector側のstateと遅延が増えます。
samplingしてもmetricsによる総数は維持し、error traceを残す方針を決めます。trace内で親子のsampling判断が食い違い、途中のspanだけ欠けないようcontext propagationを確認します。
最小の導入順
- service名、環境、versionを全signalへ付ける
- request数、error率、latencyのmetricsを持つ
- error logを安定したschemaへする
- HTTP・DB・外部APIを自動計装し、traceをつなぐ
- 一件のincidentを三signalで往復できるか試す
- 保存期間、sampling、cardinalityと費用を調整する
OpenTelemetryはsignalの生成・収集・exportを標準化しますが、何を測り、どのfieldを残し、どのalertで行動するかはシステム側で決めます。
よくある誤解
- 三種類を導入すればobservable:相関、schema、調査手順がなければ分断されたdataです。
- logを増やせば原因が分かる:問いに不要なlogは費用とnoiseを増やします。
- 平均latencyが正常なら問題なし:一部利用者の遅さはpercentileで確認します。
- trace IDをmetric labelにする:cardinalityが増えすぎます。
- OpenTelemetryが監視backendである:主にtelemetryのAPI、SDK、protocol、collectorを提供するprojectです。
まとめ
Metricsは広がり、logsは出来事、tracesは一requestの経路を調べるsignalです。共通contextで結び、metricsからtrace、traceからlogへ移り、修正後に同じ指標で確認します。cardinality、秘密情報、samplingを設計して初めて、継続して使えるobservabilityになります。