Packages

Production-grade Elixir client for the Aurinko Unified Mailbox API. Covers Email, Calendar, Contacts, Tasks, Webhooks, and Booking with caching, rate-limiting, circuit breaking, streaming pagination, and telemetry.

Current section

Files

Jump to
aurinko lib aurinko logger json_formatter.ex
Raw

lib/aurinko/logger/json_formatter.ex

defmodule Aurinko.Logger.JSONFormatter do
@moduledoc """
Structured JSON log formatter for production and staging environments.
Produces one JSON object per log line, compatible with Datadog, Loki,
Google Cloud Logging, and other log aggregation pipelines.
## Sample output
{"time":"2024-06-01T14:23:01.456Z","level":"info","msg":"[Aurinko] ← ok 42ms GET /email/messages","pid":"<0.234.0>","module":"Aurinko.HTTP.Client","request_id":"req-abc123"}
## Usage
Configure in `prod.exs` / `staging.exs`:
config :logger, :console,
format: {Aurinko.Logger.JSONFormatter, :format},
metadata: [:request_id, :module, :function, :line, :pid]
"""
# timestamp is {{year,month,day},{hour,min,sec,ms}} as passed by the Logger backend
@type log_timestamp :: {{pos_integer(), 1..12, 1..31}, {0..23, 0..59, 0..59, 0..999}}
@spec format(Logger.level(), Logger.message(), log_timestamp(), keyword()) :: binary()
def format(level, message, timestamp, metadata) do
%{
time: format_timestamp(timestamp),
level: level,
msg: IO.iodata_to_binary(message),
pid: inspect(Keyword.get(metadata, :pid, self())),
module: Keyword.get(metadata, :module),
function: Keyword.get(metadata, :function),
line: Keyword.get(metadata, :line),
request_id: Keyword.get(metadata, :request_id)
}
|> Map.reject(fn {_, v} -> is_nil(v) end)
|> Jason.encode!()
|> Kernel.<>("\n")
rescue
_ -> "#{level} #{message}\n"
end
defp format_timestamp({date, {h, m, s, ms}}) do
{year, month, day} = date
:io_lib.format(
"~4..0B-~2..0B-~2..0BT~2..0B:~2..0B:~2..0B.~3..0BZ",
[year, month, day, h, m, s, ms]
)
|> IO.iodata_to_binary()
end
end