Instrumentation events as :telemetry events

This package provides Snodo.Instrumentation.Telemetry, a Snodo.Instrumentation sink that forwards every event to :telemetry. It adds no runtime dependency to the snodo core, and installing it changes no server until the sink is configured. Tooling that consumes :telemetry events, such as telemetry_metrics, Phoenix LiveDashboard, and the OpenTelemetry instrumentations, then sees the events without an application-written bridge.

Use from an application

Add snodo_telemetry next to snodo:

{:snodo_telemetry, "~> 0.4.0"}

Then pass the module wherever a sink is accepted: the server runtime, the subscription hub, and the Tasks runner.

{:ok, hub} =
Snodo.Subscription.Hub.start_link(
name: MyApp.SubscriptionHub,
instrumentation: Snodo.Instrumentation.Telemetry
)
runtime =
MyServer.runtime(
instrumentation: Snodo.Instrumentation.Telemetry,
subscription_source: Snodo.Subscription.Hub.source(MyApp.SubscriptionHub)
)
{:ok, runner} =
Snodo.Extensions.Tasks.Runner.start_link(
store: store_ref,
instrumentation: Snodo.Instrumentation.Telemetry
)

The sink takes no options.

What is forwarded

Each event reaches :telemetry.execute/3 under the name, with the measurements, and with the metadata that Snodo.Instrumentation gives the sink. The instrumentation guide lists every event with its measurements and metadata. No event includes request params, auth data, work input, access values, task results or errors, or input responses.

The sink adds one metadata key. The :start, :stop, and :exception events of a server dispatch or a Tasks runner job share a telemetry_span_context reference, as :telemetry.span/3 emits it, so a handler can pair a :stop or :exception with its :start. The reference is created on :start and kept in the emitting process until the matching :stop or :exception. Nested dispatches in one process form a stack; runner jobs, which interleave in the runner process, are keyed by task_id. A :stop or :exception with no recorded :start gets a fresh reference. Events that are not part of a span are forwarded without the key.

Measurements are forwarded unchanged: system_time on :start, and duration in native time units on :stop and :exception. A Telemetry.Metrics definition converts the unit:

Telemetry.Metrics.summary("snodo.server.dispatch.stop.duration",
unit: {:native, :millisecond},
tags: [:method, :outcome]
)

:telemetry runs handlers in the emitting process, and Snodo.Instrumentation calls the sink synchronously, so a handler runs inside the dispatch, the hub, or the runner. Keep handlers fast and hand expensive work to another process. :telemetry detaches a handler that raises; the operation being observed is not affected.

Verification

From this package directory:

mix deps.get
mix quality
mix quality.types

mix quality checks formatting, compilation warnings, strict Credo, and the tests. The tests attach :telemetry handlers to every event in the catalog and check each event's name, measurements, and metadata through a server dispatch, a subscription hub lifecycle, and a Tasks runner job, including the shared span context and its absence from point events. The Tasks extension is a test-only dependency.