Clarion

Hex.pmHex DocsCILicense: MIT

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

# 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.

# 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 for the verified, experiment-backed explanation of exactly where structure gets destroyed in the current Logger/:logger pipeline, and 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

def deps do
[
{:clarion, "~> 0.1.0"}
]
end

Quick start

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 for custom report_cbs, passing extra metadata, and testing that your own logging stays structured.

Documentation

Full docs, including all extras above, are on HexDocs.

Contributing

See CONTRIBUTING.md.

License

MIT — see LICENSE.