A Phoenix LiveView dashboard for viewing and storing Tower events.
TowerWeb provides a web interface to monitor error events captured by Tower, and includes its own Ecto-backed reporter to persist those events to a PostgreSQL database — no separate storage package required.
Important
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 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, 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
Add tower_web as a dependency in your mix.exs:
def deps do
[
{:tower_web, "~> 0.9.0"}
]
endRegister TowerWeb.DB as a Tower reporter, and point it at your application's Ecto repo:
config :tower, :reporters, [TowerWeb.DB]# runtime.exs
config :tower_web,
enabled: true,
repo: MyApp.Repo,
base_url: "https://myapp.com/errors"Generate a migration:
mix ecto.gen.migration add_tower_web_db
Fill it with:
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)
endThen run:
mix ecto.migrate
In your Phoenix router, import and mount the dashboard:
defmodule MyAppWeb.Router do
use MyAppWeb, :router
import TowerWeb.Router
scope "/" do
pipe_through :browser
tower_dashboard "/tower"
end
endIf your app already depends on the standalone tower_db package, move to tower_web's built-in storage instead of running both:
-
tower_webdoesn't rename your existingtower_db_eventstable for you, so add a migration that does it yourself, generated before the one from Setup step 2 so it runs first: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_eventsalready 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 runmix ecto.migrateonce — Ecto applies pending migrations in timestamp order, so the rename runs before Setup's. -
Replace
TowerDBwithTowerWeb.DBeverywhere it's referenced in your app:config :tower, :reporters, [...], anyconfig :tower_db, ...keys (move them toconfig :tower_web, ...), and any direct calls likeTowerDB.Reporter.report_event/1. -
Remove
{:tower_db, ...}from yourmix.exsdeps.
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.
TowerWeb periodically prunes old events so the table doesn't grow unbounded. Configure it with:
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.
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:
# 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.
TowerWeb.DB.enable()
TowerWeb.DB.disable()Start your Phoenix server and visit /tower to see the dashboard.
See LICENSE file.