Skip to content

feat(worker): bindings for Cloudflare Workers custom spans (#899)#1016

Open
Butch78 wants to merge 1 commit into
cloudflare:mainfrom
Butch78:feat/tracing-custom-spans
Open

feat(worker): bindings for Cloudflare Workers custom spans (#899)#1016
Butch78 wants to merge 1 commit into
cloudflare:mainfrom
Butch78:feat/tracing-custom-spans

Conversation

@Butch78

@Butch78 Butch78 commented Jun 18, 2026

Copy link
Copy Markdown

Summary

Adds worker::observability — a Rust binding for the cloudflare:workers custom-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 on worker):

  • enter_span(name, |span| ...) — sync custom span.
  • enter_span_async(name, |span| async move { ... }).await — for async handlers; the callback returns a Promise workerd awaits before closing the span.
  • Span::set_attribute(key, value) (bool | number | string) and Span::is_traced().
  • with_active_span(|span| ...) — exposes the innermost open span so a tracing layer 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 a WorkersLayer (tracing_subscriber::Layer) that forwards tracing::info! events and span fields onto the active platform span.

Design notes

  • Why the WorkersLayer lives in the example, not worker. Keeping it in worker would add a tracing-subscriber dependency (and, via workspace feature unification with the existing tracing example, drag time into worker'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 into worker behind a feature if you'd prefer — happy to do that here.
  • The API is callback-scoped only (enterSpan(name, cb); no imperative start/end). tracing span lifetimes are two separate operations (on_enter/on_exit), so a Layer can't transparently bridge arbitrary span! 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-scoped enter_span wrappers (an #[instrument]-style macro could generate these), and the layer forwards events/fields. Open to a runtime-team ask for a getActiveSpan/imperative span API that would unlock a fully transparent layer.
  • No unsafe. The sync path uses ScopedClosure::borrow_mut (the callback is immediate, so it can borrow non-'static state); the async path uses an owned 'static closure 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 --release on examples/custom-spans produces a deployable Worker; the bundle emits import { tracing } from "cloudflare:workers" with enterSpan / setAttribute / isTraced.
  • Cargo.lock change is additive (the example's tracing/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.

…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) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant