# `OpentelemetryStatifier.SpanContext`
[🔗](https://github.com/riddler/opentelemetry_statifier/blob/v0.10.0/lib/opentelemetry_statifier/span_context.ex#L1)

The bridge's keyed `(session_id, macrostep)` span-context read: the
public lookup a wire-format producer needs to stamp OTel correlation
ids onto the messages it emits.

## Why it exists

`statifier_ui`'s trace wire format (its ADR-0013) reserves an optional
`otel` envelope key carrying the W3C trace and span ids of the
`statifier.macrostep` span a message belongs to, and specifies the seam
that fills it as a host-supplied function:

    otel_context: (session_id, macrostep ->
                     {:ok, %{trace_id: binary, span_id: binary}} | :none)

That record's arguments are `(session_id, macrostep)` and not "the
currently open span" on purpose: the subscriber stamping the message is
its own process consuming asynchronously, so "current" would silently
stamp the wrong macrostep under lag. The same record names the lookup
as an open dependency on this package, and this module is the answer -
`lookup/2` has exactly that arity and exactly that return shape, so a
host wires the two together by passing the capture and nothing else:

    StatifierUI.Trace.Subscriber.start_link(
      session: session,
      otel_context: &OpentelemetryStatifier.SpanContext.lookup/2
    )

(The subscriber's `:otel_context` option is `statifier_ui`'s own
follow-up to that record; this side of the seam is ready for it.)

## What it reads

The bridge already holds every open macrostep span in the ETS table
`OpentelemetryStatifier.SpanTable` owns, for its own link stitching.
This is a read of that table and nothing more: no OTel context is
created, entered, or attached, so ADR-0003 decision 8 - a macrostep
span never starts from and is never attached to the process's ambient
context - is untouched, and calling `lookup/2` from any process is
safe.

The macrostep counter a span covers is stamped onto its row by
`OpentelemetryStatifier.Handler` from the first intra-macrostep event
that lands on it, because statifier's macrostep `:start` event carries
no such measurement. Every message the wire format may stamp is
produced by one of those events, so a macrostep with a message to
correlate always has its counter recorded.

## When it answers `:none`

`:none` is an ordinary answer, not an error, and it is what the wire
format degrades to (the key is simply absent). It is returned when the
bridge was never set up, when `session_id` is unknown, when the
macrostep's span has already closed - a subscriber lagging behind the
session process is the common case - and when the span context on the
row carries no valid ids. Consumers never see half a pair: ADR-0013
requires both ids or neither, and this module returns both or `:none`.

# `t`

```elixir
@type t() :: %{trace_id: String.t(), span_id: String.t()}
```

The lowercase hex W3C Trace Context ids of one `statifier.macrostep`
span: `trace_id` is exactly 32 hex digits, `span_id` exactly 16, with
no `0x` prefix, no dashes, and no uppercase.

# `lookup`

```elixir
@spec lookup(String.t(), non_neg_integer()) :: {:ok, t()} | :none
```

Looks up the open `statifier.macrostep` span covering `macrostep` in
`session_id`, against the bridge's default span table.

Returns `{:ok, %{trace_id: hex, span_id: hex}}` on a hit and `:none` on
a miss. This is the function to hand a `statifier_ui` subscriber as its
`:otel_context` producer.

## Examples

    iex> OpentelemetryStatifier.SpanContext.lookup("no-such-session", 1)
    :none

# `lookup`

```elixir
@spec lookup(String.t(), non_neg_integer(), atom()) :: {:ok, t()} | :none
```

As `lookup/2`, against the span table named by `table`.

Hosts that passed `:table` to `OpentelemetryStatifier.setup/1` build
their producer from this arity - `&SpanContext.lookup(&1, &2, :my_table)`
is still the 2-arity function ADR-0013 asks for.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
