Packages

Phoenix LiveView dashboard for browsing Tower error events

Current section

Files

Jump to
tower_web README.md
Raw

README.md

# TowerWeb

A Phoenix LiveView dashboard for viewing and storing Tower events.

## Overview

TowerWeb provides a web interface to monitor error events captured by [Tower](https://github.com/mimiquate/tower), and includes its own Ecto-backed reporter to persist those events to a PostgreSQL database — no separate storage package required.

> [!IMPORTANT]
> **[`tower_db`](https://github.com/mimiquate/tower_db) is being deprecated in favor of this built-in storage.** If your app currently depends on `tower_db`, migrate to `tower_web`'s own storage and remove the `tower_db` dependency — see [Migrating from `tower_db`](#migrating-from-tower_db) below. New installs should skip `tower_db` entirely.

> [!WARNING]
> **Not production ready.** TowerWeb is under active development.  We use it in several applications at [Mimiquate](https://mimiquate.com), but it hasn't yet reached the bar we'd consider production ready. Use it at your own risk.
>
> Features we're waiting on before calling it production ready:
>
> - Performance test

## Installation

Add `tower_web` as a dependency in your `mix.exs`:

```elixir
def deps do
  [
    {:tower_web, "~> 0.9.0"}
  ]
end
```

## Setup

### 1. Configure the reporter

Register `TowerWeb.DB` as a Tower reporter, and point it at your application's Ecto repo:

```elixir
config :tower, :reporters, [TowerWeb.DB]
```

```elixir
# runtime.exs
config :tower_web,
  enabled: true,
  repo: MyApp.Repo,
  base_url: "https://myapp.com/errors"
```

### 2. Add and run the migration

Generate a migration:

```
mix ecto.gen.migration add_tower_web_db
```

Fill it with:

```elixir
defmodule MyApp.Repo.Migrations.AddTowerWebDB do
  use Ecto.Migration

  def up, do: TowerWeb.DB.Migration.up(from: 0, to: 3)
  def down, do: TowerWeb.DB.Migration.down(from: 3, to: 0)
end
```

Then run:

```
mix ecto.migrate
```

### 3. Mount the dashboard

In your Phoenix router, import and mount the dashboard:

```elixir
defmodule MyAppWeb.Router do
  use MyAppWeb, :router

  import TowerWeb.Router

  scope "/" do
    pipe_through :browser
    tower_dashboard "/tower"
  end
end
```

## Migrating from `tower_db`

If your app already depends on the standalone [`tower_db`](https://github.com/mimiquate/tower_db) package, move to `tower_web`'s built-in storage instead of running both:

1. `tower_web` doesn't rename your existing `tower_db_events` table for you, so add a migration that does it yourself, generated *before* the one from [Setup](#setup) step 2 so it runs first:

   ```elixir
   defmodule MyApp.Repo.Migrations.RenameTowerDbEvents do
     use Ecto.Migration

     def up, do: execute("ALTER TABLE IF EXISTS tower_db_events RENAME TO tower_web_events")
     def down, do: execute("ALTER TABLE IF EXISTS tower_web_events RENAME TO tower_db_events")
   end
   ```

   With `tower_web_events` already in place, the Setup migration becomes a no-op for the table itself (it only adds the extension and any missing indexes). Add both migrations, then run `mix ecto.migrate` once — Ecto applies pending migrations in timestamp order, so the rename runs before Setup's.
2. Replace `TowerDB` with `TowerWeb.DB` everywhere it's referenced in your app: `config :tower, :reporters, [...]`, any `config :tower_db, ...` keys (move them to `config :tower_web, ...`), and any direct calls like `TowerDB.Reporter.report_event/1`.
3. Remove `{:tower_db, ...}` from your `mix.exs` deps.

Don't keep both `tower_db` and `tower_web` installed and enabled long-term — that leaves you with two independent tables (`tower_db_events` and `tower_web_events`) and two reporters that could both persist the same event. `tower_db` is going away; `tower_web` is the path forward.

## Configuration

### Pruner

TowerWeb periodically prunes old events so the table doesn't grow unbounded. Configure it with:

```elixir
config :tower_web,
  pruner: [
    max_age: {90, :days},
    max_size: 100_000,
    max_size_per_issue: 1_000,
    interval: {30, :seconds},
    batch_size: 1_000
  ]
```

Set `pruner: false` to disable it entirely.

### Burst protection

TowerWeb drops events once a configured rate is exceeded, protecting storage and the database from sudden error storms (e.g. a crash loop). It's always on; configure `:burst_protection` under `:tower_web` to change its settings:

```elixir
# config/config.exs

config :tower_web, burst_protection: [max_count: 500, interval: 30]
```

Available settings, all optional:

| key         | meaning                                    | default |
| ----------- | -------------------------------------------- | ------- |
| `max_count` | events allowed per window before dropping    | `100`   |
| `interval`  | length of each window, in seconds            | `10`    |

Events beyond `max_count` are dropped until the window resets every `interval` seconds. A summary of dropped events, if any, is logged on reset.

### Enable/disable at runtime

```elixir
TowerWeb.DB.enable()
TowerWeb.DB.disable()
```

## Usage

Start your Phoenix server and visit `/tower` to see the dashboard.

## License

See LICENSE file.