--- id: doc-3 title: Force production logs to single-line format type: other created_date: "2026-05-03 13:53" updated_date: "2026-05-04 06:55" --- # Research: Force production logs to single-line format ## Current state **Config** (`config/config.exs`): ```elixir config :logger, :default_formatter, format: "$time $metadata[$level] $message\n", metadata: [:request_id] ``` **Override** (`config/prod.exs`): ```elixir config :logger, level: :info ``` **Log sources in prod**: | Source | Produces | Problem | | ------------------------------ | ----------------------------------------- | -------------------------------------------------- | | `Phoenix.Logger` (telemetry) | `GET /health` + `Sent 200 in Xms` | Two separate lines for one request — can't reverse | | `Phoenix.Logger` (telemetry) | `CONNECTED TO Phoenix.LiveView.Socket...` | One event with embedded `\n` — 4+ lines | | Custom `Logger.info/error/...` | Varies | Some may be multi-line | | OTP / stack traces | Crash reports | Multi-line | `Phoenix.Logger` is auto-attached by Phoenix.Endpoint and handles telemetry events including `[:phoenix, :endpoint, :start]`, `[:phoenix, :endpoint, :stop]`, and `[:phoenix, :socket_connected]`. ## Chosen approach: Option 2 (comprehensive) 1. **Disable Phoenix.Logger** — `config :phoenix, :logger, false` in prod 2. **Add Logster v2** — `Logster.attach_phoenix_logger()` for single-line HTTP request logging (logfmt format) 3. **Custom telemetry handler** — for `[:phoenix, :socket_connected]` LiveView handshakes (if not covered by Logster) 4. **Custom Logger.Formatter** — safety net replacing `\n` → `\\n` in all remaining messages 5. **Code audit** — fix multi-line Logger calls in project code ### Why Logster v2 over v1 v2 (`~> 2.0.0-rc.5`) uses `Logster.attach_phoenix_logger()` in `application.ex` instead of replacing a plug in the endpoint. This is the modern approach that works with Phoenix's telemetry-based logging architecture. It outputs logfmt (`key=value`) format by default, merging the `GET /path` and `Sent 200` lines into one. ### Architecture impact | Touchpoint | Change | | --------------------------------------------------- | ------------------------------------------------------------------------- | | `config/prod.exs` | Add `config :phoenix, :logger, false` + Logster config + formatter config | | `lib/music_library/application.ex` | Add conditional `Logster.attach_phoenix_logger()` | | `lib/music_library_web/telemetry_log_handler.ex` | **New module** — handles socket_connected telemetry | | `lib/music_library/logger/single_line_formatter.ex` | **New module** — Logger.Formatter format function | | `mix.exs` | Add `{:logster, "~> 2.0.0-rc.5"}` | | Docs | Update `docs/production-infrastructure.md` with logging config | ### Performance - Logster: one telemetry handler invocation per request, logfmt formatting is string interpolation — negligible overhead - Custom formatter: one `String.replace/3` per log event, Logger already truncates messages — negligible - No CPU or memory concerns at the log volume of a personal app ### Dependencies - `logster ~> 2.0.0-rc.5` (1.9M downloads, MIT license, active maintenance) ### Cost - None — no paid services involved --- ## Questions resolved 1. **Newline style**: Replace `\n` with `\\n` (escaped, traceable) 2. **Scope**: Prod-only; dev keeps current multi-line format for readability 3. **Module naming**: `MusicLibrary.Logger.SingleLineFormatter` and `MusicLibraryWeb.TelemetryLogHandler`