監視とオブザーバビリティ入門|メトリクス・ログ・トレースの三本柱を解説

kento_morota 29分で読めます

システムが突然停止して原因がわからない、レスポンスが遅いのにどこがボトルネックか特定できない——こうした課題は、多くの開発・運用チームが直面する共通の悩みです。

本記事では、監視とオブザーバビリティの基本概念から、メトリクス・ログ・トレースの三本柱、主要ツールの選び方、そして実践的な導入手順までを体系的に解説します。これからシステム監視を本格的に始めたい方にとって、最初の一歩となるガイドです。

監視とオブザーバビリティの違いを理解する

「監視(Monitoring)」と「オブザーバビリティ(Observability)」は似たような文脈で使われますが、その考え方には明確な違いがあります。まずはこの2つの概念を正確に理解しましょう。

監視(Monitoring)とは何か

監視とは、あらかじめ定義した条件に基づいてシステムの状態をチェックし、異常を検知する仕組みです。たとえば「CPU使用率が90%を超えたらアラートを出す」「HTTPステータス500が5分間に10回以上発生したら通知する」といった具合に、既知の障害パターンに対して事前にルールを設定します。

監視の特徴は以下のとおりです。

事前定義型のアプローチ
何を監視するかを事前に決めておく必要があります。想定外の障害には対応しにくいという制約があります。

しきい値ベースのアラート
数値がしきい値を超えたかどうかで判断するため、シンプルでわかりやすい仕組みです。

ダッシュボードによる可視化
システムの健全性を一目で把握できるダッシュボードを構築し、運用チームが常時確認できる体制を作ります。

オブザーバビリティ(Observability)とは何か

オブザーバビリティとは、システムの外部出力(メトリクス、ログ、トレース)から内部状態を推測・理解できる能力のことです。制御工学から借用された概念で、「事前に予測できなかった未知の問題」にも対処できることが最大の強みです。

たとえば、あるユーザーだけがページ読み込みに10秒かかるという報告があった場合、監視だけでは原因の特定が困難です。しかしオブザーバビリティが確保されていれば、該当リクエストのトレースをたどり、どのマイクロサービス間の通信でレイテンシが発生しているかを追跡できます。

探索型のアプローチ
事前に問題を想定する必要がなく、データを自由に探索して原因を突き止められます。

相関分析が可能
メトリクス、ログ、トレースを横断的に分析することで、複雑な障害の根本原因を特定できます。

マイクロサービス時代に必須
分散システムでは、1つのリクエストが複数のサービスを経由するため、従来の監視だけでは全体像の把握が難しくなります。オブザーバビリティはこの課題を解決します。

両者の関係性

監視はオブザーバビリティの一部と考えるとわかりやすいでしょう。監視は「既知の問題を検知する」ための手段であり、オブザーバビリティは「未知の問題も含めてシステムを理解する」ための能力です。実際の運用では、両方を組み合わせて使うことが一般的です。

オブザーバビリティの三本柱:メトリクス・ログ・トレース

オブザーバビリティを支える3つの柱について、それぞれの役割と特性を詳しく見ていきましょう。

メトリクス(Metrics)

メトリクスは、時間の経過に伴う数値データです。CPU使用率、メモリ使用量、リクエスト数、レスポンスタイム、エラー率など、システムの状態を定量的に表現します。

メトリクスの最大の利点はデータサイズが小さく、長期間の保持と集約が容易なことです。数百台のサーバーのCPU使用率を1年間分保存してもストレージ容量はわずかで済みます。

代表的なメトリクスの種類は以下のとおりです。

カウンター(Counter)
単調に増加する値です。リクエスト数やエラー数の累計を記録します。リセットは再起動時のみで、一定期間あたりの増加率(rate)を計算して使います。

ゲージ(Gauge)
上下する瞬間値です。CPU使用率やメモリ使用量、現在の同時接続数など、その瞬間の状態を表します。

ヒストグラム(Histogram)
値の分布を記録します。レスポンスタイムの分布を把握する場合などに使い、p50(中央値)やp99(99パーセンタイル)を計算できます。

実際にPrometheusでメトリクスを定義する例を見てみましょう。

// Go言語でPrometheusメトリクスを定義する例
import "github.com/prometheus/client_golang/prometheus"

// リクエスト数を記録するカウンター
var httpRequestsTotal = prometheus.NewCounterVec(
    prometheus.CounterOpts{
        Name: "http_requests_total",
        Help: "HTTPリクエストの合計数",
    },
    []string{"method", "endpoint", "status"},
)

// レスポンスタイムを記録するヒストグラム
var httpRequestDuration = prometheus.NewHistogramVec(
    prometheus.HistogramOpts{
        Name:    "http_request_duration_seconds",
        Help:    "HTTPリクエストの処理時間(秒)",
        Buckets: prometheus.DefBuckets,
    },
    []string{"method", "endpoint"},
)

ログ(Logs)

ログは、特定の時刻に発生したイベントのテキスト記録です。エラーメッセージ、ユーザーの操作履歴、システムの状態変化など、詳細なコンテキスト情報を含みます。

ログの最大の強みは豊富な情報量です。何が起きたかを文脈とともに記録できるため、障害の原因究明に欠かせません。一方で、データ量が膨大になりやすく、検索や分析にコストがかかるという課題もあります。

構造化ログの重要性
従来の非構造化ログ(プレーンテキスト)では、大量のログからの検索が困難です。JSON形式の構造化ログを採用することで、フィールド単位での検索・フィルタリングが可能になります。

// 非構造化ログ(検索しにくい)
2026-03-27 10:15:32 ERROR Failed to process order #12345 for user john_doe

// 構造化ログ(検索しやすい)
{
  "timestamp": "2026-03-27T10:15:32Z",
  "level": "ERROR",
  "message": "Failed to process order",
  "order_id": "12345",
  "user_id": "john_doe",
  "service": "order-service",
  "trace_id": "abc123def456"
}

ログレベルの適切な使い分け
DEBUG、INFO、WARN、ERROR、FATALなどのログレベルを適切に使い分けることで、障害発生時に必要な情報だけを効率よくフィルタリングできます。本番環境ではINFO以上を出力し、障害調査時にDEBUGレベルを一時的に有効にするのが一般的です。

トレース(Traces)

分散トレーシングは、1つのリクエストがシステム内の複数のサービスをどのように経由したかを追跡する仕組みです。マイクロサービスアーキテクチャでは、1回のAPIリクエストが5〜10以上のサービスを呼び出すことも珍しくありません。

トレースは「トレースID」と「スパン」で構成されます。

トレースID
リクエスト全体を一意に識別するIDです。すべてのサービスを通じて同じIDが伝播されます。

スパン(Span)
個々のサービスやオペレーションの処理単位です。スパンには開始時刻、終了時刻、親スパンIDなどの情報が含まれ、リクエストのタイムラインを構築できます。

// OpenTelemetryでスパンを作成する例(Node.js)
const { trace } = require('@opentelemetry/api');

async function processOrder(orderId) {
  const tracer = trace.getTracer('order-service');

  return tracer.startActiveSpan('processOrder', async (span) => {
    span.setAttribute('order.id', orderId);

    try {
      // 在庫確認のスパンを子として作成
      await tracer.startActiveSpan('checkInventory', async (childSpan) => {
        await inventoryService.check(orderId);
        childSpan.end();
      });

      // 決済処理のスパンを子として作成
      await tracer.startActiveSpan('processPayment', async (childSpan) => {
        await paymentService.charge(orderId);
        childSpan.end();
      });

      span.setStatus({ code: SpanStatusCode.OK });
    } catch (error) {
      span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
      throw error;
    } finally {
      span.end();
    }
  });
}

トレースにより「注文処理の合計3秒のうち、決済サービスの呼び出しに2.5秒かかっている」といったボトルネックを明確に可視化できます。

主要なオブザーバビリティツールの比較

オブザーバビリティを実現するためのツールは多数存在します。ここでは代表的なツールを目的別に紹介します。

メトリクス収集・可視化ツール

Prometheus
オープンソースのメトリクス収集・保存システムで、Cloud Native Computing Foundation(CNCF)の卒業プロジェクトです。プル型の収集モデルを採用し、PromQLという独自のクエリ言語でデータを分析できます。Kubernetes環境との親和性が非常に高く、事実上のスタンダードとなっています。

Grafana
メトリクスの可視化に特化したダッシュボードツールです。Prometheusをはじめ、多くのデータソースに対応しています。美しいグラフやアラート機能を備え、運用チームの日常的な監視画面として広く使われています。

Datadog
メトリクス、ログ、トレースを統合的に扱えるSaaS型の監視プラットフォームです。導入が簡単で多機能ですが、データ量が増えるとコストも増加するため、事前のコスト見積もりが重要です。

ログ管理ツール

Elasticsearch + Kibana(ELKスタック)
大量のログを全文検索・分析できる組み合わせです。Elasticsearchがログの保存と検索を担い、Kibanaがビジュアライゼーションを提供します。自前で運用する場合はリソース管理が必要ですが、柔軟性は抜群です。

Grafana Loki
Prometheusのラベルベースのアプローチをログに適用したツールです。ログの全文インデックスを作成しないため、ELKスタックと比べてストレージコストを大幅に削減できます。Grafanaとの統合がシームレスです。

分散トレーシングツール

Jaeger
Uber社が開発したオープンソースの分散トレーシングシステムです。OpenTelemetryとの統合が進んでおり、トレースの可視化とサービス間の依存関係の分析に優れています。

Zipkin
Twitter社が開発した分散トレーシングシステムです。軽量でセットアップが簡単なため、小規模なプロジェクトでの導入に適しています。

統合プラットフォーム:OpenTelemetry

OpenTelemetry(OTel)は、メトリクス・ログ・トレースの計装(instrumentation)を標準化するオープンソースプロジェクトです。特定のベンダーに依存せずにテレメトリデータを収集でき、バックエンドのツールを自由に選択・変更できます。

2026年現在、OpenTelemetryは事実上の業界標準となっており、新規プロジェクトではOTelを基盤とした計装を推奨します。

SREにおける監視のベストプラクティス

Google発祥のSRE(Site Reliability Engineering)の考え方を取り入れた監視のベストプラクティスを紹介します。

SLI/SLO/SLAの設定

効果的な監視を行うためには、まずシステムの信頼性目標を定義する必要があります。

SLI(Service Level Indicator)
サービスの品質を測定する具体的な指標です。たとえば「リクエストのうちレスポンスタイムが200ms以内のものの割合」や「成功したリクエストの割合」がSLIとなります。

SLO(Service Level Objective)
SLIの目標値です。「月間の可用性を99.9%にする」「p99レスポンスタイムを500ms以下にする」などと定めます。

SLA(Service Level Agreement)
ユーザーとの契約で保証するサービスレベルです。SLOよりも低く設定し、SLOに余裕を持たせるのが一般的です。

# SLO定義の例(YAML形式)
slos:
  - name: "APIの可用性"
    description: "全APIリクエストのうち正常にレスポンスを返す割合"
    sli:
      type: "availability"
      good_events: "status_code < 500"
      total_events: "all_requests"
    target: 99.9%    # SLO目標
    window: 30d       # 測定期間

  - name: "APIのレイテンシ"
    description: "APIリクエストのp99レスポンスタイム"
    sli:
      type: "latency"
      threshold: 500ms
    target: 99.0%
    window: 30d

エラーバジェットの考え方

SLOが99.9%の場合、月間の許容ダウンタイムは約43分です。この43分が「エラーバジェット」です。エラーバジェットが残っている間は新機能のリリースを積極的に行い、バジェットを使い切りそうになったら安定性向上に注力するという判断基準になります。

この仕組みにより、開発チームと運用チームの間の「新機能を出したい」「安定性を保ちたい」という対立を、データに基づいて解消できます。

アラート設計のポイント

アラートの設計は監視の中でも特に重要です。以下のポイントを押さえましょう。

症状ベースのアラート
「CPU使用率が高い」(原因ベース)ではなく、「ユーザーのレスポンスタイムが悪化している」(症状ベース)でアラートを設定します。原因ベースのアラートは誤報が多くなりがちです。

アクション可能なアラートだけを設定
アラートを受け取った人が何をすべきか明確でないアラートは、アラート疲れを引き起こします。各アラートにはRunbook(対応手順書)をリンクさせましょう。

重要度の階層化
緊急対応が必要なP1(即時対応)から、次の営業日に対応するP4まで、アラートの重要度を階層化します。深夜に呼び出すのは本当に緊急のものだけにしましょう。

実践:Prometheus + Grafanaで監視基盤を構築する

ここからは、最も一般的な組み合わせであるPrometheus + Grafanaを使った監視基盤の構築手順を解説します。

Docker Composeで環境を構築する

まずはDocker Composeを使って、PrometheusとGrafanaをローカル環境で起動しましょう。

# docker-compose.yml
version: '3.8'

services:
  prometheus:
    image: prom/prometheus:v2.51.0
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.retention.time=30d'

  grafana:
    image: grafana/grafana:10.4.0
    ports:
      - "3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    depends_on:
      - prometheus

  node-exporter:
    image: prom/node-exporter:v1.7.0
    ports:
      - "9100:9100"

volumes:
  prometheus_data:
  grafana_data:

Prometheusの設定

Prometheusの設定ファイルでスクレイプ対象を定義します。

# prometheus/prometheus.yml
global:
  scrape_interval: 15s      # 15秒ごとにメトリクスを収集
  evaluation_interval: 15s   # 15秒ごとにルールを評価

rule_files:
  - "alert_rules.yml"

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['alertmanager:9093']

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'node-exporter'
    static_configs:
      - targets: ['node-exporter:9100']

  - job_name: 'application'
    static_configs:
      - targets: ['app:8080']

アラートルールの定義

Prometheusのアラートルールを定義して、異常を検知できるようにします。

# prometheus/alert_rules.yml
groups:
  - name: application_alerts
    rules:
      - alert: HighErrorRate
        expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "高エラー率を検知"
          description: "5xxエラー率が5%を超えています(現在値: {{ $value | humanizePercentage }})"
          runbook_url: "https://wiki.example.com/runbooks/high-error-rate"

      - alert: HighLatency
        expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 1.0
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "レスポンスタイムの悪化を検知"
          description: "p99レスポンスタイムが1秒を超えています"

Grafanaダッシュボードの構築

Grafanaにログインし、Prometheusをデータソースとして追加したら、以下のようなダッシュボードを構築します。

RED メソッドに基づくダッシュボード
Rate(リクエスト率)、Errors(エラー率)、Duration(処理時間)の3つを中心に可視化するのがREDメソッドです。マイクロサービスの監視に適しています。

USE メソッドに基づくダッシュボード
Utilization(使用率)、Saturation(飽和度)、Errors(エラー)を可視化するUSEメソッドは、インフラリソースの監視に適しています。

PromQLクエリの例を見てみましょう。

# リクエスト率(RPS)
rate(http_requests_total[5m])

# エラー率
rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m])

# p99レスポンスタイム
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))

# CPU使用率
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# メモリ使用率
(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100

OpenTelemetryでアプリケーションを計装する

OpenTelemetryを使ってアプリケーションにメトリクス、ログ、トレースを組み込む方法を紹介します。

Node.jsアプリケーションへの導入

Node.jsアプリケーションにOpenTelemetryを導入する手順を示します。

# 必要なパッケージをインストール
npm install @opentelemetry/sdk-node \
  @opentelemetry/api \
  @opentelemetry/auto-instrumentations-node \
  @opentelemetry/exporter-trace-otlp-http \
  @opentelemetry/exporter-metrics-otlp-http
// tracing.js - アプリケーション起動前に読み込む設定ファイル
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http');
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');

const sdk = new NodeSDK({
  serviceName: 'my-api-service',
  traceExporter: new OTLPTraceExporter({
    url: 'http://localhost:4318/v1/traces',
  }),
  metricReader: new PeriodicExportingMetricReader({
    exporter: new OTLPMetricExporter({
      url: 'http://localhost:4318/v1/metrics',
    }),
    exportIntervalMillis: 30000,
  }),
  instrumentations: [
    getNodeAutoInstrumentations({
      '@opentelemetry/instrumentation-http': { enabled: true },
      '@opentelemetry/instrumentation-express': { enabled: true },
    }),
  ],
});

sdk.start();
console.log('OpenTelemetry SDK initialized');

process.on('SIGTERM', () => {
  sdk.shutdown().then(() => process.exit(0));
});

この設定により、HTTPリクエストやExpressのルーティングが自動的にトレースされます。アプリケーション起動時にこのファイルを先に読み込むことで、自動計装が有効になります。

# アプリケーションの起動コマンド
node --require ./tracing.js app.js

カスタムスパンとメトリクスの追加

自動計装に加えて、ビジネスロジック固有のスパンやメトリクスを追加することも重要です。

const { trace, metrics } = require('@opentelemetry/api');

const tracer = trace.getTracer('order-service');
const meter = metrics.getMeter('order-service');

// カスタムメトリクスの定義
const orderCounter = meter.createCounter('orders_processed_total', {
  description: '処理された注文の合計数',
});

const orderValueHistogram = meter.createHistogram('order_value', {
  description: '注文金額の分布',
  unit: 'JPY',
});

async function createOrder(orderData) {
  return tracer.startActiveSpan('createOrder', async (span) => {
    span.setAttribute('order.customer_id', orderData.customerId);
    span.setAttribute('order.item_count', orderData.items.length);

    // 注文金額を計算
    const totalValue = calculateTotal(orderData.items);
    span.setAttribute('order.total_value', totalValue);

    // メトリクスを記録
    orderCounter.add(1, { status: 'created' });
    orderValueHistogram.record(totalValue);

    // 注文処理のロジック
    const result = await saveOrder(orderData);
    span.end();
    return result;
  });
}

ログの集約と分析基盤の構築

分散システムでは、各サービスから出力されるログを一元管理する仕組みが不可欠です。

構造化ログの実装パターン

アプリケーションで構造化ログを出力するためのパターンを紹介します。

// Node.js + Winston での構造化ログ設定
const winston = require('winston');

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json()
  ),
  defaultMeta: {
    service: 'order-service',
    environment: process.env.NODE_ENV,
  },
  transports: [
    new winston.transports.Console(),
  ],
});

// 使用例
logger.info('注文が作成されました', {
  order_id: '12345',
  customer_id: 'cust_789',
  total_amount: 15000,
  item_count: 3,
});

// エラーログにはスタックトレースを含める
logger.error('決済処理に失敗しました', {
  order_id: '12345',
  error_code: 'PAYMENT_DECLINED',
  error_message: err.message,
  stack: err.stack,
});

ログとトレースの相関付け

ログにトレースIDを含めることで、特定のリクエストに関連するすべてのログを横断的に検索できるようになります。

const { trace } = require('@opentelemetry/api');

function getTraceContext() {
  const span = trace.getActiveSpan();
  if (span) {
    const context = span.spanContext();
    return {
      trace_id: context.traceId,
      span_id: context.spanId,
    };
  }
  return {};
}

// ログ出力時にトレースコンテキストを付与
logger.info('データベースクエリを実行', {
  ...getTraceContext(),
  query: 'SELECT * FROM orders WHERE id = ?',
  duration_ms: 45,
});

これにより、Grafanaでトレースからログへ、ログからトレースへとシームレスに切り替えながら調査できます。

監視・オブザーバビリティ導入のロードマップ

最後に、段階的に監視とオブザーバビリティを導入するためのロードマップを提示します。

フェーズ1:基本的な監視の導入(1〜2週間)

まずは最低限の監視を導入します。

インフラメトリクスの収集
Prometheus + Node Exporterを導入し、CPU、メモリ、ディスク、ネットワークの基本メトリクスを収集します。

基本的なアラートの設定
ディスク使用率90%超過、メモリ使用率95%超過など、致命的な障害につながる条件のアラートを設定します。

ダッシュボードの構築
Grafanaで基本的なダッシュボードを作成し、チーム全員がシステムの状態を確認できるようにします。

フェーズ2:アプリケーション監視の追加(2〜4週間)

インフラの監視に加えて、アプリケーションレベルの監視を追加します。

アプリケーションメトリクスの追加
リクエスト数、エラー率、レスポンスタイムなどのアプリケーションメトリクスを計装します。REDメソッドに基づくダッシュボードを構築しましょう。

構造化ログの導入
ログをJSON形式に統一し、ログ集約基盤(Loki、Elasticsearchなど)に送信します。

SLI/SLOの定義
チームでSLI/SLOを定義し、SLOに基づいたアラートに切り替えます。

フェーズ3:フルオブザーバビリティの実現(1〜2ヶ月)

三本柱を統合し、真のオブザーバビリティを実現します。

分散トレーシングの導入
OpenTelemetryを導入し、サービス間のリクエストフローを可視化します。

相関分析の実現
メトリクス、ログ、トレースをトレースIDで紐付け、Grafanaで横断的な分析ができる環境を構築します。

エラーバジェットの運用開始
SLOに基づくエラーバジェットを計算し、リリース判断に活用します。

継続的な改善のポイント

監視・オブザーバビリティ基盤は、一度構築して終わりではありません。以下の点を定期的に見直しましょう。

アラートの品質レビュー
月に一度、発生したアラートを振り返り、誤報が多いアラートの調整や不足しているアラートの追加を行います。

ダッシュボードの更新
システム構成の変更に合わせて、ダッシュボードを最新の状態に保ちます。使われていないダッシュボードは整理しましょう。

インシデントからの学習
障害が発生した際は、ポストモーテム(振り返り)を実施し、「検知できなかったこと」「検知が遅れたこと」を洗い出して改善します。

まとめ

監視とオブザーバビリティは、信頼性の高いシステム運用に不可欠な要素です。本記事のポイントを振り返りましょう。

監視とオブザーバビリティの違い
監視は既知の問題を検知する仕組み、オブザーバビリティは未知の問題も含めてシステムを理解する能力です。

三本柱の役割
メトリクスは「何が起きているか」を数値で示し、ログは「なぜ起きたか」の詳細を記録し、トレースは「どこで起きたか」をリクエスト単位で追跡します。

段階的な導入が重要
いきなり完璧な基盤を目指すのではなく、基本的な監視から始めて段階的にオブザーバビリティを拡充していくアプローチが現実的です。

OpenTelemetryが標準
ベンダーロックインを避けるために、計装にはOpenTelemetryを採用することを推奨します。

まずはPrometheus + Grafanaで基本的な監視環境を構築し、そこからログ集約、分散トレーシングへと段階的に拡張していくことをおすすめします。システムの信頼性向上は一朝一夕には実現しませんが、着実な一歩を踏み出すことが重要です。

#監視#オブザーバビリティ#SRE
共有:
無料メルマガ

週1回、最新の技術記事をお届け

AI・クラウド・開発の最新記事を毎週月曜にメールでお届けします。登録は無料、いつでも解除できます。

プライバシーポリシーに基づき管理します

起業準備に役立つ情報、もっとありますよ。

まずは話だけ聞いてもらう