この記事の要点
• オブザーバビリティの3本柱はメトリクス(集計値)・ログ(イベント記録)・トレース(リクエスト経路)
• 3本柱は相関IDで繋ぎ、相互補完的に活用することで真価を発揮する
• OpenTelemetryによりベンダー非依存の統一的な計装が可能
オブザーバビリティとは
オブザーバビリティ(Observability、可観測性)は、もともと制御理論の用語で「システムの内部状態を、その出力から推論できる度合い」を指します。ソフトウェアの文脈では「本番システムで何が起きているかを、外部から観測できる仕組み」という意味で使われ、特にマイクロサービスやクラウドネイティブ環境において必須の能力です。
オブザーバビリティを実現する手段として広く知られているのが「3 本柱(Three Pillars)」、すなわち メトリクス(Metrics)、ログ(Logs)、トレース(Traces) です。
flowchart LR
App["Application"]
App -->|計装| M["Metrics<br/>(集計値)"]
App -->|計装| L["Logs<br/>(イベント記録)"]
App -->|計装| T["Traces<br/>(リクエスト経路)"]
M --> O["Observability Backend"]
L --> O
T --> O
O --> D["Dashboards / Alerts"]
なぜオブザーバビリティが必要か
モノリスからマイクロサービスへの変化
モノリスでは 1 台のサーバーのログを tail -f するだけで多くのことが分かりました。しかし数十のサービスが連携する現代のシステムでは、1 つのリクエストが複数のサービスとデータベース、メッセージキューを跨いで処理されます。「どこで遅延しているのか」「どのサービスがエラーを返したのか」を追跡するには、構造化された観測データが不可欠です。
モニタリングとの違い
モニタリングは「既知の異常を検知する」ことに焦点を当てます。一方オブザーバビリティは「未知の問題を後から調査できる」ことを目指します。事前に予想していなかった問題でも、データから原因を辿れる状態を作るのがゴールです。
基本原則
3 本柱は相互補完
メトリクスは「全体の傾向」を示し、ログは「個別のイベント」を示し、トレースは「リクエストの経路」を示します。3 つを単独で使うのではなく、組み合わせて初めて真価を発揮します。
相関 ID で繋ぐ
3 本柱は「相関 ID(trace_id、request_id など)」を共有することで結びつきます。たとえばあるリクエストのトレースから対応するログに飛べる、メトリクスのスパイクから該当時間帯のトレースを抽出できる、といった連携が可能になります。
構造化が前提
全てのテレメトリーは構造化されたフォーマット(JSON、Protobuf)で記録します。「文字列としてのログ」では機械処理ができません。
構成要素の詳細
1. メトリクス(Metrics)
メトリクスは時系列の数値データです。CPU 使用率、リクエスト数、エラー率、レイテンシなどを定期的にサンプリングし、集約します。
特徴:
- 容量効率が高い(数値の集計)
- 長期保存に向く
- 集計済みなので個別リクエストの追跡はできない
- アラートやダッシュボードに最適
メトリクスの種類:
- Counter: 単調増加するカウンタ(リクエスト総数)
- Gauge: 上下する値(メモリ使用量)
- Histogram: 値の分布(レイテンシ分布)
- Summary: 分位数の計算(p50, p95, p99)
2. ログ(Logs)
ログは個別のイベント記録です。アプリケーションが「何かをした瞬間」のスナップショットを残します。
特徴:
- 詳細な情報を持てる(コンテキスト・スタックトレース)
- 構造化することで検索・集計が可能
- 容量が大きくなりやすい
- 個別事象の調査に最適
3. トレース(Traces)
トレースは 1 つのリクエストがシステム内を通過する経路を時系列で記録します。各サービスでの処理は「スパン(Span)」として表現され、親子関係を持ちます。
gantt
title 分散トレース例
dateFormat X
axisFormat %L
section Web
request handler :0, 200
section Auth
verify token :20, 50
section DB
query users :60, 120
section Cache
set cache :130, 150
特徴:
- リクエスト全体の流れを可視化
- ボトルネック特定に有効
- サービス間の依存関係も把握可能
- サンプリングが必須(全部記録するとコスト膨大)
実装例(OpenTelemetry + TypeScript)
OpenTelemetry は CNCF 配下のオブザーバビリティ標準で、3 本柱を統一的に扱えます。
実践メモ: 自動計装で広くカバーし、足りない箇所を手動計装で補うアプローチが効率的です。最初から完璧な計装を目指す必要はありません。
初期設定
// telemetry.ts
import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
const sdk = new NodeSDK({
serviceName: "order-service",
traceExporter: new OTLPTraceExporter({
url: "http://collector:4318/v1/traces",
}),
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({
url: "http://collector:4318/v1/metrics",
}),
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
カスタムメトリクスの記録
import { metrics } from "@opentelemetry/api";
const meter = metrics.getMeter("order-service");
const orderCounter = meter.createCounter("orders.created", {
description: "Total number of orders created",
});
const orderLatency = meter.createHistogram("orders.create.duration", {
description: "Order creation latency",
unit: "ms",
});
export async function createOrder(input: unknown): Promise<void> {
const start = Date.now();
try {
// 業務処理
orderCounter.add(1, { result: "success" });
} finally {
orderLatency.record(Date.now() - start);
}
}
カスタムスパンの作成
import { trace, SpanStatusCode } from "@opentelemetry/api";
const tracer = trace.getTracer("order-service");
export async function processOrder(orderId: string): Promise<void> {
await tracer.startActiveSpan("processOrder", async (span) => {
span.setAttribute("order.id", orderId);
try {
await chargePayment(orderId);
await reserveInventory(orderId);
span.setStatus({ code: SpanStatusCode.OK });
} catch (err) {
span.setStatus({
code: SpanStatusCode.ERROR,
message: (err as Error).message,
});
span.recordException(err as Error);
throw err;
} finally {
span.end();
}
});
}
構造化ログとトレースの相関
import { trace } from "@opentelemetry/api";
import pino from "pino";
const logger = pino();
export function logWithTrace(msg: string, attrs: Record<string, unknown> = {}): void {
const span = trace.getActiveSpan();
const ctx = span?.spanContext();
logger.info({
msg,
trace_id: ctx?.traceId,
span_id: ctx?.spanId,
...attrs,
});
}
メリット
- 障害対応の高速化: 原因特定までの時間(MTTR)が短縮される
- 未知の問題に対応可能: 事前定義していない調査ができる
- ボトルネック発見: トレースで遅延箇所が一目瞭然
- 開発フィードバック: 本番の挙動を開発に活かせる
- データ駆動の意思決定: SLO や SLI の根拠が得られる
デメリット
- コスト: テレメトリーの保存・転送・処理に費用がかかる
- 計装の手間: コードに観測ポイントを埋め込む必要がある
- データ過多: 何でも記録すると分析できなくなる
- 学習コスト: ツールの組み合わせと運用ノウハウが必要
- プライバシー: 個人情報をログに残さない設計が必要
ユースケース
- マイクロサービスの障害調査: サービス間のレイテンシ・エラー追跡
- SLO 管理: エラーバジェット消費率の継続監視
- パフォーマンス改善: ボトルネック特定とリリース後の効果測定
- キャパシティプランニング: トラフィック傾向分析
- セキュリティ監査: 不審なアクセスパターンの検出
よくある落とし穴
3 本柱を別々に運用
メトリクス、ログ、トレースが相関できないと、それぞれの便利さが半減します。trace_id を共通キーとして全テレメトリーに付与します。
注意: メトリクスのラベルにユーザーIDやリクエストIDのような高カーディナリティ値を入れると、時系列数が爆発的に増加しバックエンドが破綻します。
サンプリング設定の不備
トレースを 100% 記録するとコストが爆発します。一方で 0.1% にすると稀なエラーが見つかりません。テールベースサンプリングなどを検討します。
個人情報の漏洩
メールアドレスやクレジットカード番号をログに書くのは厳禁です。マスキング処理を必須化します。
ベンダーロックイン
特定の SaaS 専用 SDK で計装すると移行が困難になります。OpenTelemetry のような中立な計装を選択します。
アラート疲れ
不要なアラートを大量に設定すると、本当の異常が埋もれます。SLO ベースで意味のあるアラートに絞ります。
他概念との比較
| 用語 | 焦点 | 主な質問 |
|---|---|---|
| Monitoring | 既知の異常検知 | 「何が壊れたか?」 |
| Observability | 未知の問題の調査 | 「なぜ壊れたか?」 |
| APM | アプリケーション性能監視 | 「どこで遅いか?」 |
| Logging | イベント記録 | 「何が起きたか?」 |
| Profiling | コードレベルの詳細解析 | 「どの関数が重いか?」 |
ベストプラクティス
1. OpenTelemetry を採用する
ベンダー中立な標準を使うことで、将来の移行が容易になります。
2. 構造化ログを徹底する
JSON など機械処理可能な形式で出力し、必要なフィールド(service、env、version、trace_id)を必ず含めます。
3. SLI と SLO を定義する
「成功率 99.9%」「p99 レイテンシ 500ms」など、ビジネスに直結する指標を決め、それを軸に観測します。
4. 相関 ID を全層で伝播する
HTTP ヘッダーやメッセージキューのプロパティを通じて trace_id を伝播させます。
5. 計装はライブラリ任せから始める
自動計装で広くカバーし、足りない箇所を手動計装で補います。
6. データ保持期間を設計する
長期保存は集約値、詳細データは短期で、というように階層化してコストと有用性のバランスを取ります。
まとめ
オブザーバビリティの 3 本柱(メトリクス・ログ・トレース)は、それぞれ得意とする情報の粒度と用途が異なります。3 つを連携させ、共通の相関 ID で結ぶことで、分散システムでも「内部で何が起きているか」を把握できるようになります。OpenTelemetry の登場により、計装の標準化とベンダー非依存が現実的になりました。本番環境を未知の世界にしないため、設計段階からオブザーバビリティを組み込みましょう。
ポイント: オブザーバビリティ成熟度は段階的に向上させるものです。まずはレベル2(集約ログ+基本メトリクス)を目指し、徐々にトレーシングやSLOを追加していきましょう。
さらに踏み込んだトピック
第 4 の柱としてのプロファイル
近年、コンティニュアスプロファイリング(Continuous Profiling)が「第 4 の柱」として注目されています。CPU やメモリの使用状況を関数レベルで継続的に記録することで、メトリクスでは「全体が遅い」までしか分からない問題を「この関数が原因」というレベルまで掘り下げられます。Pyroscope、Parca、Google Cloud Profiler などが代表的な実装です。
サンプリング戦略
トレースは全て収集すると保存コストとパフォーマンス影響が大きいため、サンプリングが必須です。
- Head-based sampling: トレース開始時に確率で決定。シンプルだが稀なエラーを取りこぼす
- Tail-based sampling: トレース完了後にエラーや高レイテンシを基準に選別。コレクタ側で実装が必要
- Adaptive sampling: 流量に応じて動的に確率を調整
本番では Tail-based を推奨することが多いですが、コレクタの実装難易度は高くなります。
Exemplars
Prometheus のメトリクスに、特定のサンプル時刻のトレース ID を紐づける機能を Exemplar と呼びます。「99 パーセンタイルのレイテンシが上がった」というメトリクスのスパイクから、ワンクリックで該当するトレースに飛べるようになります。
SLO・SLI・SLA の関係
- SLI(Service Level Indicator): 計測する指標(例: 成功リクエストの割合)
- SLO(Service Level Objective): 目標値(例: 99.9%)
- SLA(Service Level Agreement): 顧客との契約(例: 99.5% を割ったら返金)
オブザーバビリティは SLI を計測する基盤であり、SLO を継続的に評価する手段になります。
エラーバジェット
SLO を 99.9% と設定した場合、月間 0.1% の失敗が許容されます。これを「エラーバジェット」と呼び、開発速度と信頼性のバランスを取る指標になります。バジェットが残っていればリスクのあるリリースができ、使い切れば信頼性向上に注力する、という意思決定に使えます。
OpenTelemetry Collector
各サービスから直接バックエンドに送るのではなく、OpenTelemetry Collector を介すると、サンプリング、フィルタリング、変換、複数バックエンドへのファンアウトが一元化できます。アプリケーションは Collector にだけ送り、運用側でルーティングを決められるためベンダー切替が容易です。
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
http:
processors:
batch:
tail_sampling:
policies:
- name: errors
type: status_code
status_code: { status_codes: [ERROR] }
- name: slow
type: latency
latency: { threshold_ms: 1000 }
exporters:
otlphttp:
endpoint: https://backend.example.com
service:
pipelines:
traces:
receivers: [otlp]
processors: [tail_sampling, batch]
exporters: [otlphttp]
高度なトピック
USE メソッドと RED メソッド
代表的なメトリクス設計フレームワークとして 2 つあります。
- USE メソッド(Brendan Gregg 提唱): リソース指向。Utilization(使用率)、Saturation(飽和度)、Errors(エラー)。インフラ・OS のリソース監視に向く。
- RED メソッド(Tom Wilkie 提唱): サービス指向。Rate(リクエスト数)、Errors(エラー数)、Duration(レイテンシ)。マイクロサービスの監視に向く。
両者は対立せず補完的です。インフラには USE、サービスには RED を適用するのが定石です。
高カーディナリティ属性の罠
メトリクスのラベルにユーザー ID やリクエスト ID のような高カーディナリティ値を入れると、時系列数が爆発的に増えバックエンドが破綻します。高カーディナリティ情報はログまたはトレースに置き、メトリクスはサービス名・エンドポイント・ステータスコードなど少数のラベルに留めます。
Cardinality Explosion の検知
Prometheus では prometheus_tsdb_head_series などの内部メトリクスでカーディナリティを監視できます。一定の閾値を超えたらアラートを発報し、原因となるメトリクスを特定する運用が必要です。
構造化ログのスキーマ統一
サービスごとにログのフィールド名がバラバラだと検索性が落ちます。組織共通のログスキーマ(OpenTelemetry Logs Data Model や Elastic Common Schema など)を採用するとクエリが書きやすくなります。
{
"timestamp": "2026-04-10T12:34:56.789Z",
"severity": "ERROR",
"service.name": "order-service",
"service.version": "1.4.2",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"span_id": "00f067aa0ba902b7",
"event": "order.failed",
"order.id": "ord-001",
"error.type": "ValidationError",
"error.message": "amount must be positive"
}
ログレベルの運用ガイドライン
| レベル | 用途 |
|---|---|
| ERROR | 即時対応が必要な障害 |
| WARN | 異常だが即時対応不要、傾向を見るべき事象 |
| INFO | 重要な業務イベント(注文作成、支払い完了など) |
| DEBUG | 開発時のみ有効化する詳細情報 |
| TRACE | 関数呼び出しレベルの極めて詳細な情報 |
本番では INFO 以上に絞り、必要に応じて DEBUG を一時有効化する運用が推奨されます。
Logs to Metrics 変換
特定パターンのログ件数をメトリクス化することで、ログ基盤とメトリクス基盤を橋渡しできます。OpenTelemetry Collector の logstransform や Loki の Recording Rule で実現できます。「ERROR ログの発生率」をアラート化する典型例です。
Real User Monitoring との接続
サーバー側だけでなく、ブラウザやモバイルアプリでの実ユーザー体験(Core Web Vitals、エラー、レイテンシ)を計測することで、エンドツーエンドのオブザーバビリティが完成します。OpenTelemetry にはブラウザ用の SDK もあり、サーバートレースとフロントエンドトレースを同じ trace_id で繋げます。
オブザーバビリティ成熟度モデル
組織のオブザーバビリティ成熟度を 5 段階に分けると、現在地と次の目標が見えやすくなります。
レベル 1: Reactive
- ログファイルを SSH で見る
- 障害が起きてから調査
- メトリクスはサーバー単位の CPU/メモリのみ
レベル 2: Proactive
- 集約ログ基盤を導入(ELK、Loki など)
- 主要サービスにメトリクスを設定
- 簡単なダッシュボードを構築
- 基本アラートを運用
レベル 3: Distributed
- 分散トレーシングを導入
- 構造化ログに移行
- 相関 ID で 3 本柱を結ぶ
- 主要サービスで SLO を定義
レベル 4: Data-Driven
- エラーバジェットで開発判断
- カオスエンジニアリングを実施
- ダッシュボードがリリース判断に使われる
- ML/異常検知を活用
レベル 5: Embedded
- すべてのサービスが OpenTelemetry で計装
- 開発フェーズから観測ポイントが組み込まれる
- 障害ポストモーテムが文化として根付く
- ビジネス指標とオブザーバビリティが連動
各レベルを順番に積み上げ、組織の現在地に合った投資を選びましょう。
設計時のチェックリスト
- 新サービスは OpenTelemetry の自動計装で計装されているか
- 構造化ログに
trace_idspan_idservice.nameが含まれているか - カーディナリティの高いラベルがメトリクスに混入していないか
- 個人情報のフィルタリング処理は組み込まれているか
- SLI/SLO は業務的に意味のある指標で定義されているか
- アラートは SLO ベースで、ノイズが抑制されているか
- ログ・トレースの保持期間とコストは設計されているか
- インシデント対応時に必要な情報がすべて取得可能か
- 開発環境で計装が動作することを確認しているか
- ダッシュボードがチームに共有されているか
これらを定期的に見直し、運用に組み込むことが成熟度を上げる鍵です。
参考リソース
- OpenTelemetry - Observability primer
- Google SRE Book - Monitoring Distributed Systems
- CNCF - Observability Whitepaper
- Distributed Systems Observability (Cindy Sridharan, O’Reilly)