FastMCPをOpenTelemetryで監視|ログ・トレース入門

FastMCPをOpenTelemetryで監視し、ログ・トレース・障害を分析する構成 LLM

FastMCPサーバーを公開したあと、「エラーは出ているが、どの処理で失敗したのか分からない」「一部のツールだけ遅い気がする」という状態になっていないでしょうか。

そこで役立つのがOpenTelemetryです。FastMCPにはOpenTelemetryによるトレース計測が組み込まれており、ツール・リソース・プロンプトの呼び出しをSpanとして追跡できます。

この記事では、ローカルのJaegerへトレースを送り、FastMCPの処理時間とエラー箇所を確認するところから、ログ相関、カスタムSpan、本番運用の注意点まで順番に解説します。

この記事の結論
最初は自動計測されたTraceをOTLPでJaegerへ送るだけで十分です。その後、原因調査に必要な箇所へカスタムSpanと構造化ログを追加します。入力本文、トークン、個人情報は記録しません。

FastMCPの自動SpanをOTLP経由で監視基盤へ送る構成

スポンサーリンク

FastMCPの監視で分かること

可観測性(Observability)は、主に次の3要素で構成されます。

種類 分かること FastMCPでの扱い
Trace 1リクエスト内の処理順序、所要時間、失敗箇所 ツールなどの呼び出しを自動計測
Log その時点で起きた個別イベント Pythonのloggingなどを別途設定
Metric エラー率、レイテンシ、件数の時系列傾向 SDKや監視基盤側で別途設定

FastMCPが標準で自動計測する中心はTraceです。アプリケーション独自のログやメトリクスまで自動的に完成するわけではありません。

まずTraceで「どこが遅いか、どこで失敗したか」を見えるようにし、必要になったログとメトリクスだけを追加すると、監視が過剰になりません。

スポンサーリンク

OpenTelemetryとOTLPの役割

OpenTelemetryは、Trace・Log・Metricを共通形式で計測・転送するための標準です。OTLP(OpenTelemetry Protocol)は、そのテレメトリをJaeger、Grafana Tempo、Datadogなどへ送るためのプロトコルです。

典型的な経路は次の通りです。

FastMCP Server
  └─ OpenTelemetry SDK / 自動計測
       └─ OTLP Exporter
            └─ OpenTelemetry Collector または Jaeger
                 └─ 監視画面・検索・アラート

OpenTelemetry SDKを設定していない場合、FastMCPの計測処理は実質的に何も出力しません。そのため、開発初期は計測を組み込んだままにしておき、必要な環境だけExporterを有効化できます。

最短構成:FastMCPのTraceをJaegerで見る

ここでは、ローカル環境で動作を確認します。本番環境へ進む前に、Traceが期待どおり生成され、機密情報が含まれていないことを確認してください。

1. OpenTelemetryをインストールする

FastMCP本体に加えて、OpenTelemetryの自動計測とOTLP Exporterをインストールします。

pip install fastmcp opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install

opentelemetry-bootstrap -a installは、現在のPython環境を調べて対応する計測パッケージを追加します。仮想環境を有効にしてから実行してください。

2. Jaegerを起動する

ローカル確認用にJaegerをDockerで起動します。

docker run --rm -d --name jaeger \
  -p 16686:16686 \
  -p 4317:4317 \
  jaegertracing/all-in-one:latest
  • 16686:JaegerのWeb画面
  • 4317:OTLP/gRPCの受信ポート

ブラウザでhttp://localhost:16686を開くと、Jaegerの画面を確認できます。上のlatestはローカル検証向けです。本番やCIでは再現性を保つため、確認済みのバージョンへ固定してください。

3. FastMCPを自動計測付きで起動する

OTEL_SERVICE_NAME=fastmcp-search \
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4317 \
opentelemetry-instrument fastmcp run server.py --transport http

FastMCPクライアントからツールを呼び出したあと、JaegerのService一覧でfastmcp-searchを選択します。Traceが表示されれば最小構成は完成です。

FastMCPとJaegerを別々のDockerコンテナで動かす場合、127.0.0.1やlocalhostはJaegerを指しません。同じDockerネットワークに接続し、http://jaeger:4317のようにサービス名を指定します。

FastMCPが自動生成するSpan

FastMCPは、主要なMCP操作を次のようなSpan名で記録します。

操作 Span名の例 主に確認する属性
ツール呼び出し tools/call search gen_ai.tool.name、エラー、所要時間
リソース取得 resources/read mcp.resource.uri
プロンプト取得 prompts/get summarize gen_ai.prompt.name
マウント先への委譲 delegate search 委譲先、処理時間

エラーが発生すると、Spanの状態は自動的にERRORになり、例外情報も記録されます。まず赤いSpanを探し、その親子関係と直前の処理を確認するのが基本です。

mcp.method.name、mcp.session.id、fastmcp.server.nameなども調査に役立ちます。ただし、FastMCPのバージョン更新で属性名が変わる可能性があります。古い記事で使われているrpc.*属性ではなく、現在のmcp.*とfastmcp.*属性を基準にしてください。

FastMCPツール実行をSpanへ分解したウォーターフォール

独自処理にカスタムSpanを追加する

自動Spanだけでは、「外部APIの取得」と「ランキング処理」のどちらが遅いかまでは分からないことがあります。その場合、調べたい境界へカスタムSpanを追加します。

from fastmcp import FastMCP
from fastmcp.telemetry import get_tracer

mcp = FastMCP("search-server")
tracer = get_tracer(__name__)

@mcp.tool
def search(query: str, limit: int = 10) -> list[dict]:
    with tracer.start_as_current_span("search.fetch") as span:
        results = fetch_results(query=query, limit=limit)
        span.set_attribute("search.result_count", len(results))

    with tracer.start_as_current_span("search.rank") as span:
        ranked = rank_results(results)
        span.set_attribute("search.ranked_count", len(ranked))

    return ranked

Span名はsearch.fetch、search.rankのように、機能と処理を組み合わせると検索しやすくなります。属性には件数、キャッシュヒット、処理モードなど、集計しやすく安全な値を使います。

次の値は記録しないでください。

  • 検索文やプロンプトの全文
  • APIキー、Cookie、Bearer token
  • メールアドレスなどの個人情報
  • モデルの入力・出力全文
  • データベースの接続文字列

入力内容が必要に見えても、長さ、種別、ハッシュ化された内部IDなどへ置き換えられないかを先に検討します。

プログラム内でOTLP Exporterを設定する

環境変数とopentelemetry-instrumentが使いにくい場合は、Pythonコード内でTracerProviderを構成できます。

from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

provider = TracerProvider()
provider.add_span_processor(
    BatchSpanProcessor(
        OTLPSpanExporter(endpoint="http://localhost:4317")
    )
)
trace.set_tracer_provider(provider)

# OpenTelemetry設定後にFastMCPをimportする
from fastmcp import FastMCP

重要なのは、FastMCPをimportする前にOpenTelemetryを設定することです。また、ネットワーク送信にはSimpleSpanProcessorではなく、処理への影響を抑えやすいBatchSpanProcessorを使います。

本番環境ではコードへ送信先を固定せず、環境変数やSecret管理から渡す方が安全です。

ログとTraceを関連付ける

Traceは処理のつながり、Logはその瞬間の出来事を表します。同じtrace_idとspan_idをログへ含めると、遅いTraceから関連ログへ移動できます。

OpenTelemetryのログ計測を追加する場合は、対応パッケージを導入します。

pip install opentelemetry-instrumentation-logging

アプリケーション側では、検索本文そのものではなく、安全な結果だけを構造化ログへ残します。

import logging

logger = logging.getLogger(__name__)

def record_search_completed(result_count: int, cache_hit: bool) -> None:
    logger.info(
        "search completed",
        extra={
            "result_count": result_count,
            "cache_hit": cache_hit,
        },
    )

Trace・Metric・LogをOTLPへ送る起動例は次の通りです。

opentelemetry-instrument \
  --traces_exporter otlp \
  --metrics_exporter otlp \
  --logs_exporter otlp \
  --service_name fastmcp-search \
  fastmcp run server.py --transport http

OpenTelemetry Python 1.40以降では、ログの自動計測を有効にするための追加フラグは原則不要です。それ以前の環境ではOTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=trueが必要な場合があります。利用中のバージョンに合わせて公式ドキュメントを確認してください。

OTLP/gRPCとOTLP/HTTPの違い

OTLPには主にgRPCとHTTP/protobufがあります。

方式 一般的なポート Endpoint例
OTLP/gRPC 4317 http://collector:4317
OTLP/HTTP 4318 http://collector:4318

gRPCのEndpointへ/v1/tracesを付けないでください。HTTPではExporterがシグナル別パスを補う構成と、OTEL_EXPORTER_OTLP_TRACES_ENDPOINTへ/v1/tracesまで指定する構成があります。

接続できないときは、次を確認します。

  • Exporterと受信側でgRPC/HTTPが一致しているか
  • ポートが4317/4318で混ざっていないか
  • Docker内から到達できるホスト名か
  • CollectorやJaegerがOTLP Receiverを有効にしているか
  • TLSが必要なEndpointへ平文HTTPで送っていないか

本番ではCollectorを挟む

小規模な検証ではFastMCPからJaegerへ直接送信できます。本番ではOpenTelemetry Collectorを挟む構成が扱いやすくなります。

Collectorを使うと、アプリケーションを変更せずに次を調整できます。

  • 送信先の追加・変更
  • バッチ化と再試行
  • 不要な属性の削除
  • サンプリング
  • TLSや認証
  • 複数サービスのテレメトリ統合

アプリケーション側の送信先をCollectorへ統一し、Collectorから監視サービスへ転送すると、ベンダー変更や障害時の切り分けも容易になります。

データ量をサンプリングで抑える

全Traceを保存すると、アクセス増加に合わせてコストも増えます。まず開発環境は100%、本番は10%などから始め、調査に必要な量へ調整します。

export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1

この例は、およそ10%をヘッドサンプリングします。ただし、処理開始時点で保存有無を決めるため、「エラーだけ必ず残す」という動作にはなりません。重要なエラーを残したい場合は、CollectorやバックエンドでTrace完了後に判断するテールサンプリングも検討します。

最初に作るダッシュボードとアラート

最初から多数のグラフを作る必要はありません。次の4項目があれば、主な障害を検出できます。

  1. ツール別の呼び出し回数
  2. ツール別のp95レイテンシ
  3. エラー率とerror.type
  4. テレメトリが突然届かなくなった状態

特に「テレメトリがない」ことも監視対象です。ExporterやCollectorが止まると、サービスが正常なのではなく、単に観測できなくなっている可能性があります。

アラートは、単発エラーではなく一定時間の比率や継続時間を条件にすると、通知疲れを防げます。

OpenTelemetryをテストする

監視設定もコードと同じようにテストできます。FastMCP公式は、テスト用のInMemorySpanExporterを使ってSpanをメモリへ集める方法を案内しています。

確認したいのは次の点です。

  • ツール呼び出しで期待するSpanが作られる
  • 失敗時にSpanがERRORになる
  • カスタム属性が記録される
  • 入力本文や認証情報が属性に含まれない

FastMCP本体のテスト環境をまだ作っていない場合は、先に「FastMCPをpytestでテスト|ツール・リソース・認証の実践例」を参照してください。

セキュリティと運用の注意点

OpenTelemetryは調査を助けますが、記録内容を誤ると機密情報の集積場所にもなります。

  • OTLP Endpointはインターネットへ無制限公開しない
  • Collectorと監視基盤の通信にTLSと認証を使う
  • Span属性とログの許可リストを決める
  • 保存期間と閲覧権限を設定する
  • 開発・検証・本番をservice.nameやResource属性で分離する
  • SDK、Exporter、Collectorのバージョンを固定して更新する
  • 監視基盤への送信障害が本処理を止めないか確認する

認証方式の基本は「FastMCPの認証を実装|Bearer Token・OAuthの基本」、コンテナ運用は「FastMCPをDockerでデプロイ|本番向け構成と設定」もあわせて確認してください。

よくあるトラブル

JaegerにServiceが表示されない

最低1回はツールを呼び出し、Traceを発生させます。そのうえでOTEL_SERVICE_NAME、送信先、ポート、Dockerネットワークを確認します。

Traceはあるが独自処理の内訳が分からない

FastMCPの自動Span配下へ、外部API、データベース、ランキングなどのカスタムSpanを追加します。細かく分割しすぎず、障害原因を切り分けられる境界に絞ります。

ログにtrace_idが入らない

ログ計測パッケージ、OpenTelemetryのバージョン、起動方法を確認します。また、Traceのコンテキスト外で別スレッドやバックグラウンド処理を開始すると、コンテキストの引き継ぎが必要になる場合があります。

監視を入れたら遅くなった

BatchSpanProcessor、サンプリング、Collectorを使い、Span属性のサイズとカスタムSpan数を減らします。Exporterのタイムアウトや再試行も確認してください。

実運用前のチェックリスト

  • □ Jaegerまたは監視基盤でFastMCPのTraceを確認した
  • □ ツール別の所要時間とエラー箇所を追える
  • □ OTLPの方式とポートをgRPC/HTTPで統一した
  • □ Docker内の送信先に適切なサービス名を使った
  • □ 入力本文、トークン、個人情報を記録していない
  • □ 本番ではBatchSpanProcessorとCollectorを検討した
  • □ サンプリング率と保存期間を決めた
  • □ テレメトリ欠損も検知できる
  • □ Spanの生成と機密情報の非記録をテストした

まとめ

FastMCPにはOpenTelemetryのTrace計測が組み込まれているため、OTLP Exporterを設定するだけで、ツール・リソース・プロンプトの処理を可視化できます。

最初の一歩は、ローカルJaegerへTraceを送ることです。その後、原因調査に必要な処理だけへカスタムSpanを追加し、ログをtrace_idで関連付けます。本番ではCollector、サンプリング、TLS、閲覧権限を整えます。

監視は「たくさん記録すること」ではありません。障害の発見と原因特定に必要な情報を、安全な形で残すことが目的です。

まだFastMCPサーバーを作っていない場合は、「PythonでFastMCPサーバーを作る|MCPツール実装入門」から始めると、この記事の構成をそのまま試せます。

参考資料

コメント

タイトルとURLをコピーしました