W3C trace context for a hook, and the spans every stage opens (ground rule 8).
The edge reads traceparent / tracestate off the request (from_headers/1)
and stores them on the hook as Ankusa.Envelope.trace, a map
%{traceparent: "00-<trace-id>-<parent-id>-<flags>", tracestate: String.t() | nil}.
Every sink then carries that value to the consumer: traceparent on
Ankusa.Sink.Http, the traceparent / tracestate fields and the
broker-native header pair on Ankusa.Sink.Message, the CloudEvents
traceparent extension on lifecycle events.
No SDK, no cost
Core depends on opentelemetry_api only. Without an SDK started (the default
for a library user; ankusa_server starts one when tracing.enabled), span/3
calls its function directly and current/1 returns the context the request
carried, so a traceparent sent to the edge reaches the consumer byte for
byte and nothing else happens.
With an SDK
ankusa.ingestis a:serverspan, a child of the provider's span (the request'straceparent);ankusa.verifyandankusa.commitare its children. With an SDK the hook stores the ingest span's context (current/1): same trace id, our span as the parent id,tracestatecarried through, the sampled flag as the sampler decided it. The value is fixed at ingest, so every retry, replay and sink carries the same one.ankusa.dispatch.attempt(one per attempt, a:producerspan) is a child of the stored context and links to it, so the whole life of a hook is one trace however long the retries take.ankusa.claim_checkis the pack write of a large body;ankusa.replayis one step of a replay job;ankusa.lifecycle.eventbuilds a lifecycle event.
Only the sampled bit of the trace flags is kept (00 or 01): the other bits
are reserved by the specification. A request whose traceparent is malformed,
repeated, version ff, or all-zero in its trace or parent id carries no trace
(the header stays an ordinary forwarded request header, as before). A
tracestate over 512 bytes is dropped; the traceparent still travels.
Summary
Types
Span attributes: string keys, scalar values (an OpenTelemetry attribute).
The trace context stored on a hook (Ankusa.Envelope.trace).
Functions
Whether an SDK is running (spans are recorded); false is the no-op API.
The context to store on a hook being ingested: the current span's, when an SDK
runs and the process is inside a valid span; otherwise fallback (the context
the request carried, verbatim).
Mark the current span failed. reason is a short class (an atom or string),
never a raw error term: those can carry URLs and credentials.
The trace context of a request's headers, or nil. One pass over the list;
header names are matched without case. A hook without a traceparent pays for
nothing but that pass.
Add attributes to the calling process's current span (a no-op without one).
Run fun inside a span named name; returns what fun returns.
The 32-hex trace id of a trace context, nil for nil. It is what a log line
carries as trace_id (Ankusa.Edge.Ingest), so a line can be found from the
trace and the other way round.
Types
Span attributes: string keys, scalar values (an OpenTelemetry attribute).
@type span_opt() :: {:attributes, attributes() | (-> attributes())} | {:kind, :internal | :server | :client | :producer | :consumer} | {:parent, t() | nil} | {:link, t() | nil}
The trace context stored on a hook (Ankusa.Envelope.trace).
Functions
@spec active?() :: boolean()
Whether an SDK is running (spans are recorded); false is the no-op API.
The context to store on a hook being ingested: the current span's, when an SDK
runs and the process is inside a valid span; otherwise fallback (the context
the request carried, verbatim).
Mark the current span failed. reason is a short class (an atom or string),
never a raw error term: those can carry URLs and credentials.
The trace context of a request's headers, or nil. One pass over the list;
header names are matched without case. A hook without a traceparent pays for
nothing but that pass.
@spec set_attributes(attributes()) :: :ok
Add attributes to the calling process's current span (a no-op without one).
Run fun inside a span named name; returns what fun returns.
Options: :attributes (a map, or a function that builds it: it runs only when
an SDK records the span, so the no-op path allocates nothing), :kind
(default :internal), :parent (a stored context to continue; default the
calling process's current span) and :link (a stored context to link to). An
exception in fun is recorded on the span and re-raised. With no SDK started
this is fun.().
The 32-hex trace id of a trace context, nil for nil. It is what a log line
carries as trace_id (Ankusa.Edge.Ingest), so a line can be found from the
trace and the other way round.