README.md

# ServerLogger

Persistent Erlang `:logger` handler for Elixir/Phoenix applications. Captures log events into an ETS buffer, batch-inserts them into PostgreSQL with automatic monthly partitioning, and provides configurable retention with auto-pruning. Includes an optional Phoenix LiveDashboard page for real-time log viewing and search.

## Features

- **Zero-overhead buffering** — Log events are stored as raw tuples in ETS by the caller process; formatting is deferred to flush time
- **Batch inserts** — Configurable flush interval and max buffer size; bulk `INSERT` into PostgreSQL
- **Monthly partitions** — Automatic partition creation and rotation; old partitions are dropped when all levels expire
- **Per-level retention** — Configure lifetime per log level (debug: 1 day, error: 90 days, etc.), including "never save" and "keep forever"
- **Crash recovery** — ETS table is owned by the supervisor; unflushed entries survive `BufferServer` restarts
- **LiveDashboard integration** — Optional page with filtering, sorting, search, pagination, auto-refresh, and metrics

## Installation

Add `server_logger` to your dependencies:

```elixir
def deps do
  [
    {:server_logger, "~> 0.1.0"}
  ]
end
```

## Setup

### 1. Configure

```elixir
# config/config.exs
config :server_logger,
  repo: MyApp.Repo,
  enabled: true,
  buffer_flush_interval_ms: 1_000,
  buffer_max_size: 2_000,
  lifetime_days: [
    debug: 1,
    info: 7,
    warning: 30,
    error: 90,
    critical: 0        # 0 = keep forever
  ],
  memory_limits: [
    max_message_size_mb: 8
  ]
```

### 2. Generate and run the migration

```bash
mix server_logger.gen.migration
mix ecto.migrate
```

### 3. Add to your supervision tree

```elixir
# lib/my_app/application.ex
def start(_type, _args) do
  children = [
    MyApp.Repo,
    # ... other children ...
    ServerLogger.Supervisor
  ]

  Supervisor.start_link(children, strategy: :one_for_one)
end
```

### 4. (Optional) Add LiveDashboard page

```elixir
# lib/my_app_web/router.ex
live_dashboard "/dashboard",
  additional_pages: [
    server_logs: ServerLogger.Dashboard.Page
  ]
```

## Configuration Reference

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `repo` | module | *required* | Your Ecto Repo module |
| `enabled` | boolean | `true` | Enable/disable the entire system |
| `buffer_flush_interval_ms` | integer | `1_000` | Periodic flush interval in ms |
| `buffer_max_size` | integer | `2_000` | Max ETS entries before forced flush |
| `logging_enabled` | `:stdio` \| `:stderr` \| `nil` | `nil` | ServerLogger's own diagnostic logging |
| `prune_interval_ms` | integer | `21_600_000` (6h) | How often the pruner checks for expired logs |
| `lifetime_days` | keyword | see below | Per-level retention policy |
| `memory_limits` | keyword | `[max_message_size_mb: 8]` | Message truncation limits |

### Lifetime days

- `nil` — Never save logs of this level
- `0` — Save and keep forever (never prune)
- Positive number — Prune after N days (supports 0.25 increments)

Default: `[debug: 1, info: 7, warning: 30, error: 90, critical: 0]`

## Architecture

```
┌─────────────┐     ┌───────────┐     ┌──────────────┐     ┌────────────┐
│ Your App    │────▸│  :logger  │────▸│   Handler    │────▸│ ETS Buffer │
│ Logger.info │     │  (Erlang) │     │ (caller pid) │     │  (public)  │
└─────────────┘     └───────────┘     └──────────────┘     └─────┬──────┘
                                                                 │
                                                          ┌──────▼──────┐
                                                          │ BufferServer│
                                                          │ (periodic   │
                                                          │  flush)     │
                                                          └──────┬──────┘
                                                                 │
                                                          ┌──────▼──────┐
                                                          │ PostgreSQL  │
                                                          │ (partitioned│
                                                          │  table)     │
                                                          └─────────────┘
```


## AI Disclosure

This library was vibe engineered with the assistance of Anthropic's Claude 4.6 Opus. See (docs/implementation-plans)[./docs/implementation-plans/] for implementation reference prompts.

## License

MIT