← Cloudflare Workers / workers / observability / traces
Vlastní spany
Cloudflare Workers automaticky instrumentuje operace platformy, jako jsou volání fetch, čtení z KV a dotazy D1. Vlastní spans umožňují tuto viditelnost rozšířit i na vaši aplikační logiku, takže můžete trasovat vlastní cesty kódu společně s vestavěnou instrumentací.
API pro vlastní spans je k dispozici dvěma způsoby, oba nabízejí stejné metody a chovají se identicky:
import { tracing } from "cloudflare:workers", works anywhere in your codebase, including utility functions, libraries, and modules that do not have access to the handler context.ctx.tracing: dostupné naExecutionContextpředaný do vašeho handleru, což se hodí, pokud už s handlerem pracujete.
Existují dvě metody vytváření spanů:
enterSpan(): vytvoří span, který automaticky skončí, jakmile se callback vrátí nebo se vyřeší jím vrácený promise. Použijte to pro většinu instrumentace.startActiveSpan(): vytvoří span, který ukončíte ručně volánímspan.end(). Použijte to, když span musí přežít callback, například při instrumentaci streamů nebo jiných dlouhotrvajících operací.
Povolit trasování
Vlastní spany vyžadují, aby bylo u vašeho Workeru zapnuté trasování. Pokud jste to ještě neudělali, nastavte observability.traces.enabled na true ve vašem Konfigurační soubor Wrangler:
{
"$schema": "./node_modules/wrangler/config-schema.json",
"observability": {
"traces": {
"enabled": true
}
}
}[observability.traces]
enabled = trueVytvořte vlastní span
Použijte tracing.enterSpan() k obalení části kódu pojmenovaným spanem. Span se automaticky stane potomkem toho spanu, který je aktuálně aktivní, a končí ve chvíli, kdy callback vrátí hodnotu nebo se vrácený promise vyřeší.
Následující příklad používá oba přístupové způsoby, konkrétně cloudflare:workers import a ctx.tracing, to show that they are interchangeable:
import { tracing } from "cloudflare:workers";
export default {
async fetch(request, env, ctx) {
// Using the import
return tracing.enterSpan("handleRequest", async (span) => {
span.setAttribute("url.path", new URL(request.url).pathname);
const user = await ctx.tracing.enterSpan("auth", async () => {
// Using ctx.tracing
return authenticate(request, env);
});
return buildResponse(user);
});
},
};import { tracing } from "cloudflare:workers";
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
// Using the import
return tracing.enterSpan("handleRequest", async (span) => {
span.setAttribute("url.path", new URL(request.url).pathname);
const user = await ctx.tracing.enterSpan("auth", async () => {
// Using ctx.tracing
return authenticate(request, env);
});
return buildResponse(user);
});
},
};Referenční dokumentace API
tracing.enterSpan(name, callback, ...args)
Vytvoří nový span a spustí callback uvnitř něj. Span se automaticky ukončí, jakmile callback vrátí hodnotu (synchronně nebo asynchronně) nebo vyvolá výjimku.
Parametry:
| Parametr | Typ | Popis |
|---|---|---|
name |
string |
Název spanu. Zobrazuje se ve vizualizacích trasování. |
callback |
(span: Span, ...args: A) => T |
Funkce, která se má spustit v rámci spanu. Přijímá Span objekt jako první argument, následovaný dalšími argumenty předanými do enterSpan. |
...args |
A |
Volitelné další argumenty předávané callbacku za span . |
Vrací: Návratová hodnota callback.
Chování:
- Nový span je potomkem toho spanu, který je aktuálně aktivní v asynchronním kontextu. Pokud není aktivní žádný span, stane se potomkem kořenového spanu požadavku.
- Vnořené
enterSpanvolání a spanů vytvořených za běhu (jako jsoufetchnebo operace KV) spuštěné uvnitř callbacku se automaticky stanou potomky tohoto spanu. - Span končí, když callback vrátí hodnotu synchronně, synchronně vyvolá výjimku, nebo když se jeho vrácený promise splní nebo odmítne.
// Synchronous callback — span ends when the function returns
const result = tracing.enterSpan("parse", (span) => {
span.setAttribute("format", "json");
return JSON.parse(body);
});
// Async callback — span ends when the promise settles
const data = await tracing.enterSpan("fetchData", async (span) => {
const res = await fetch("https://api.example.com/data");
span.setAttribute("http.response.status_code", res.status);
return res.json();
});
// Forwarding arguments
const doubled = tracing.enterSpan("compute", (span, x) => x * 2, 21);tracing.startActiveSpan(name, callback, ...args)
Vytvoří nový span, učiní ho aktivním spanem, zatímco callback se spustí a vrátí výsledek callbacku bez automaticky ukončuje span. Musíte zavolat span.end() explicitně, jakmile je operace dokončena.
Parametry:
| Parametr | Typ | Popis |
|---|---|---|
name |
string |
Název spanu. Zobrazuje se ve vizualizacích trasování. |
callback |
(span: Span, ...args: A) => T |
Funkce, která se má spustit, dokud je span aktivní. Přijímá Span objekt jako první argument, následovaný dalšími argumenty. |
...args |
A |
Volitelné další argumenty předávané callbacku za span . |
Vrací: Návratová hodnota callback.
Chování:
- Na rozdíl od
enterSpan, span je ne automaticky ukončen v okamžiku, kdy callback vrátí hodnotu nebo vyvolá výjimku. Za voláníspan.end(). - Pokud zapomenete zavolat
span.end(), span se jako záloha stále odešle ve chvíli, kdy je zničen objekt spanu vlastněný požadavkem. Nespoléhejte se na toto chování, vždy volejtespan.end()explicitně.
Použijte startActiveSpan když potřebujete, aby span pokrýval operaci přesahující jediný callback, například při instrumentaci stream pipeline, kde má span zůstat otevřený, dokud není stream zcela zpracován:
import { tracing } from "cloudflare:workers";
export default {
async fetch(request, env, ctx) {
const body = request.body;
if (!body) return new Response("No body", { status: 400 });
// The span is active during the callback, so the pipeThrough
// operation is correctly nested. The span stays open after
// the callback returns, until flush() calls span.end().
const stream = tracing.startActiveSpan("process-stream", (span) => {
span.setAttribute(
"request.content_type",
request.headers.get("content-type") ?? "unknown",
);
return body.pipeThrough(
new TransformStream({
transform(chunk, controller) {
// Process each chunk
controller.enqueue(chunk);
},
flush() {
span.setAttribute("stream.status", "complete");
span.end();
},
cancel() {
span.setAttribute("stream.status", "cancelled");
span.end();
},
}),
);
});
return new Response(stream);
},
};import { tracing } from "cloudflare:workers";
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const body = request.body;
if (!body) return new Response("No body", { status: 400 });
// The span is active during the callback, so the pipeThrough
// operation is correctly nested. The span stays open after
// the callback returns, until flush() calls span.end().
const stream = tracing.startActiveSpan("process-stream", (span) => {
span.setAttribute(
"request.content_type",
request.headers.get("content-type") ?? "unknown",
);
return body.pipeThrough(
new TransformStream({
transform(chunk, controller) {
// Process each chunk
controller.enqueue(chunk);
},
flush() {
span.setAttribute("stream.status", "complete");
span.end();
},
cancel() {
span.setAttribute("stream.status", "cancelled");
span.end();
},
}),
);
});
return new Response(stream);
},
};Referenci na span můžete také zachytit pro pozdější použití bez streamů:
let capturedSpan;
const value = tracing.startActiveSpan("manual-operation", (span) => {
capturedSpan = span;
span.setAttribute("phase", "started");
return computeResult();
});
// The span is still open here — you can set more attributes
capturedSpan.setAttribute("phase", "complete");
capturedSpan.end(); // Now the span is submittedSpan
Span objekt je předán do enterSpan a startActiveSpan callbacky. Poskytuje metody pro doplnění spanu o metadata a řízení jeho životního cyklu.
span.setAttribute(key, value)
Nastaví atribut spanu.
| Parametr | Typ | Popis |
|---|---|---|
key |
string |
Název atributu. |
value |
string | number | boolean | undefined |
Hodnota atributu. Předání undefined nemá žádný efekt (no-op). |
Atributy se zobrazují spolu se span ve vašich trasování a exportech OpenTelemetry.
span.setAttribute("user.plan", "enterprise");
span.setAttribute("item.count", 42);
span.setAttribute("cache.hit", true);span.isTraced
A readonly boolean označující, zda se toto vyvolání trasuje. Pokud požadavek není vzorkován (na základě vašeho head_sampling_rate), isTraced je false a enterSpan stále spustí callback, ale nezaznamená žádnou telemetrii.
Toho můžete využít k přeskočení nákladného výpočtu atributů, pokud požadavek není trasován:
tracing.enterSpan("process", (span) => {
if (span.isTraced) {
span.setAttribute(
"request.body.preview",
JSON.stringify(body).slice(0, 200),
);
}
return processBody(body);
});span.end()
Ukončí span a odešle jeho atributy do systému trasování. Tato metoda je idempotentní, opakované volání po prvním volání nemá žádný účinek. Po end() je volána, span.isTraced vrací false a jakékoli další setAttribute volání se tiše ignorují, včetně volání z asynchronní práce na pozadí, která ještě nebyla dokončena.
- U spanů vytvořených pomocí
enterSpan, nemusíte volatend(), the runtime calls it automatically. Callingend()sami je bezpečné, ale nemá to žádný účinek, protože runtime span již ukončil. - U spanů vytvořených pomocí
startActiveSpan, vy musí voláníend()pro odeslání spanu.
let mySpan;
const result = tracing.startActiveSpan("manual-op", (span) => {
mySpan = span;
span.setAttribute("step", "processing");
return doWork();
});
// Later, when the work is truly complete:
mySpan.end(); // Span is submitted
mySpan.end(); // No-op, safe to call againVnořené spany
Spany se automaticky vnořují podle asynchronního kontextu JavaScriptu. Jakékoli enterSpan volání nebo operaci platformy (jako je fetch a env.MY_KV.get()) který běží uvnitř callbacku, se stává potomkem obklopujícího spanu.
import { tracing } from "cloudflare:workers";
async function handleOrder(env, orderId) {
return tracing.enterSpan("handleOrder", async (span) => {
span.setAttribute("order.id", orderId);
// This KV read is automatically a child of "handleOrder"
const order = await env.ORDERS_KV.get(orderId, "json");
// This nested span is also a child of "handleOrder"
const total = tracing.enterSpan("calculateTotal", (innerSpan) => {
innerSpan.setAttribute("item.count", order.items.length);
return order.items.reduce((sum, item) => sum + item.price, 0);
});
// This fetch is a child of "handleOrder"
await fetch("https://api.example.com/notify", {
method: "POST",
body: JSON.stringify({ orderId, total }),
});
return new Response(JSON.stringify({ orderId, total }));
});
}import { tracing } from "cloudflare:workers";
async function handleOrder(env: Env, orderId: string) {
return tracing.enterSpan("handleOrder", async (span) => {
span.setAttribute("order.id", orderId);
// This KV read is automatically a child of "handleOrder"
const order = await env.ORDERS_KV.get(orderId, "json");
// This nested span is also a child of "handleOrder"
const total = tracing.enterSpan("calculateTotal", (innerSpan) => {
innerSpan.setAttribute("item.count", order.items.length);
return order.items.reduce(
(sum: number, item: any) => sum + item.price,
0,
);
});
// This fetch is a child of "handleOrder"
await fetch("https://api.example.com/notify", {
method: "POST",
body: JSON.stringify({ orderId, total }),
});
return new Response(JSON.stringify({ orderId, total }));
});
}
Protokolování v rámci spanů
console.log() a další metody konzole vysílají log eventy, které jsou automaticky přiřazeny aktuálně aktivnímu spanu. To znamená, že výstup protokolu zevnitř enterSpan nebo startActiveSpan callback je přiřazen k danému spanu ve vašich traces a exportech OpenTelemetry.
tracing.enterSpan("processPayment", async (span) => {
console.log("Starting payment processing"); // attributed to "processPayment"
const result = await chargeCard(token, amount);
console.log("Payment complete", result.id); // also attributed to "processPayment"
});Typy TypeScript
Kompletní deklarace typů pro API vlastních spanů:
declare module "cloudflare:workers" {
namespace tracing {
function enterSpan<T, A extends unknown[]>(
name: string,
callback: (span: Span, ...args: A) => T,
...args: A
): T;
function startActiveSpan<T, A extends unknown[]>(
name: string,
callback: (span: Span, ...args: A) => T,
...args: A
): T;
}
class Span {
readonly isTraced: boolean;
setAttribute(
key: string,
value: string | number | boolean | undefined,
): void;
end(): void;
}
}Stejné API je k dispozici v kontextu handleru jako ctx.tracing, se stejnými typy.
Volba mezi enterSpan a startActiveSpan
enterSpan |
startActiveSpan |
|
|---|---|---|
| Span končí | Automaticky ve chvíli, kdy callback vrátí hodnotu, vyhodí výjimku, nebo se jeho vrácený promise vypořádá | Ručně, při volání span.end() |
| Rozsah aktivního kontextu | Během callbacku | Během callbacku |
| Případ použití | Většina instrumentace: synchronní i asynchronní práce, která se vejde do jednoho callbacku | Operace, které přetrvávají déle než callback, například stream pipelines |
| Zpracování chyb | Span se při throw automaticky ukončí | Span zůstává otevřený i při throw, zavolejte span.end() nebo se spolehnout na pojistku runtime |
Obě metody nastaví span jako rodiče aktivního kontextu pouze během callbacku. Jakmile se callback vrátí, span přestává být aktivním rodičem. S enterSpan, na tomto rozdílu nezáleží, protože span je také ukončen. S startActiveSpan, span zůstává otevřený, ale už není nadřazeným kontextem. Nové spany vytvořené po návratu callbacku už nejsou potomky tohoto spanu.
Omezení
- Žádné ruční propojování rodič-potomek. Vztahy rodič-potomek jsou určovány automaticky pomocí asynchronního kontextu JavaScriptu.
- Ne
setAttributes(hromadné nastavení) zatím. Použijte jednotlivésetAttributevolání. Hromadné nastavení je plánováno pro některou z budoucích verzí. - Ne
spanContext()(ID trasování/spanů) zatím. Přístup k identifikátorům trace a span pro ruční propagaci napříč hranicemi je plánován do budoucí verze. - Ne
setOutcomezatím. Nastavení výsledného stavu spanu je plánováno pro některou z budoucích verzí.
Další omezení trasování najdete v známá omezení stránce.