observability-triage について
Cloudflare Workers Observabilityで本番エラーを調査する開発者向けのスキルです。呼び出し数の正確な集計、ワークフローの失敗調査、プロファイリング、既知のノイズの除外を扱います。
主な機能
- 01Workers Observabilityのクエリ
- 02呼び出し数の正確な集計
- 03ワークフローインスタンスの失敗調査
- 04CPUとヒープのプロファイル取得
- 05既知のランタイムノイズの除外
活用シーン
- 01本番エラーの急増調査
- 02失敗したワークフローステップの追跡
- 03デプロイ間のエラー比較
- 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-apiMCP サーバーはmcp__cloudflare-api__executeにaccountIdを事前バインドし、npx wrangler whoamiでその値を表示できます。トリアージ対象の Worker は、このリポジトリがデプロイするものです(deploy/alchemy/alchemy.run.tsを参照):メインアプリの Worker と補助 Worker(audit engine、landing、self-host)。 cloudflare-apiMCP サーバー(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 秒にしてください。
-
cfCLI でキャプチャします(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.pprofAPI で同等の操作を行う場合:
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 し、上記のランタイムとアプリの判別基準を確認してください)と、誤った状態が残っていないことを確認してからにしてください。各項目には識別用のマーカー(安定している場合はフィンガープリント)と、実際に注視すべき兆候を必ず記載してください。