AshOnetime.Telemetry (ash_onetime v0.6.0)

Copy Markdown View Source

Emits the closed, value-free telemetry surface for keyed-effect admission.

Out-of-the-box metrics attachment

The library emits events but does NOT attach a handler — a fresh application sees nothing until it attaches one. Call attach/0 (or attach/1 with options) from your application startup (e.g. your start/2 callback's supervised children, after the repo is started) to attach the default handler, which routes the closed event surface into standard Telemetry.Metrics counter and summary definitions. This mirrors Oban.Telemetry.attach_default_logger/1 and Ash.Telemetry — an opt-in helper so a consumer does not hand-roll a handler.

# in your application startup, once per VM
AshOnetime.Telemetry.attach()

If you already maintain a Telemetry.Metrics reporter (StatsD, Prometheus), you may prefer to declare the metric definitions in your own MyApp.Telemetry rather than using this helper — see metrics/0 for the canonical definition list the helper attaches.

Summary

Functions

Emits [:ash_onetime, :admission] with :duration and result_class in [:admitted, :rejected, :failed]. The classified outcome of an admission attempt.

Attaches the default metrics handler to the closed [:ash_onetime, *] event surface.

Emits [:ash_onetime, :cache] with result_class in [:hit, :miss, :stale, :corrupt, :failure, :timeout, :stored, :expired, :oversized, :disabled]. The optional completed-response cache outcome for an idempotency admission.

Emits [:ash_onetime, :cleanup] with :count and result_class in [:claims_deleted, :partitions_dropped, :partitions_created]. A bounded cleanup run.

Emits [:ash_onetime, :conflict] with result_class in [:complete, :processing, :nonce_used, :malformed]. A collision on an in-flight or completed claim under the same logical key.

Undoes attach/1 by detaching the handler.

Emits [:ash_onetime, :encoding] with :duration and result_class in [:stored, :rejected, :rollback, :failed]. The outcome of encoding a response payload for storage/replay.

Emits [:ash_onetime, :external_recovery] with :duration and result_class in [:processing_committed, :execute_succeeded, :recover_succeeded, :absence_proven, :outcome_unknown, :external_effect_unavailable, :finalize_locked, :replayed]. The stage outcomes of the committed external-effect recovery protocol.

Emits [:ash_onetime, :fingerprint_mismatch] with result_class: :rejected. A stored response whose fingerprint does not match the incoming request is rejected (no replay).

The unique id used to attach the default metrics handler.

Emits [:ash_onetime, :reap] with :count and result_class: :claims_reaped. The count of abandoned processing recovery points reaped by a bounded reaper run.

Emits [:ash_onetime, :replay] with :duration and result_class in [:returned, :rejected]. The replayed-vs-rejected decision on a stored response.

Emits [:ash_onetime, :store_uncertainty] with result_class in [:sent, :unknown, :disconnected, :lock_timeout, :worker_timeout]. The authoritative-state- unavailable conditions on the committed-claim path; all fail closed (see operations.md).

Emits [:ash_onetime, :uncertain_exception] — a store-internal exception (a raise inside a committed-claim transaction) before it is collapsed to :dispatched_unknown. This is a diagnosis surface, NOT an admission/business event: it bypasses emit/6 (which is shaped for admission events with strategy/resource/action/result_class and validates a result-class allowlist) and calls :telemetry.execute/3 directly with a %{strategy:, phase:, exception:} metadata map. The exception is the exception STRUCT MODULE (e.g. Postgrex.Error), not the full struct — to avoid leaking request material that may be embedded in an exception message.

Emits [:ash_onetime, :untracked_execution] with result_class: :checkout_unavailable. An idempotent action executed without a stored admission after a checkout failure — correct by design but worth visibility (the optional :execute_untracked path).

Emits [:ash_onetime, :verification] with :duration and result_class in [:verified, :rejected, :timeout]. The outcome of a token/proof verification.

Functions

admission(duration, strategy, resource, action, result_class)

@spec admission(non_neg_integer(), atom(), module(), atom(), atom()) ::
  :ok | {:error, AshOnetime.Error.t()}

Emits [:ash_onetime, :admission] with :duration and result_class in [:admitted, :rejected, :failed]. The classified outcome of an admission attempt.

attach(opts \\ [])

@spec attach(keyword()) :: :ok | {:error, :already_exists}

Attaches the default metrics handler to the closed [:ash_onetime, *] event surface.

The handler re-emits each event as a downstream [:ash_onetime, event, :metric] event carrying the same atoms-only metadata and a normalized :count or :duration measurement. This gives a consumer a single attach point to consume via their own Telemetry.Metrics reporter or custom aggregation, without hand-rolling the event list and the count/duration split.

The handler is a pure router — it adds no state and emits nothing the upstream validator did not already permit, so the value-free guarantee is preserved. It does NOT depend on telemetry_metrics; a consumer running a Telemetry.Metrics reporter declares the metric definitions in their own MyApp.Telemetry (see the documentation/telemetry.md runnable example for the canonical counter/summary definitions).

Idempotent per name — safe to call once at boot; a second attach with the same name returns {:error, :already_exists} without detaching the first.

Options

  • :name — scope the handler to a unique id (default nil, one handler per VM). Use a distinct name to attach alongside another consumer's handler.

Examples

# once at boot, in your app's start/2 after the repo starts
AshOnetime.Telemetry.attach()

# attach a second, separately-scoped handler
AshOnetime.Telemetry.attach(name: :edge)

cache(strategy, resource, action, result_class)

@spec cache(atom(), module(), atom(), atom()) :: :ok | {:error, AshOnetime.Error.t()}

Emits [:ash_onetime, :cache] with result_class in [:hit, :miss, :stale, :corrupt, :failure, :timeout, :stored, :expired, :oversized, :disabled]. The optional completed-response cache outcome for an idempotency admission.

cleanup(strategy, resource, action, count, result_class)

@spec cleanup(atom(), module(), atom(), non_neg_integer(), atom()) ::
  :ok | {:error, AshOnetime.Error.t()}

Emits [:ash_onetime, :cleanup] with :count and result_class in [:claims_deleted, :partitions_dropped, :partitions_created]. A bounded cleanup run.

conflict(strategy, resource, action, result_class)

@spec conflict(atom(), module(), atom(), atom()) ::
  :ok | {:error, AshOnetime.Error.t()}

Emits [:ash_onetime, :conflict] with result_class in [:complete, :processing, :nonce_used, :malformed]. A collision on an in-flight or completed claim under the same logical key.

detach(opts \\ [])

@spec detach(keyword()) :: :ok | {:error, :not_found}

Undoes attach/1 by detaching the handler.

Pass the same :name used when attaching to detach a scoped handler.

Examples

:ok = AshOnetime.Telemetry.attach()
:ok = AshOnetime.Telemetry.detach()

:ok = AshOnetime.Telemetry.attach(name: :edge)
:ok = AshOnetime.Telemetry.detach(name: :edge)

encoding(duration, strategy, resource, action, result_class)

@spec encoding(non_neg_integer(), atom(), module(), atom(), atom()) ::
  :ok | {:error, AshOnetime.Error.t()}

Emits [:ash_onetime, :encoding] with :duration and result_class in [:stored, :rejected, :rollback, :failed]. The outcome of encoding a response payload for storage/replay.

external_recovery(duration, strategy, resource, action, result_class)

@spec external_recovery(non_neg_integer(), atom(), module(), atom(), atom()) ::
  :ok | {:error, AshOnetime.Error.t()}

Emits [:ash_onetime, :external_recovery] with :duration and result_class in [:processing_committed, :execute_succeeded, :recover_succeeded, :absence_proven, :outcome_unknown, :external_effect_unavailable, :finalize_locked, :replayed]. The stage outcomes of the committed external-effect recovery protocol.

fingerprint_mismatch(strategy, resource, action)

@spec fingerprint_mismatch(atom(), module(), atom()) ::
  :ok | {:error, AshOnetime.Error.t()}

Emits [:ash_onetime, :fingerprint_mismatch] with result_class: :rejected. A stored response whose fingerprint does not match the incoming request is rejected (no replay).

handler_id(name \\ nil)

@spec handler_id(term()) :: binary()

The unique id used to attach the default metrics handler.

Without an argument the id is the constant "ash-onetime-default-metrics". When a name is provided the id is suffixed with the inspected name, which allows multiple scoped handlers (e.g. one per OTP node in a multi-node setup) to coexist.

reap(strategy, resource, action, count, result_class)

@spec reap(atom(), module(), atom(), non_neg_integer(), atom()) ::
  :ok | {:error, AshOnetime.Error.t()}

Emits [:ash_onetime, :reap] with :count and result_class: :claims_reaped. The count of abandoned processing recovery points reaped by a bounded reaper run.

replay(duration, strategy, resource, action, result_class)

@spec replay(non_neg_integer(), atom(), module(), atom(), atom()) ::
  :ok | {:error, AshOnetime.Error.t()}

Emits [:ash_onetime, :replay] with :duration and result_class in [:returned, :rejected]. The replayed-vs-rejected decision on a stored response.

store_uncertainty(strategy, resource, action, result_class)

@spec store_uncertainty(atom(), module(), atom(), atom()) ::
  :ok | {:error, AshOnetime.Error.t()}

Emits [:ash_onetime, :store_uncertainty] with result_class in [:sent, :unknown, :disconnected, :lock_timeout, :worker_timeout]. The authoritative-state- unavailable conditions on the committed-claim path; all fail closed (see operations.md).

uncertain_exception(strategy, opts)

Emits [:ash_onetime, :uncertain_exception] — a store-internal exception (a raise inside a committed-claim transaction) before it is collapsed to :dispatched_unknown. This is a diagnosis surface, NOT an admission/business event: it bypasses emit/6 (which is shaped for admission events with strategy/resource/action/result_class and validates a result-class allowlist) and calls :telemetry.execute/3 directly with a %{strategy:, phase:, exception:} metadata map. The exception is the exception STRUCT MODULE (e.g. Postgrex.Error), not the full struct — to avoid leaking request material that may be embedded in an exception message.

A fresh application sees nothing unless it attaches a handler (the lib's telemetry-only posture). Consumers wanting store-transaction diagnosis attach to this event.

untracked_execution(strategy, resource, action)

@spec untracked_execution(atom(), module(), atom()) ::
  :ok | {:error, AshOnetime.Error.t()}

Emits [:ash_onetime, :untracked_execution] with result_class: :checkout_unavailable. An idempotent action executed without a stored admission after a checkout failure — correct by design but worth visibility (the optional :execute_untracked path).

verification(duration, strategy, resource, action, result_class)

@spec verification(non_neg_integer(), atom(), module(), atom(), atom()) ::
  :ok | {:error, AshOnetime.Error.t()}

Emits [:ash_onetime, :verification] with :duration and result_class in [:verified, :rejected, :timeout]. The outcome of a token/proof verification.