# `Snodo.Instrumentation`
[🔗](https://github.com/joshrotenberg/snodo/blob/v0.4.3/lib/snodo/instrumentation.ex#L1)

Optional, dependency-free lifecycle instrumentation.

Instrumentation sinks receive telemetry-shaped event names, numeric
measurements, and deliberately bounded metadata. Sink failures are isolated:
observability cannot change protocol, transport, or Tasks behavior.

Dispatch events for `tools/call`, `prompts/get`, and `resources/read` also
carry `:component`: the registered tool or prompt name, or the URI of a
registered direct resource. A name the router does not register, and a
resource URI that only a template matches, is reported as `:unknown`, so the
value stays bounded by the catalog and is safe as a metric tag.

A sink may bridge events to `:telemetry`, OpenTelemetry, Logger, a metrics
process, or a test collector without making any of those systems a framework
dependency.

# `config`

```elixir
@type config() :: module() | {module(), term()} | Snodo.Instrumentation.Config.t()
```

# `event_name`

```elixir
@type event_name() :: [atom()]
```

# `handle_event`

```elixir
@callback handle_event(
  event_name(),
  measurements :: map(),
  metadata :: map(),
  options :: term()
) :: term()
```

# `emit`

```elixir
@spec emit(Snodo.Instrumentation.Config.t() | nil, event_name(), map(), map()) :: :ok
```

Emits one event and isolates every sink failure.

# `normalize!`

```elixir
@spec normalize!(config() | nil) :: Snodo.Instrumentation.Config.t() | nil
```

Normalizes and validates an optional sink configuration.

---

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