# `BeamAgent.Telemetry`
[🔗](https://github.com/beardedeagle/beam-agent/blob/main/lib/beam_agent/telemetry.ex#L1)

OpenTelemetry-style span and event emission for the BeamAgent SDK.

All five backend session handlers emit telemetry events via this module so that
consuming applications can attach handlers once and observe every backend
uniformly. No OTLP export or collector is built in — this module follows the
Erlang/OTP `:telemetry` convention: the library emits events, applications
handle them.

## Optional dependency

The `:telemetry` library is an **optional** dependency. When present, events
are emitted via `:telemetry.execute/3`. When absent, all emission is a silent
no-op with zero overhead. To enable telemetry, add `{:telemetry, "~> 1.3"}` to
your application's `deps` in `mix.exs` and ensure the `:telemetry` application
is started.

## When to use directly vs through `BeamAgent`

Backends call this module internally during session operation. Use it directly
only when implementing a custom backend adapter or adding instrumentation to a
session handler.

## Event namespace

All events are published under the `[:beam_agent, ...]` prefix. The `agent`
parameter (an atom such as `:claude` or `:codex`) becomes the second element of
the event name list:

```
[:beam_agent, :claude, :query, :start]
[:beam_agent, :claude, :query, :stop]
[:beam_agent, :claude, :query, :exception]
[:beam_agent, :session, :state_change]   # always at this fixed path
[:beam_agent, :run, :state_change]       # canonical run lifecycle
[:beam_agent, :buffer, :overflow]        # always at this fixed path
```

## Span lifecycle example

```elixir
start_time = BeamAgent.Telemetry.span_start(:claude, :query, %{session_id: id})
# ... do work ...
BeamAgent.Telemetry.span_stop(:claude, :query, start_time)
```

## Attaching handlers

Use the standard `:telemetry.attach/4` or `:telemetry.attach_many/4` call in
your application startup:

```elixir
:telemetry.attach_many(
  :my_handler,
  [
    [:beam_agent, :claude, :query, :start],
    [:beam_agent, :claude, :query, :stop]
  ],
  &MyTelemetryHandler.handle/4,
  []
)
```

See the `:telemetry` library documentation for handler function signature details.

# `buffer_overflow`

```elixir
@spec buffer_overflow(pos_integer(), pos_integer()) :: :ok
```

Emit a buffer overflow warning when accumulated transport data exceeds the limit.

The event is published at the fixed path `[:beam_agent, :buffer, :overflow]`.
This event fires when the session engine's inbound buffer grows beyond the
configured maximum, which typically signals a misbehaving backend or extremely
large responses.

Parameters:
- `buffer_size` — current size of the buffer in bytes
- `max` — the configured maximum in bytes

# `span_exception`

```elixir
@spec span_exception(atom(), atom(), term()) :: :ok
```

Emit a span exception event when a unit of work fails.

The event is published at `[:beam_agent, agent, event_suffix, :exception]`.
Call this instead of `span_stop/3` when the work raised an error or exception.

Parameters:
- `agent` — backend atom
- `suffix` — operation atom, must match the `span_start/3` call
- `reason` — the error reason or exception term

# `span_exception`

```elixir
@spec span_exception(atom(), atom(), term(), map()) :: :ok
```

Emit a span exception event with additional metadata.

Same as `span_exception/3` but merges caller-supplied metadata into the
emitted exception event.

# `span_start`

```elixir
@spec span_start(atom(), atom(), map()) :: integer()
```

Emit a span start event and return a monotonic start time.

The returned integer must be passed unchanged to `span_stop/3` or
`span_exception/3` so the duration can be computed.

Parameters:
- `agent` — backend atom, e.g. `:claude`, `:codex`, `:gemini`
- `event_suffix` — atom labelling the operation, e.g. `:query`, `:connect`
- `metadata` — arbitrary map attached to the telemetry event

Returns a monotonic integer (result of `:erlang.monotonic_time/0`).

## Example

```elixir
t = BeamAgent.Telemetry.span_start(:claude, :query, %{session_id: id})
# work...
BeamAgent.Telemetry.span_stop(:claude, :query, t)
```

# `span_stop`

```elixir
@spec span_stop(atom(), atom(), integer()) :: :ok
```

Emit a span stop event, computing duration from the start time.

The event is published at `[:beam_agent, agent, event_suffix, :stop]` with a
`duration` measurement in native time units.

Parameters:
- `agent` — same atom passed to `span_start/3`
- `suffix` — same atom passed to `span_start/3`
- `start_time` — the integer returned by `span_start/3`

# `span_stop`

```elixir
@spec span_stop(atom(), atom(), integer(), map()) :: :ok
```

Emit a span stop event with additional metadata.

Same as `span_stop/3` but merges caller-supplied metadata into the stop event.

# `state_change`

```elixir
@spec state_change(atom(), atom(), atom()) :: :ok
```

Emit a state change event for a `gen_statem` transition.

The event is published at the fixed path `[:beam_agent, :session, :state_change]`.
Backend session handlers call this on every state machine transition so that
consumers can observe the full session lifecycle.

Parameters:
- `agent` — backend atom identifying which session handler fired
- `from_state` — the state the session is leaving (e.g. `:connecting`, `:ready`)
- `to_state` — the state the session is entering

Valid state atoms: `:connecting`, `:initializing`, `:ready`, `:active_query`,
`:error`.

# `state_change`

```elixir
@spec state_change(atom(), atom(), atom(), map()) :: :ok
```

Emit a state change event for a non-session BeamAgent domain.

The event is published at `[:beam_agent, domain, :state_change]`.
Canonical BeamAgent domains such as runs, steps, and routines use this helper
to report lifecycle transitions without inventing a second telemetry style.

---

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