本文へ移動

Workers tracing の startSpan() は子 span を持たない、例外記録 API も追加

Workers のカスタム span API 4種の挙動を解説。tracing を有効にしたうえで startSpan() の非 active 性、root span への属性付け、例外記録と end() の呼び出し順を確認する。

Cloudflare Workers のカスタム span で使える OpenTelemetry の span API が増えた。2026年9月25日の changelog に載ったのは tracing.startSpan()、tracing.getActiveSpan()、span.recordException()、span.setAttributes() の4つで、どれも cloudflare:workers から import する tracing を起点に使う。自前の span を仕込むなら、書き始める前に押さえておきたい挙動が二つある。startSpan() で作った span は active span にならないこと、そして getActiveSpan() はカスタム span の外で invocation の root span を返すことだ。

startSpan() の span には何もネストしない

changelog によると、tracing.startSpan(name) は span を作って返すが、その span を active span にはしない。他の span はその下にネストしない。OpenTelemetry の他の環境の感覚で「親 span を開いておけば、その間に作った span は子になる」と考えて書くと、この前提は外れる。

終了は呼び出し側の責任で、処理が終わったら span.end() を呼ぶ。changelog の例では try/finally の finally で end() を呼んでおり、成功しても例外で抜けても必ず閉じる形になっている。end() を呼び忘れた span がトレース上でどう扱われるかは changelog には書かれていない。

使いどころは、例の "load-profile" のように、一つの処理の区間を独立した span として切り出す場面だ。区間の中にさらに階層を作る用途には、この API 単体では向かない。

getActiveSpan() が指すのは「今の span」

tracing.getActiveSpan() は現在 active な span を返す。changelog が挙げる用途は、span オブジェクトを引数で持ち回らずに、ヘルパー関数やライブラリから今の span に注釈を付けることだ。カスタム span の外で呼ぶと、invocation の root span が返る。例のコメントが「Annotate the invocation’s root span」となっているのはこのためで、fetch ハンドラの冒頭で呼べばリクエスト全体に user.id や user.plan を載せられる。

前の節と組み合わせると注意点が一つ出る。startSpan() の span は active にならないので、その区間の中でヘルパーが getActiveSpan() を呼んでも、返るのは startSpan() で作った span ではない。他にカスタム span が開いていなければ root span が返ると読めるが、これは changelog の二つの記述を組み合わせた推測だ。ヘルパーで付けた属性を startSpan() の span に載せたいなら、span オブジェクトを引数で渡すほうが確実である。

例では tracing.getActiveSpan()?.setAttributes(...) とオプショナルチェーンを使っている。getActiveSpan() が undefined などを返す条件があるのかは、changelog の本文からは分からない。

例外を span に残す recordException()

span.recordException(exception) は span に例外イベントを記録する。受け付けるのは Error、文字列、または code・name・message のいずれかを持つオブジェクトだ。TypeScript 版の例では catch 節の err を err as Error とキャストして渡している。

例の catch 節は recordException() の後に throw err で例外を投げ直している。記録するだけでは処理の流れは変わらないので、エラーレスポンスをどう返すかは従来どおり自分で決める。記録によって span のステータスがエラー扱いになるかどうかは changelog に書かれていない。

呼び出し順を例のコードで追う

カスタム span を記録するには、先に Worker の tracing を有効にする。Cloudflare の Custom spans 文書 に従い、Wrangler 設定の observability.traces.enabled が true か確認する。以下は changelog の TypeScript 版の例だ。

import { tracing } from "cloudflare:workers";

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const user = await authenticate(request, env);

    // Annotate the invocation's root span
    tracing.getActiveSpan()?.setAttributes({
      "user.id": user.id,
      "user.plan": user.plan,
    });

    const span = tracing.startSpan("load-profile");
    try {
      return Response.json(await loadProfile(env, user.id));
    } catch (err) {
      span.recordException(err as Error);
      throw err;
    } finally {
      span.end();
    }
  },
};

流れは次のとおりだ。

  1. カスタム span の外で getActiveSpan() を呼び、root span に setAttributes() でまとめて属性を付ける
  2. startSpan("load-profile") で区間を開く
  3. 例外が出たら recordException() で span に記録し、投げ直す
  4. finally で end() を呼んで区間を閉じる

authenticate と loadProfile は例の中で定義されていないアプリ側の関数である。

setAttribute() と setAttributes() はチェーンできる

span.setAttributes(attributes) は複数の属性を一度に設定する。あわせて setAttribute() と setAttributes() が span 自身を返すようになったので、呼び出しをチェーンできる。たとえば tracing.startSpan("load-profile").setAttributes({ ... }) と書けば、作成と属性付けを一つの式で済ませて span を変数に受けられる。

changelog には、これらの API を使うのに必要な compatibility date や wrangler のバージョン、対象プランは書かれていない。導入前に自分の Worker の設定で動くかを確かめたうえで、まずは root span への属性付けと、重い処理一つを startSpan() で囲むところから始めるのが手堅い。

出典 種別について