feat(worker): bindings for Cloudflare Workers custom spans (#899)#1016
feat(worker): bindings for Cloudflare Workers custom spans (#899)#1016Butch78 wants to merge 1 commit into
Conversation
…e#899) Add `worker::observability` — a binding for the `cloudflare:workers` `tracing.enterSpan` custom-span API (shipped 2026-06-16): - `enter_span` (sync) and `enter_span_async` (for async handlers) open custom trace spans that nest under the automatic platform spans in the Workers Observability waterfall. - `Span::set_attribute` / `Span::is_traced`. - `with_active_span` exposes the innermost open span so a `tracing_subscriber::Layer` can forward `tracing` events onto it. The binding stays dependency-free; the new `custom-spans` example shows a `WorkersLayer` (kept out of `worker` to avoid a `tracing-subscriber` dependency) plus the end-to-end usage. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
@kflansburg — reviving this against your note in #899:
That precondition shipped on 2026-06-16 ( On why platform-measured duration is the crux rather than a nicety: as @omarabid ran into in #899, hand-rolled timing inside a Worker fundamentally can't work — CI hasn't been triggered on the branch — I believe it needs a maintainer to approve the workflow run. Happy to rebase, or to split the bindings from the example if a smaller first cut is easier to review. |
Summary
Adds
worker::observability— a Rust binding for thecloudflare:workerscustom-span tracing API (shipped 2026-06-16). This is the runtime capability @kflansburg flagged as the prerequisite in #899 ("Once [Workers Observability supports user-defined trace spans], it should be possible to create a tracing Subscriber to collect spans").Closes the binding half of #899.
What's added
worker::observability(binding, no new deps onworker):enter_span(name, |span| ...)— sync custom span.enter_span_async(name, |span| async move { ... }).await— forasynchandlers; the callback returns aPromiseworkerd awaits before closing the span.Span::set_attribute(key, value)(bool | number | string) andSpan::is_traced().with_active_span(|span| ...)— exposes the innermost open span so atracinglayer can forward events onto it.Spans nest automatically via the JS async context, so they appear in the trace waterfall with correct parent/child nesting next to the automatic
fetch/KV/D1 spans.examples/custom-spans— a runnable Worker demonstrating the binding plus aWorkersLayer(tracing_subscriber::Layer) that forwardstracing::info!events and span fields onto the active platform span.Design notes
WorkersLayerlives in the example, notworker. Keeping it inworkerwould add atracing-subscriberdependency (and, via workspace feature unification with the existingtracingexample, dragtimeintoworker's build). The binding is useful on its own and stays dependency-free; the layer is ~90 lines anyone can copy, or we can lift it intoworkerbehind a feature if you'd prefer — happy to do that here.enterSpan(name, cb); no imperative start/end).tracingspan lifetimes are two separate operations (on_enter/on_exit), so aLayercan't transparently bridge arbitraryspan!lifetimes — and durations must come from the platform anyway, since guest timer resolution is clamped (the root of the zero-duration issue reported in [Feature] Integrate with tracing #899). So structure+timing come from the closure-scopedenter_spanwrappers (an#[instrument]-style macro could generate these), and the layer forwards events/fields. Open to a runtime-team ask for agetActiveSpan/imperative span API that would unlock a fully transparent layer.unsafe. The sync path usesScopedClosure::borrow_mut(the callback is immediate, so it can borrow non-'staticstate); the async path uses an owned'staticclosure since the promise outlives the call.Validation
cargo clippy --features d1,queue --all-targets --workspace -- -D warnings✅cargo clippy --all-features --package worker-sandbox --all-targets -- -D warnings✅cargo fmt --all -- --check✅ /cargo check✅worker-build --releaseonexamples/custom-spansproduces a deployable Worker; the bundle emitsimport { tracing } from "cloudflare:workers"withenterSpan/setAttribute/isTraced.Cargo.lockchange is additive (the example'stracing/tracing-subscriber); no existing versions bumped.A standalone version was also prototyped here for discussion: https://github.com/Butch78/cf-workers-rs-tracing
I'll sign the CLA once the bot picks this up.