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

Log messages from a request handler to the client, as `notifications/message`.

    :ok = Snodo.Logging.log(context, :info, "Import started", logger: "importer")
    :ok = Snodo.Logging.log(context, :debug, %{"rows" => 120})

The server opts in by advertising the `"logging"` capability. Which messages
reach the client then depends on the protocol revision:

  * 2026-07-28: the client sets `io.modelcontextprotocol/logLevel` in a
    request's `_meta`. Messages below that level are dropped, and a request
    without a level gets no messages at all.
  * 2025-11-25 and 2025-06-18: the client sets a level with
    `logging/setLevel`. Over stdio that level holds for the connection. Over
    HTTP these dialects keep no session, so a request uses the runtime's
    `:log_level` (default `:info`), which the older specifications allow
    when the client has not set one.

`log/4` returns `:ok` both when the message was written and when it was
dropped on purpose (no `"logging"` capability, below the level, no level, or
no channel for notifications), so a handler can match `:ok`. It returns
`{:error, reason}` for invalid input (`:invalid_level`, `:invalid_data`,
`:invalid_logger`, `:too_large`) or when the request can no longer take
messages (`:closed`, `:cancelled`, `:limit_reached`, `:timeout`,
`:wrong_process`).

Messages share the request's notification channel with progress
notifications: they are written before the request's result, one at a time,
each acknowledged after the transport writes it. A request may send at most
1,000 messages, each with at most 16 KiB of encoded `data`. Only the process
that runs the request may log; `Snodo.Component.Wrap.Timeout` hands that
right to its task.

# `level`

```elixir
@type level() ::
  :debug | :info | :notice | :warning | :error | :critical | :alert | :emergency
```

# `t`

```elixir
@opaque t()
```

# `levels`

```elixir
@spec levels() :: [level()]
```

The levels, from least to most severe (RFC 5424 severities).

# `log`

```elixir
@spec log(Snodo.Context.t(), level(), term(), keyword()) :: :ok | {:error, atom()}
```

Sends `data` at `level` for the request in `context`.

`data` is any JSON value. `opts` takes `:logger`, a string naming the
source of the message.

---

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