# Structured logging

BeamAgent can attach an opt-in Logger bridge to its current telemetry events:

```elixir
config :beam_agent, :structured_logging, true
```

Restart the application after changing the setting. The supervised handler
emits the telemetry event name as its Logger message, such as
`beam_agent.run.stop`, and safe values as Logger metadata. Configure your
application's Logger backend to choose text or JSON formatting and its sink.
Logging is disabled by default and does not change run return values.

The bridge handles these events: `run.start`, `run.stop`, `run.exception`,
`verification.start`, `verification.stop`, `verification.exception`,
`context.compressed`, `context.compression_failed`, and
`guardrail.rejected`, all under the `beam_agent` prefix. It emits only
fields relevant to each event: an opaque `run_key` for run events; bounded
outcome, status, error, kind, guardrail, phase, and unit classifications;
and non-negative duration/count measurements. `beam_agent_event` holds the
same stable event name as the message. The Logger record itself has the
standard Logger timestamp.

The bridge independently validates every field, even if another producer
emits a malformed event with a BeamAgent name. It never logs prompts, goals,
messages, tool names or arguments, tool results, model responses, raw reasons,
exception structs, stacktraces, State/Run/Trace structs, or Erlang reference
values. Verification, context, and guardrail events currently have no durable
`run_key` in their telemetry contract; their log records therefore cannot be
joined to a completed run by key. Persistent traces remain a separate,
sanitized evidence surface.

Telemetry invokes handlers synchronously. Keep Logger backends appropriately
configured for your workload. If the bridge handler raises, telemetry
detaches it without changing the run result. A bridge process restart replaces
its handler attachment, so it does not accumulate duplicate handlers.
