# `X402.Telemetry.Stats`
[🔗](https://github.com/cardotrejos/x402/blob/v0.9.0/lib/x402/telemetry/stats.ex#L1)

Dependency-free in-memory aggregator for x402 telemetry events.

For deployments without LiveDashboard or a `Telemetry.Metrics` reporter:
`attach/1` subscribes to every event in `X402.Telemetry.events/0` and
keeps per-event counters (total and per status) plus simple latency
statistics for span events that carry a `:duration` measurement, in a
lazily created ETS table. `snapshot/1` reads them back:

    :ok = X402.Telemetry.Stats.attach()
    # ... traffic ...
    X402.Telemetry.Stats.snapshot()
    #=> %{
    #     [:x402, :plug, :payment_verified] => %{count: 42, by_status: %{}, latency: nil},
    #     [:x402, :facilitator, :verify, :stop] => %{
    #       count: 42,
    #       by_status: %{ok: 40, error: 2},
    #       latency: %{count: 42, min_us: 812, max_us: 9_120, mean_us: 2_310, sum_us: 97_020}
    #     },
    #     ...
    #   }

Status is read from the `:status` metadata (`:ok` / `:error` events) or
derived from `:success` (facilitator spans). Counters are ETS atomics;
latency minima and maxima are compare-and-swap updates, so concurrent
events never lose samples.

The table needs no supervision. Call `detach/1` to stop aggregating and
drop it, or `reset/1` to zero the counters while attached.

# `event_stats`

```elixir
@type event_stats() :: %{
  count: non_neg_integer(),
  by_status: %{optional(atom()) =&gt; non_neg_integer()},
  latency: latency() | nil
}
```

Aggregated statistics for one event.

# `latency`

```elixir
@type latency() :: %{
  count: pos_integer(),
  min_us: non_neg_integer(),
  max_us: non_neg_integer(),
  mean_us: non_neg_integer(),
  sum_us: non_neg_integer()
}
```

Latency statistics in microseconds for span events.

# `snapshot`

```elixir
@type snapshot() :: %{optional(X402.Telemetry.event()) =&gt; event_stats()}
```

Snapshot of every event seen since attaching (or the last reset).

# `attach`
*since 0.9.0* 

```elixir
@spec attach(keyword()) :: :ok | {:error, :already_attached}
```

Attaches the aggregator to the x402 telemetry events.

Returns `{:error, :already_attached}` when a handler for `:name` exists.

## Options

* `:name` (`t:atom/0`) - ETS table name (also used to derive the telemetry handler id). The default value is `X402.Telemetry.Stats`.

* `:events` (list of list of `t:atom/0`) - Events to aggregate. Defaults to `X402.Telemetry.events/0`.

# `detach`
*since 0.9.0* 

```elixir
@spec detach(atom()) :: :ok
```

Detaches the aggregator and drops its table.

# `reset`
*since 0.9.0* 

```elixir
@spec reset(atom()) :: :ok
```

Zeroes every counter while staying attached.

# `snapshot`
*since 0.9.0* 

```elixir
@spec snapshot(atom()) :: snapshot()
```

Returns the aggregated statistics per event.

Events that were never observed are absent from the map.

---

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