std/tracing: OpenTelemetry-compatible observability (tracing, metrics, logs) - #13
Merged
Conversation
Adds the tracing signal of OpenTelemetry as pure Xi, alongside std/monitoring and off until enabled. A Tracer records spans with a trace/span identity, parent-child nesting via a current-span stack, attributes, events, and status; finished spans are buffered and handed to a SpanExporter on flush. Every mechanic is an interface, so a program replaces any of them with a bind: the Tracer itself, the SpanExporter, the Sampler, the IdGenerator, or the TraceClock. Defaults are a random id generator, a system clock, an always-on sampler, and a console exporter; an in-memory exporter ships for tests. Needs no new C or runtime code: ids come from crypto.randomHex, timing from time.nowNanos, output through the injected Logger. Includes a runnable example and tests for nesting, attributes/events, error recording, and pluggability. Design and staged roadmap in docs/tracing.md.
genIf handled the else clause for a plain if but not for if let: the if-let
path emitted only 'if ((x).has_value) { ... }' and returned, so a trailing
else { ... } was parsed as a separate statement and lowered to 'else;' plus an
unconditional block. The else body then ran even when the optional was present
(most visible with compound-typed optionals). Mirror the plain-if else handling
in the if-let path, with the bound name in scope only in the then-branch.
Regression test in examples/language/iflet_else_test.xi.
A W3CPropagator injects a SpanContext into a traceparent header (00-<trace>-<span>-<flags>) and extracts one back, returning none for an absent or malformed header (wrong length, bad separators, non-hex or all-zero ids). startChild(remoteContext, ...) continues a trace across a process boundary, so a server span parents to the caller's span and shares its trace id. std/http gains requestWith(..., extraHeaders), a behavior-preserving refactor that shares the transport with request, so an outgoing call can carry the injected traceparent. Tests cover round-trip, rejection, and cross-service parenting.
OtlpHttpSpanExporter POSTs OTLP/JSON to <endpoint>/v1/traces, the wire format an
OpenTelemetry collector, Jaeger or Tempo reads. The encoder builds the
resourceSpans/scopeSpans/spans envelope and follows OTLP/JSON conventions: span
kind and status as ints, 64-bit times as strings, attribute values wrapped in a
typed { stringValue } envelope. Endpoint and service name come from a
TracingConfig (DefaultTracingConfig by default, or bind readConfig).
The exporter is opt-in (import std/tracing/exporter/otlp.xi and bind it), so a
plain app keeps the console exporter and no HTTP dependency. Encoder tests assert
the shape and conventions by navigating the Json, no live collector needed.
RatioSampler keeps roughly the configured fraction of traces, deciding from the trace id's first 16 bits so the choice is stable across a whole trace and every service agrees; the ratio comes from TracingConfig.sampleRatio. AlwaysOffSampler keeps tracing structurally on while emitting nothing. Both are opt-in (import std/tracing/sampling.xi and bind), so the default AlwaysOnSampler still auto-injects. The pure ratioDecision is unit-tested for the full/none edges, per-trace stability, and the threshold.
TracingMonitoring implements Monitoring, so importing it surfaces the finished-span count and enabled flag in the std/monitoring report under 'tracing' (the same list-injected way WebMonitoring adds 'web'); neither module depends on the other's internals. WebTracer brackets an HTTP handler with a server span: begin continues an inbound trace from the traceparent header (or starts a root) and tags the span with http.request.method and url.path; finish records http.response.status_code and the status. A handler injects WebTracer and calls begin/finish around its work, since Xi web dispatch is where-guarded handlers rather than a middleware chain.
Adds currentTraceId/currentSpanId to the Tracer (the innermost active span, or "") and a TraceLog that wraps the injected Logger to append trace_id/span_id to each line while a span is active. TraceLog is a distinct interface rather than a Logger decorator, so it does not depend on itself; the pure correlate() is tested, and the accessors are shown tracking span nesting.
Adds a Meter with the OpenTelemetry instrument types: counter, up-down counter, gauge, and histogram (count/sum/min/max). It is the metrics signal, under std/monitoring rather than std/tracing; MetricsMonitoring surfaces the numbers in the monitoring report under 'metrics'. Instruments are kept in a flat list keyed by (name, kind) because a Map cannot yet be class state. Tests cover each instrument and the snapshot.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds an OpenTelemetry-compatible observability suite as pure Xi, alongside
std/monitoringand off until enabled. No new C or runtime code: ids come fromcrypto.randomHex, timing fromtime.nowNanos, export overstd/http, payloads viastd/json, correlation through the injectedLogger.Every mechanic is a bindable interface, so a program can replace any of them (the
Traceritself, the exporter, sampler, id generator, clock, propagator, config, meter) with abind.What is included (all stages)
W3CPropagatorinject/extract of thetraceparentheader;startChild(remoteContext, ...)continues a trace across a process boundary.std/httpgainsrequestWith(..., extraHeaders)(a behavior-preserving refactor) to carry the header outbound.<endpoint>/v1/traces(the format a Collector, Jaeger or Tempo reads), following the OTLP conventions (kind/status ints, string-encoded 64-bit times, typed attribute values). Opt-in; endpoint/service name from a typedTracingConfig.RatioSampler(stable per-trace, ratio from config) andAlwaysOffSampler, opt-in; the defaultAlwaysOnSamplerstays auto-injected.TracingMonitoringsurfaces the span count in thestd/monitoringreport undertracing;WebTracerbrackets an HTTP handler with a server span (continues an inbound trace, sets HTTP semantic-convention attributes).TraceLogstampstrace_id/span_idonto log lines while a span is active.Meterwith counter, up-down counter, gauge, and histogram instruments understd/monitoring/instruments.xi, surfaced in the monitoring report undermetrics.Design and staged rationale:
docs/tracing.md.Also in this PR
if let ... elseran the else branch unconditionally (codegen emittedelse;plus a stray block, so the else body always ran; most visible with compound-typed optionals). Fixed incompiler/impl/codegen/stmt.xiwith a regression test. General fix, not tracing-specific.Verification
std/httpchanges).