mcpField
O
observability-triage

observability-triage

github.com/every-appクラウド・DevOps25.5k195 回インストール2 日前に更新
リポジトリ

observability-triage について

Cloudflare Workers Observabilityで本番エラーを調査する開発者向けのスキルです。呼び出し数の正確な集計、ワークフローの失敗調査、プロファイリング、既知のノイズの除外を扱います。

主な機能

  1. 01Workers Observabilityのクエリ
  2. 02呼び出し数の正確な集計
  3. 03ワークフローインスタンスの失敗調査
  4. 04CPUとヒープのプロファイル取得
  5. 05既知のランタイムノイズの除外

活用シーン

  1. 01本番エラーの急増調査
  2. 02失敗したワークフローステップの追跡
  3. 03デプロイ間のエラー比較
  4. 04稼働中の本番アイソレートの分析

MCP 対応

cloudflare-api MCP サーバーを使って Cloudflare Workers Observability をクエリします。ツールが利用できない場合の認証手順も README に記載されています。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/every-app/open-seo --skill observability-triage

インストール後、Claude Code で/observability-triageを入力して読み込まれたか確認します。

詳細

可観測性のトリアージ

Cloudflare Workers Observability にクエリを実行して、本番環境のエラーを正しくカウントし、調査済みで行き詰まったノイズは除外します。以下の既知のノイズ一覧を適用し、該当する項目は再調査しないでください。

アクセス

  • アカウントは実行時に解決してください。ハードコードしないでください。cloudflare-api MCP サーバーは mcp__cloudflare-api__execute に accountId を事前バインドし、npx wrangler whoami でその値を表示できます。トリアージ対象の Worker は、このリポジトリがデプロイするものです(deploy/alchemy/alchemy.run.ts を参照):メインアプリの Worker と補助 Worker(audit engine、landing、self-host)。
  • cloudflare-api MCP サーバー(mcp__cloudflare-api__execute)経由でクエリを実行してください。ツールがない場合は、認証フローを実行してユーザーに URL を提示してください。wrangler の OAuth トークンでは observability API に 403 でアクセスできません(スコープが不足しています)。wrangler のトークンを使った curl に時間を費やさないでください。
  • 2 つ目のエラーソースは PostHog ですが、exceededMemory/canceled/responseStreamDisconnected の結果は確認できません。Worker の結果に関する質問に回答できるのは、こちらだけです。

クエリのレシピ(検証済みの形式)

POST /accounts/{account_id}/workers/observability/telemetry/query。以下はすべて必須です。API の 400 エラーは原因が分かりにくいため注意してください。

  • queryId: "adhoc" が必須です。期間はエポックミリ秒で指定します:timeframe: { from, to }。

  • 結果ごとに呼び出し数をカウントします:

    {
      "queryId": "adhoc",
      "timeframe": { "from": 0, "to": 0 },
      "parameters": {
        "datasets": ["cloudflare-workers"],
        "filters": [
          { "key": "$metadata.type", "operation": "eq", "value": "cf-worker-event", "type": "string" }
        ],
        "calculations": [{ "operator": "count", "alias": "count" }],
        "groupBys": [
          { "value": "$workers.scriptName", "type": "string" },
          { "value": "$workers.outcome", "type": "string" }
        ],
        "limit": 100
      },
      "view": "calculations",
      "limit": 100
    }
    
  • cf-worker-event フィルターを指定することで、カウント対象が呼び出しになります。これを指定しないと、すべてのログ行がカウントされます。

  • 生サンプル:view: "events"、空の calculations/groupBys、小さな limit を指定します。イベントには $metadata(service、trigger、level、message、fingerprint、requestId)と $workers(outcome、scriptName、scriptVersion、wallTimeMs、cpuTimeMs、eventType)が含まれます。

  • メッセージごとにグループ化したエラーレベルのアプリログ:$metadata.level eq error でフィルターし、$metadata.message でグループ化します。

  • 検証済みのフィルター演算子:eq、neq。パーセンタイルには、"p50" ではなく "median" を指定する必要があります。取得したイベントに対する部分文字列のフィルタリングは、クライアント側で行ってください。

  • 便利な詳細分析:$metadata.trigger(ルート)、$workers.scriptVersion(ウィンドウの途中でデプロイにより発生率が変化したか)、$workers.event.request.headers.user-agent でグループ化します。

カウント時の注意点

  • グループ化された結果はソートされず、実質的に最大約 10 行までに制限されています(limit に関係なく)。 グループが見つからなくても、ゼロとは限りません。エラーの結果を確認するには、エラー行が上位に含まれることを期待するのではなく、$workers.outcome neq ok を追加してください。クライアント側でソートします。
  • 1 つの isolate の停止が、多数のイベントに波及します。 OOM によって isolate が停止すると、その時点で当該 isolate に割り当てられているすべてのリクエストが失敗します。生の OOM イベントをタイムスタンプでクラスタリングしてから、ユーザーへの影響件数を判断してください。
  • メッセージのプレフィックスによってグループが分割されます。 先頭にタイムスタンプが付くログ(better-auth の形式)は、1 つのエラーが N 個の単一カウントのグループに分割されます。メッセージごとにグループ化したカウントでは大幅に過少評価されます。イベントのサンプルを確認し、クライアント側でまとめてください。
  • 本番環境へのデプロイは手動です。メインの修正が完了していても、本番環境でその修正が動作しているとは限りません。修正が効かなかったと判断する前に、$workers.scriptVersion と各スクリプトの modified_on を確認してください。

ワークフローの失敗

  • Error を引数としてログに出すと、メッセージが失われます。 console.error("x failed:", error) は Workers Logs に x failed: at async … と記録されます(スタックフレームのみで、Error: message の行はありません)。ログ出力されたオブジェクト内の Error は {remote: true} または {} にシリアライズされます。スタックを検索して呼び出し元を特定し、新しいログ行の describeError(error)(src/server/lib/errors.ts)か、以下の Workflows インスタンスから実際のメッセージを確認してください。
  • エンジンは、ステップの試行が失敗した理由を記録します。 GET /accounts/{account_id}/workflows/{workflow_name}/instances?status=errored&date_start=<iso> は、エラーになったインスタンスを一覧表示します(ワークフロー名は wrangler.jsonc にあります。監査ワーカーでは ai-visibility-workflow、rank-check-workflow、site-audit-workflow、local-rank-workflow)。GET …/instances/{id} は error と、各試行の error.name/error.message を含むすべてのステップを返します。ステップの失敗メッセージが残るのはここだけです。
  • 試行時の WorkflowInternalError: Attempt failed due to internal workflows error は、アプリケーションコードではなく、エンジン自体の失敗です。 そのステップを実行している呼び出しは、約 30 秒後に canceled として報告されます。これは、Cloudflare のステータスインシデント(Workflows、Durable Objects、または本番環境をホストする Ashburn リージョンのネットワークインシデント)と時期が一致する形で発生します。同じタイミングで、バインディングの create() も拒否されます(at async WorkflowImpl.create (cloudflare-internal:workflows-api…))。コードを読む前に、該当する時間帯の cloudflarestatus.com/history を確認してください。
  • ワークフローの呼び出しが canceled になるのは通常の動作です。 エンジンは、ステップが完了した後、またはスリープ中に呼び出しをキャンセルします(エラーになったインスタンスを伴わない呼び出しが、ワークフローごとに 1 日に数百件あります)。そのため、キャンセルされた呼び出しではなく、エラーになった インスタンス を数えてください。

プロファイリング(CPU とヒープのフレームグラフ)

Cloudflare は、稼働中の本番環境のアイソレートから、オンデマンドで CPU またはメモリ割り当てのプロファイルを取得します(2026-10-09 リリース)。「CPU またはメモリはどこで消費されているか」という疑問に答える場合に限って利用してください。ログや呼び出しメトリクスでは答えられない疑問です。通常のエラーのトリアージでは必要ありません。キャプチャでは、指定した時間枠に、すでに稼働している単一のアイソレートをサンプリングします。時間枠は 10~15 秒にしてください。

  • cf CLI でキャプチャします(npm i -g cf、初回のみ cf login)。pprof protobuf を標準出力に書き込みます(生の API レスポンスは gzip 圧縮されていますが、以下のリーダーはどちらにも対応しています)。

    cf workers versions profile latest --worker-id open-seo --duration-ms 10000 --profile-type cpu > open-seo-cpu.pprof
    cf workers versions profile latest --worker-id open-seo --duration-ms 15000 --profile-type heap > open-seo-heap.pprof
    

    API で同等の操作を行う場合:POST /accounts/{account_id}/workers/workers/{worker_id}/versions/latest/profile に { "duration_ms": 10000, "profile_type": "cpu" } を指定します(worker ID は GET /accounts/{account_id}/workers/workers で取得できます)。ダッシュボードでは同じキャプチャをフレームグラフとして表示します:Worker → Observability → Flamegraph view。

  • Go を使わずに読み取るには、node .agents/skills/observability-triage/scripts/pprof-top.mjs open-seo-cpu.pprof を実行します。最も負荷の高い self フレームと inclusive フレームが出力されます。Google の pprof でもファイルを開けます。

  • トラフィックをすでに処理しているアイソレートのみがサンプリングされます。こちらで新たに起動することはありません。アプリのワーカーは常に条件を満たします。監査ワーカーは監査が実行中でなければサンプル数が 0 になるため、先に監査を開始してください。アイドル状態のプレビューとセルフホストワーカーでは、"The Worker has no loaded isolate in its recent execution locations" または "No recent executions were found" というエラーが発生します。

  • 1 回のキャプチャで対象となるのは、最近実行されたアイソレートの中から選ばれた、1 つのコロにある単一のアイソレートです。そのため、サンプル数の少ないフレームを信頼する前に、キャプチャを繰り返してください。通常の負荷で CPU を 10 秒間キャプチャすると、1 ミリ秒間隔で約 2,000 件のサンプルが得られます。

  • 関数名はソースマップがなくても読み取れます(Vite ビルドでは関数名が保持されます)。ファイル名は index-OuwxKDQa.js のようなチャンクハッシュです。本番環境へのデプロイ時にソースマップは PostHog のエラートラッキングへアップロードされ、デプロイ前に削除されるため、Cloudflare はソースマップを持っておらず、ここでのチャンク名はハッシュのままです。

  • ヒーププロファイルが示すのは、時間枠内に行われたメモリ割り当て(約 512 kB の割り当てごとに 1 つのスタック)であり、保持されているメモリではありません。見つかるのはメモリの頻繁な生成と破棄であり、リークではありません。

  • 呼び出しデータと組み合わせると、どのルートが各フレームのコストを負担しているかを確認できます。cf-worker-event を $metadata.trigger でグループ化し、$workers.cpuTimeMs の sum、median、p90 を確認してください。単一のアイソレートを 10 秒間キャプチャすると、そのアイソレートが処理したリクエストがそのまま反映されます。そのため、対処する前に、ホットフレームの割合をルートの平均値に基づくミリ秒数へ換算してください。2 ミリ秒の認証失敗がプロファイルの大半を占めている場合、全体の 3 分の 1 を占めるフレームでも、リクエストあたりのコストは 1 ミリ秒を大きく下回ります。

既知のノイズ — これらは除外し、再調査しないでください

調査の結果、修正または優先度を下げるべきファーストパーティの出力箇所が存在しないと判明した項目だけを、ここに記載します。それぞれ、再び有意なシグナルとなる条件を 1 つ記載しています。

1. SAM チャットの Durable Object ライフサイクル(クローズコード 1006)
  • メッセージ(1 つの現象を 3 通りでカウント):Connection closed: this Durable Object instance is no longer active. Reconnect or retry the request.(休止/退去)、Durable Object reset because its code was updated.(デプロイ)、および対応する呼び出しサマリー。$metadata.error は close です。
  • 識別方法:eventType: "hibernatableWebSocket"、エントリポイントが SamChatAgent/OnboardingChatAgent、webSocketType: "close", code: 1006, wasClean: false、outcome: "exception"、1 桁の wallTimeMs、cpuTimeMs: 0、スタックなし。フィンガープリントは 3aa4cac26653d09a0a41100a33d413ae(例外)、0ae15457af49b4d9a117eeecf66b040a(サマリー)です。
  • 修正できない理由:DO は JS の実行中に破棄されます。workerd が webSocketClose を配信する時点で IoContext はすでに中断されているため、最初の await は完了しません。partyserver はすでにクローズ処理全体を try/catch しているため、onClose をオーバーライドしても何も捕捉できません。
  • 影響なし:トランスクリプトは DO の SQLite にメッセージごとに保存され、PartySocket は必ず再接続します。また、中断されたターンではクレジット計測(onChatResponse)は実行されません。
  • 実際に注視すべき兆候:デプロイと相関しない継続的な増加です(休止以外の、会話中の退去を意味します)。
2. Network connection lost.
  • 識別方法:フィンガープリント be89d4ff7a64cb4d4dceae0f51cfe708、またはメッセージの完全一致で識別できます。ランタイム生成イベントの形式は、source.level がなく、source.exception があり、$metadata.origin: "fetch" です。これは、すべてのアプリログ(source.level があり、exception がない)とは逆です。
  • 修正できない理由:workerd が記録する、ストリームの途中でクライアントが切断したイベントです。この例外はアプリコードには到達しません。同じ requestId の対応するリクエストイベントは outcome: "ok" です。出力箇所はなく、wrangler.jsonc のオブザーバビリティ設定にもメッセージ単位のフィルターはありません。ストリームハンドラーに try/catch を追加しないでください(形だけの対応です)。
  • 実際に注視すべき兆候:このグループに占める /mcp の割合が増えた場合は、ツール呼び出しのレイテンシに関する症状(クライアントがタイムアウトしてキャンセルしている)として扱い、このロググループではなく /mcp のパフォーマンス対応に回してください。
ランタイムとアプリの判別基準

見慣れないエラーイベントを調査する前に、source.level がなく、source.exception がある場合は、アプリではなく Workers ランタイムが記録したものです。grep で呼び出し箇所を探しても見つかりません。対応するリクエストの outcome を基準に判断してください。

エントリの追加

このリストに項目を追加するのは、ファーストパーティの出力箇所がないこと(メッセージを grep し、上記のランタイムとアプリの判別基準を確認してください)と、誤った状態が残っていないことを確認してからにしてください。各項目には識別用のマーカー(安定している場合はフィンガープリント)と、実際に注視すべき兆候を必ず記載してください。

コメント