kino_ex_ratatui emits :telemetry events at the boundaries the widget itself controls. They sit one layer above the events ex_ratatui already emits — together they give you a complete profile of a Livebook-driven TUI.
Event tree at a glance
[:kino_ex_ratatui, :transport, :connect] span — widget boots Session + runtime
[:kino_ex_ratatui, :transport, :disconnect] single — server :DOWN or widget terminate
[:kino_ex_ratatui, :render, :frame] span — ANSI broadcast over the Kino bridge
[:kino_ex_ratatui, :input, :forward] single — bytes from xterm.js → runtime
[:kino_ex_ratatui, :resize] single — resizes after the boot oneSpan events emit :start / :stop / :exception suffixes. Most handlers only attach to :stop for timing and :exception for failures.
See Kino.ExRatatui.Telemetry for the full metadata reference.
Quick start: log every event
# In a Livebook setup cell, or your application's start/2 callback:
Kino.ExRatatui.Telemetry.attach_default_logger(level: :info)Detach with Kino.ExRatatui.Telemetry.detach_default_logger/0.
Wiring Telemetry.Metrics
If you're already running Telemetry.Metrics (e.g. in a Phoenix LiveDashboard), add these alongside whatever ex_ratatui metrics you care about:
defmodule MyApp.Telemetry do
import Telemetry.Metrics
def metrics do
[
# Time-to-first-frame on widget boot.
summary("kino_ex_ratatui.transport.connect.stop.duration",
unit: {:native, :millisecond}
),
# How many sessions opened / closed.
counter("kino_ex_ratatui.transport.disconnect"),
# Per-frame ANSI broadcast cost (typically microseconds).
summary("kino_ex_ratatui.render.frame.stop.duration",
unit: {:native, :microsecond}
),
# Input bytes flowing client → server.
sum("kino_ex_ratatui.input.forward.byte_count"),
# Resize storms (windows, splits, …).
counter("kino_ex_ratatui.resize")
]
end
endPairing with ex_ratatui's events
The two trees are complementary, not duplicative:
| Concern | Owned by |
|---|---|
mount/1 runtime, App handle_event/2, render command building | [:ex_ratatui, ...] |
Widget boot (network handshake + first Transport.start_server/1), ANSI broadcast over the Kino bridge, client input forwarding | [:kino_ex_ratatui, ...] |
Attach to whichever you need. A typical setup attaches [:ex_ratatui, :runtime, :event, :stop] for App-level latency and [:kino_ex_ratatui, :render, :frame, :stop] for the wire cost — together you can spot where time is going without instrumenting either layer manually.
Custom handlers
The public helpers Kino.ExRatatui.Telemetry.span/3 and Kino.ExRatatui.Telemetry.execute/3 are thin wrappers around :telemetry.span/3 and :telemetry.execute/3 that prepend :kino_ex_ratatui to the event name. Use them if you're building a higher-level wrapper around Kino.ExRatatui.new/2 and want to emit your own events under the same namespace.
For one-off handlers, attach directly:
:telemetry.attach(
"my-app-frame-tracker",
[:kino_ex_ratatui, :render, :frame, :stop],
&MyApp.TuiTracker.handle_frame/4,
nil
)Always use a captured module function (&MyApp.TuiTracker.handle_frame/4), not an anonymous function — :telemetry logs a performance warning otherwise.