---
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`
