Skip to content

[Feature] Integrate with tracing #899

Description

@sagikazarmark

Is there an existing issue for this?

  • I have searched the existing issues

Description

Would it make sense/be possible to integrate Rust tracing into Workers observability?

Activity

  1. kflansburg commented on Jan 3, 2026

    @kflansburg
    Member

    I don’t believe Workers Observability supports user defined trace spans yet. Once it does, it should be possible to create a tracing Subscriber to collect spans.

    For logging, this example shows using the tracing crate which should then emit logs that are captured by Workers Observability.

  2. sagikazarmark commented on Jan 4, 2026

    @sagikazarmark
    Author

    Thanks @kflansburg! I saw that example, but I was wondering if a deeper integration was possible.

    Feel free to close the issue (or keep it open for tracking, I couldn't find any other)

  3. esotuvaka commented on Jan 5, 2026

    @esotuvaka
    Contributor

    I looked into this too and had to fallback to the example that @kflansburg linked. When using tower_http::TraceLayer with axum it calls into the system's Time module, which compiles but panics at runtime on WASM.

    I only looked very briefly, but I was unable to find a way to provide my own time's to the TraceLayer middleware via a formatter

  4. omarabid commented on Jan 18, 2026

    @omarabid
    Contributor

    It's possible with a custom executor and you also need to manually propagate context (doesn't really work out of the box like the rest of OTEL stack). I have it working reliably and sending data to honeycomb. The only issue (which seems unsolvable) is that all spans show the same start and end time because Cloudflare doesn't provide access to execution time within the platform. This means every span appears to have zero duration, making it impossible to see the actual execution time of individual functions.

  5. Butch78 commented on Jun 17, 2026

    @Butch78

    The precondition @kflansburg named — "Once [Workers Observability supports
    user-defined trace spans], it should be possible to create a tracing Subscriber
    to collect spans"
    — shipped on 2026-06-16: custom spans via
    cloudflare:workers tracing.enterSpan.

    I put together a working proof-of-concept of both halves (the binding and a
    tracing layer) so there's something concrete to design against:
    https://github.com/Butch78/cf-workers-rs-tracing

    It builds clean to wasm32-unknown-unknown, is clippy-clean, and worker-build
    packages it into a deployable Worker whose bundle emits the expected glue
    (import { tracing } from "cloudflare:workers", enterSpan, setAttribute,
    isTraced). It's two pieces:

    1. Binding — enter_span (sync) and enter_span_async (for async
      handlers, the callback returns a Promise that workerd awaits before
      closing the span), plus a Span handle with set_* / is_traced. Safe, no
      unsafe: the sync form uses ScopedClosure::borrow_mut (the span callback
      is immediate, so it can borrow non-'static state); the async form uses an
      owned 'static closure since the promise outlives the call.
    2. tracing_subscriber::Layer — forwards tracing::info! events and span
      fields onto the active platform span as attributes, no per-call-site
      changes.

    The constraint worth discussing

    The API is callback-scoped only — enterSpan(name, cb), no imperative
    startSpan/endSpan, no getActiveSpan. tracing span lifetimes are two
    separate operations (on_enter / on_exit via a guard's Drop), so a Layer
    can't bridge an arbitrary span! to a platform span: at on_enter you'd have
    to call enterSpan and not return from its callback until the later, separate
    on_exit. On a single-threaded Worker you can't suspend/resume that callback.
    And durations have to come from the platform anyway (guest timer resolution is
    clamped — the root of the zero-duration problem reported earlier in this
    thread).

    So the PoC splits it: span structure + timing come from closure-scoped
    enter_span wrappers (which an #[instrument]-style macro could generate);
    events + fields are forwarded transparently by the Layer.

    Questions for the upstream shape

    • Land the binding split as worker-sys/src/types/tracing.rs +
      worker::tracing (mirroring the recent email encapsulation), with the
      Layer behind a tracing feature? Happy to open that PR.
    • Is there appetite to ask the runtime team for an imperative
      startSpan/endSpan or a getActiveSpan? That's the one addition that would
      unlock a fully transparent tracing Layer (bridging span lifetimes, not
      just events).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions