# `PhoenixExRatatui.Telemetry`
[🔗](https://github.com/mcass19/phoenix_ex_ratatui/blob/v0.2.0/lib/phoenix_ex_ratatui/telemetry.ex#L1)

`:telemetry` integration for `phoenix_ex_ratatui`.

Mirrors the shape of [`ExRatatui.Telemetry`](`ExRatatui.Telemetry`)
one layer up — events fire at the boundaries this package controls
(LiveView mount, frame push, client input forward, Transport
shutdown) rather than at the runtime / session layer
`ex_ratatui` already instruments. Both fire concurrently when a
Phoenix-driven TUI is running, and consumers attach handlers to
whichever they need.

## Why a separate event tree

`ex_ratatui` measures the App / runtime layer:
`[:ex_ratatui, :runtime, :event]`, `[:ex_ratatui, :render, :frame]`,
`[:ex_ratatui, :session, :lifecycle, *]`, and so on. Those events
fire from inside ExRatatui.Server regardless of transport.

`phoenix_ex_ratatui` adds events for the Phoenix-side cost:

  * mount + Transport boot (network handshake, App mount, first
    `take_cells_diff/1` returning the full grid)
  * the per-frame work the LiveView itself does on top of the
    runtime: encoding the diff to JSON, calling `push_event/3`,
    pushing over the WebSocket
  * client input being decoded and forwarded into the runtime
  * Transport teardown

Attaching handlers to BOTH event trees gives a complete profile of
a Phoenix-driven TUI without double-counting any single operation.

## Events

All events are prefixed with `:phoenix_ex_ratatui`.

### Span events (`:start` / `:stop` / `:exception`)

Each span emits three events with the suffix appended. Handlers
typically attach to `:stop` for timing and `:exception` for
failure tracking.

| Event | Description | Metadata |
| ----- | ----------- | -------- |
| `[:phoenix_ex_ratatui, :transport, :connect]` | `PhoenixExRatatui.Transport.start_link/1` — constructs `CellSession`, boots ExRatatui.Server, runs `mount/1`, ships first frame. | `:mod`, `:width`, `:height`, `:target` |
| `[:phoenix_ex_ratatui, :render, :frame]` | Per-frame Phoenix-side work: `Renderer.Html.encode_diff/1` + `Phoenix.LiveView.push_event/3`. | `:mod`, `:width`, `:height`, `:ops_count` |

`:start` events carry `%{monotonic_time: integer, system_time: integer}`
as measurements. `:stop` events add `:duration` (native units). On
exception the metadata gains `:kind`, `:reason`, and `:stacktrace`.

### Single events

| Event | Description | Measurements | Metadata |
| ----- | ----------- | ------------ | -------- |
| `[:phoenix_ex_ratatui, :transport, :disconnect]` | `Transport.stop/2` was called and the runtime server is being torn down. Does not fire on linked-process EXIT — `ex_ratatui`'s own `[:ex_ratatui, :transport, :disconnect]` covers that path. | `%{system_time: integer}` | `:mod`, `:reason` |
| `[:phoenix_ex_ratatui, :input, :forward]` | A decoded client input is forwarded to the runtime via `Transport.push_event/2`. | `%{system_time: integer}` | `:mod`, `:event` |
| `[:phoenix_ex_ratatui, :intent, :dispatch]` | A runtime intent was dispatched to the LV/LC socket (e.g. `{:navigate, "/path"}` → `push_navigate`). Fires once per intent, regardless of recognised vs unrecognised. | `%{system_time: integer}` | `:intent` |

## Attaching a default logger

    # In Application.start/2 (or iex during dev):
    PhoenixExRatatui.Telemetry.attach_default_logger()

That attaches a handler logging every `:stop` and single event at
`:debug` level. Pass `attach_default_logger(level: :info)` to bump
the level. Detach with `detach_default_logger/0`.

Real apps wire `Telemetry.Metrics` instead — define metrics in a
`MyApp.Telemetry` module and they show up in the `LiveDashboard`'s
Metrics tab automatically. Example:

    defmodule MyApp.Telemetry do
      import Telemetry.Metrics

      def metrics do
        [
          summary("phoenix_ex_ratatui.transport.connect.stop.duration",
            unit: {:native, :millisecond}),
          counter("phoenix_ex_ratatui.transport.disconnect"),
          summary("phoenix_ex_ratatui.render.frame.stop.duration",
            unit: {:native, :microsecond}),
          counter("phoenix_ex_ratatui.input.forward")
        ]
      end
    end

# `attach_default_logger`

```elixir
@spec attach_default_logger(keyword()) :: :ok | {:error, :already_exists}
```

Attaches a logger that prints every `phoenix_ex_ratatui` telemetry
event. Useful during development; detach with
`detach_default_logger/0`.

## Options

  * `:level` — log level (default: `:debug`).
  * `:events` — list of event suffixes to attach (default: all
    `:stop` and single events).

# `detach_default_logger`

```elixir
@spec detach_default_logger() :: :ok | {:error, :not_found}
```

Detaches the default logger previously attached with
`attach_default_logger/1`.

# `execute`

```elixir
@spec execute([atom(), ...], map(), map()) :: :ok
```

Emits a single `:telemetry` event rooted at
`[:phoenix_ex_ratatui | event]`.

`:system_time` is added to the measurements automatically if not
already present.

# `span`

```elixir
@spec span([atom(), ...], map(), (-&gt; term())) :: term()
```

Wraps `fun` in a `:telemetry` span rooted at
`[:phoenix_ex_ratatui | event]`.

The `fun`'s return value is returned unchanged. The given `meta`
is forwarded to both the `:start` and `:stop` events.

---

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