Skip to content

Repository files navigation

OpentelemetryStatifier

OpenTelemetry instrumentation for the Statifier family of statechart packages - in the opentelemetry_oban / opentelemetry_ecto mold: the libraries emit :telemetry events, this package turns them into OpenTelemetry spans, span events, and span links. It depends only on opentelemetry_api; hosts bring their own SDK and exporter.

Status: early implementation. setup/1 attaches to every event Statifier.Session.Telemetry.events/0 names. A macrostep's :start/:stop pair becomes one statifier.macrostep span; everything that fires in between - the eleven effect events, the nine trace events, :interpret, :unroutable, :halt - becomes a span event on it, and each span links to the same session's previous macrostep and (for an invoked child's :initialize macrostep) to the invoking parent's open span. :terminate cleans up the session's rows, and a periodic sweep ends the spans a brutally killed session orphans with an error status rather than leaking them. The design this package implements is recorded in statifier-ex:

  • docs/opentelemetry.md - span topology, context propagation, attribute mapping, cardinality policy, and trace-off degradation.
  • st-ADR-0062 - packaging and scope: family-scoped, public-events-only, and unpublished until statifier itself is on Hex.

The short version of the design: a statechart macrostep is a span; effect and trace telemetry events are span events on it; there is no session-lifetime span; each macrostep roots its own trace, stitched to its neighbors (previous macrostep, invoking parent) with span links; nothing unbounded is exported as an attribute by default.

Installation

Not published to Hex - statifier itself is unpublished, and a Hex package cannot carry a git dependency, so this package pins statifier main SHAs under st-ADR-0061's contract and asks the same of its consumers:

def deps do
  [
    {:opentelemetry_statifier,
     github: "riddler/opentelemetry_statifier", ref: "<pinned sha>"}
  ]
end

Usage

Call setup/0 (or setup/1 with options) once, typically at application start, after your host has configured its own OpenTelemetry SDK and exporter:

:ok = OpentelemetryStatifier.setup()

# or, with options:
:ok = OpentelemetryStatifier.setup(record_datamodel_values: false)

This attaches a handler to every event the statifier telemetry contract emits. Each statechart macrostep becomes a statifier.macrostep span, carrying statifier.session_id, statifier.trigger, statifier.outcome, and the macrostep's counters and resulting configuration as attributes. The effect and trace events that fire inside the macrostep land on the span as span events (statifier.effect.send, statifier.trace.exit_set, ...), attributes mapped uniformly under the statifier. namespace: a resolved source location flattens to statifier.source.line/statifier.source.column, a configuration becomes a sorted string array, and the raw effect struct is never serialized. The two events that carry datamodel values (:datamodel_change, :datamodel_init) are recorded without those values unless you opt in with record_datamodel_values: true - nothing unbounded is exported by default.

Each macrostep span is the root of its own trace. Span links stitch the traces together: every macrostep links to the same session's previous macrostep span, and an invoked child session's :initialize macrostep links to the parent macrostep span that was open when the child started.

Call teardown/0 to detach everything, for example between test cases:

:ok = OpentelemetryStatifier.teardown()

With no SDK started, the bridge is a cheap no-op: spans go through a no-op tracer and nothing is exported.

The :initialize macrostep span starts late

Span start times come from the telemetry event's own monotonic_time, so for an event-triggered macrostep the span's wall time matches the statifier.duration measurement almost exactly. The :initialize macrostep is the one documented exception: statifier emits its :start from the session's init/1 and its :stop from the following handle_continue, so the span opens after some of the work it covers has already happened. The span is correspondingly shorter than statifier.duration reports - materially so, not by a rounding margin.

This skew is accepted rather than corrected: the alternative is back-calculating the start from end_time - duration, which every other bridge in the family does for every span and which is strictly less accurate for the other triggers. Read statifier.duration, not the span's wall time, when you want the macrostep's real cost.

License

MIT - see LICENSE.

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages