Skip to content

add distributed tracing core (std.trace)#449

Merged
kacy merged 1 commit into
mainfrom
feat/o11y-trace
Jul 17, 2026
Merged

add distributed tracing core (std.trace)#449
kacy merged 1 commit into
mainfrom
feat/o11y-trace

Conversation

@kacy

@kacy kacy commented Jul 17, 2026

Copy link
Copy Markdown
Owner

summary

second phase of observability: spans. a span records one unit of work (name,
timing, attributes, status) and its place in a trace. spans nest through a
per-os-thread "current" span, so starting a span inside another parents it
automatically — no context threaded through calls:

span := trace.start("handle_request")
span.set_attr("http.route", "/api")
span.end()

built on pieces already in place: the per-thread current span uses a
threadlocal global (#438); ids come from log.new_trace_id/new_span_id;
time.mono_nanos gives a precise duration on a wall-clock base. tracing is OFF
until set_active(true) (std.obs.init will flip it from the environment), so
start() is a cheap no-op in an uninstrumented program.

  • across a spawn: capture current_context() and re-establish it in the task
    with with_context() (the one place propagation is explicit)
  • across a process: W3C traceparent format/parse for http/grpc headers
  • ended spans collect in a bounded buffer for the OTLP exporter (phase 3) to drain

what was tested

  • 3 unit tests: nesting via the current span, no-op when inactive, traceparent
    round-trip
  • a cross-spawn program confirming a child task joins the parent's trace and
    parents under the root
  • 85 examples

pure-additive stdlib; no compiler change, no bootstrap seed refresh.

second phase of observability support: spans. a span records one unit of work
(name, timing, attributes, status) and its place in a trace. spans nest through
a per-os-thread "current" span, so starting a span inside another parents it
automatically with no context threaded through calls:

    span := trace.start("handle_request")
    span.set_attr("http.route", "/api")
    span.end()

built on the pieces already in place: the per-thread current span uses a
threadlocal global (#438); ids come from log.new_trace_id / new_span_id; timing
uses time.mono_nanos for a precise duration on a wall-clock base. tracing is off
until set_active(true) (std.obs.init flips it from the environment), so start()
is a cheap no-op in an uninstrumented program.

- crossing a spawn: capture current_context() and re-establish it in the task
  with with_context() (the one place propagation is explicit)
- crossing a process: W3C traceparent format/parse for http/grpc headers
- ended spans collect in a bounded buffer that the OTLP exporter drains next

verified: 3 unit tests (nesting via the current span, no-op when inactive,
traceparent round-trip) plus a cross-spawn program confirming a child task joins
the parent's trace and parents under the root; 85 examples.
@kacy
kacy force-pushed the feat/o11y-trace branch from 85345cd to 827d7da Compare July 17, 2026 18:29
@kacy
kacy merged commit ac7e9d4 into main Jul 17, 2026
2 checks passed
@kacy
kacy deleted the feat/o11y-trace branch July 17, 2026 18:37
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