# Clarion
[](https://hex.pm/packages/clarion)
[](https://hexdocs.pm/clarion)
[](https://github.com/singlefleck/clarion/actions/workflows/ci.yml)
[](LICENSE)
Correct structured logging for library authors: readable in a dev console,
fully queryable in production, and immune to the one failure mode that
usually destroys it — a caller's `report_cb` can never cause the original
structured data to be lost, because Clarion never calls it itself.
## The problem
```elixir
# naive: readable now, unrecoverable later
Logger.info("retrying request to #{url}, attempt #{attempt}/#{max}")
```
Fine in a terminal. Once this event reaches a JSON handler (Datadog, Loki,
anything doing programmatic log analysis), `url`, `attempt`, and `max` are
gone — flattened into one string before `:logger` ever saw them as data.
No formatter, no handler, no clever config downstream can get them back.
```elixir
# Clarion: same information, still structured everywhere it goes
require Clarion
Clarion.error(%{event: :http_retry, url: url, attempt: attempt, max: max})
```
Every consumer of this event gets what it actually wants: a console
handler renders `http_retry url=... attempt=2 max=3` via
`Clarion.default_report_cb/1`; a JSON handler gets real `url`/`attempt`/
`max` fields to filter, alert, and query on.
## Why this needs a library at all
Erlang's `:logger` already supports structured reports and a `report_cb`
rendering callback — this isn't new machinery. What's missing is a call
site API that makes the *correct* pattern the only reachable one:
`Clarion.info/2`, `.warning/2`, and `.error/2` require a map or keyword
list, not a string, so there's no code path where a report gets
interpolated into text before it ever reaches `:logger`.
Read [`docs/how_logging_actually_works.md`](docs/how_logging_actually_works.md)
for the verified, experiment-backed explanation of exactly where structure
gets destroyed in the current `Logger`/`:logger` pipeline, and
[`docs/comparison.md`](docs/comparison.md) for how Clarion differs from
JSON formatters, text formatters, and log-shipping backends that already
exist on Hex — short version: those all operate on an event *after* it
exists; Clarion operates at the call site that creates it.
## Installation
```elixir
def deps do
[
{:clarion, "~> 0.1.0"}
]
end
```
## Quick start
```elixir
defmodule MyHttpClient do
require Clarion
def request(url) do
case do_request(url) do
{:ok, response} ->
Clarion.info(%{event: :http_request_succeeded, url: url, status: response.status})
{:ok, response}
{:error, reason} ->
Clarion.error(%{event: :http_request_failed, url: url, reason: reason})
{:error, reason}
end
end
end
```
See [`docs/getting_started.md`](docs/getting_started.md) for custom
`report_cb`s, passing extra metadata, and testing that your own logging
stays structured.
## Documentation
Full docs, including all extras above, are on
[HexDocs](https://hexdocs.pm/clarion).
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md).
## License
MIT — see [LICENSE](LICENSE).