How to Monitor Honeybadger for Elixir with Vigilmon
Honeybadger is an error monitoring, uptime tracking, and performance monitoring client for Elixir and Phoenix. The honeybadger Hex package automatically captures unhandled exceptions in Phoenix request handlers, integrates with Plug to attach request context (path, params, user) to every error report, supports custom check-ins (cron job monitoring), and can report custom errors with structured metadata.
Honeybadger's built-in uptime checks work from Honeybadger's own infrastructure. Vigilmon gives you a second external probe from different infrastructure and geographic regions — a critical complement when your application needs defense-in-depth: two independent monitoring systems with different failure modes means one is always watching even if the other experiences an outage.
This tutorial sets up Honeybadger for exception monitoring and Vigilmon for uptime and heartbeat monitoring, giving your Phoenix application complete observability coverage.
Why Use Honeybadger and Vigilmon Together?
Honeybadger's built-in uptime monitoring is convenient. Vigilmon adds:
- Independent infrastructure — if Honeybadger's own systems have an incident, your uptime monitoring continues
- Multi-region probes — Vigilmon checks from multiple geographic regions, catching CDN and regional routing failures that a single-probe check would miss
- Heartbeat monitoring — detect when background jobs stop running, even if they produce no exceptions
- Response time history — track latency trends over time and catch regressions before they become outages
- Status pages — give your users a public status page showing both uptime and incident history
Key Metrics to Track
| Metric | Source | What it tells you | |--------|--------|-------------------| | Unhandled exception count by class | Honeybadger | Error frequency and new error introductions | | Affected users count per error | Honeybadger | Business impact of each error | | Uptime percentage | Vigilmon | External availability from multiple regions | | Response time p95/p99 | Vigilmon | Whether the app is slow before it goes down | | Heartbeat interval | Vigilmon | Whether scheduled jobs complete on time | | Deploy error rate change | Honeybadger | Whether a release introduced new failures |
Step 1: Add Honeybadger to Your Project
# mix.exs
defp deps do
[
{:honeybadger, "~> 0.21"}
]
end
Configure in your application:
# config/runtime.exs
config :honeybadger,
api_key: System.get_env("HONEYBADGER_API_KEY"),
environment_name: System.get_env("HONEYBADGER_ENV", "production"),
exclude_envs: [:dev, :test],
breadcrumbs_enabled: true,
ecto_repos: [MyApp.Repo]
Add to your supervision tree:
# lib/my_app/application.ex
children = [
Honeybadger,
MyApp.Repo,
MyAppWeb.Endpoint
]
Step 2: Phoenix and Plug Integration
Honeybadger integrates with Phoenix automatically when you add use Honeybadger.Plug to your endpoint or router:
# lib/my_app_web/router.ex
defmodule MyAppWeb.Router do
use MyAppWeb, :router
use Honeybadger.Plug
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_live_flash
plug :put_root_layout, html: {MyAppWeb.Layouts, :root}
plug :protect_from_forgery
plug :put_secure_browser_headers
end
pipeline :api do
plug :accepts, ["json"]
end
scope "/", MyAppWeb do
pipe_through :browser
get "/", PageController, :home
get "/health", HealthController, :index
end
end
Honeybadger.Plug catches unhandled exceptions, attaches the Plug connection (path, params, method, IP), and re-raises so Phoenix renders error pages normally.
Step 3: User Context for Better Error Attribution
Attach user context so every exception includes who was affected:
# lib/my_app_web/plugs/set_honeybadger_context.ex
defmodule MyAppWeb.Plugs.SetHoneybadgerContext do
import Plug.Conn
def init(opts), do: opts
def call(conn, _opts) do
case get_session(conn, :current_user_id) do
nil ->
conn
user_id ->
Honeybadger.context(
user_id: user_id,
user_email: get_session(conn, :current_user_email)
)
conn
end
end
end
Add to your router pipeline:
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug MyAppWeb.Plugs.SetHoneybadgerContext # ← add after fetch_session
plug :fetch_live_flash
# ...
end
Now every exception includes the affected user, enabling Honeybadger's "affected users" count per error.
Step 4: Custom Exception Reporting
Report caught exceptions with structured context:
# lib/my_app/order_processor.ex
defmodule MyApp.OrderProcessor do
require Logger
def process(order_id) do
order = MyApp.Repo.get!(MyApp.Order, order_id)
with {:ok, _payment} <- charge_payment(order),
{:ok, _fulfillment} <- create_fulfillment(order) do
{:ok, order}
else
{:error, :payment_declined} ->
Logger.info("Payment declined for order", order_id: order_id)
{:error, :payment_declined}
{:error, reason} ->
Honeybadger.notify("Order processing failed", %{
order_id: order_id,
customer_id: order.customer_id,
amount_cents: order.amount_cents,
reason: inspect(reason),
context: "order_processor.process"
})
Logger.error("Order processing failed", order_id: order_id, reason: inspect(reason))
{:error, reason}
end
rescue
e ->
Honeybadger.notify(e, %{
order_id: order_id,
context: "order_processor.process"
})
reraise e, __STACKTRACE__
end
defp charge_payment(_order), do: {:ok, :charged}
defp create_fulfillment(_order), do: {:ok, :fulfilled}
end
Step 5: Breadcrumbs for Request Tracing
Honeybadger breadcrumbs give you a trail of events leading to an exception:
# lib/my_app/checkout_flow.ex
defmodule MyApp.CheckoutFlow do
def run(cart, user) do
Honeybadger.add_breadcrumb("Checkout started", %{
cart_id: cart.id,
item_count: length(cart.items),
user_id: user.id
})
with {:ok, total} <- calculate_total(cart) do
Honeybadger.add_breadcrumb("Total calculated", %{total_cents: total})
with {:ok, payment} <- charge(user, total) do
Honeybadger.add_breadcrumb("Payment charged", %{payment_id: payment.id})
finalize_order(cart, payment)
end
end
end
defp calculate_total(_cart), do: {:ok, 4999}
defp charge(_user, _amount), do: {:ok, %{id: "ch_123"}}
defp finalize_order(_cart, _payment), do: {:ok, :complete}
end
When an exception occurs in this flow, Honeybadger shows the full breadcrumb trail — which step succeeded and which failed.
Step 6: Check-ins for Scheduled Jobs
Honeybadger check-ins notify the Honeybadger service when a scheduled job runs. Use these alongside Vigilmon heartbeats for defense-in-depth job monitoring:
# lib/my_app/workers/nightly_sync.ex
defmodule MyApp.Workers.NightlySync do
use Oban.Worker, queue: :scheduled
require Logger
@impl Oban.Worker
def perform(%Oban.Job{}) do
start = System.system_time(:second)
with :ok <- sync_customers(),
:ok <- sync_orders(),
:ok <- sync_inventory() do
duration_s = System.system_time(:second) - start
# Report to Honeybadger check-in
honeybadger_checkin_id = Application.get_env(:my_app, :honeybadger_nightly_checkin_id)
if honeybadger_checkin_id do
Honeybadger.checkin(honeybadger_checkin_id)
end
# Also ping Vigilmon heartbeat
ping_vigilmon()
Logger.info("Nightly sync completed", duration_seconds: duration_s)
:ok
else
{:error, reason} ->
Honeybadger.notify("NightlySync job failed", %{reason: inspect(reason)})
Logger.error("Nightly sync failed", reason: inspect(reason))
{:error, reason}
end
end
defp ping_vigilmon do
url = Application.get_env(:my_app, :vigilmon)[:nightly_sync_heartbeat_url]
if url do
:httpc.request(:get, {String.to_charlist(url), []}, [{:timeout, 5_000}], [])
end
end
defp sync_customers, do: :ok
defp sync_orders, do: :ok
defp sync_inventory, do: :ok
end
Step 7: Health Endpoint for Vigilmon
# lib/my_app_web/controllers/health_controller.ex
defmodule MyAppWeb.HealthController do
use MyAppWeb, :controller
def index(conn, _params) do
checks = %{
database: check_database(),
honeybadger: check_honeybadger()
}
all_ok = Enum.all?(checks, fn {_, v} -> v[:status] == "ok" end)
http_status = if all_ok, do: 200, else: 503
conn
|> put_status(http_status)
|> json(%{
status: if(all_ok, do: "ok", else: "degraded"),
checks: checks,
version: Application.spec(:my_app, :vsn) |> to_string()
})
end
defp check_database do
case Ecto.Adapters.SQL.query(MyApp.Repo, "SELECT 1", []) do
{:ok, _} -> %{status: "ok"}
{:error, reason} -> %{status: "error", reason: inspect(reason)}
end
rescue
e -> %{status: "error", reason: Exception.message(e)}
end
defp check_honeybadger do
# Honeybadger is healthy if its sender process is alive
case Process.whereis(Honeybadger.Sender) do
nil -> %{status: "degraded", reason: "Honeybadger.Sender not running"}
_pid -> %{status: "ok"}
end
end
end
Step 8: Set Up Monitors in Vigilmon
HTTP uptime monitor:
- Sign in at vigilmon.online
- Click New Monitor → HTTP
- URL:
https://yourdomain.com/health - Interval: 1 minute (paid) or 5 minutes (free)
- Keyword check: body contains
"status":"ok"
Heartbeat monitor for nightly sync:
- Click New Monitor → Heartbeat
- Set expected interval to 25 hours (24-hour job + 1-hour grace period)
- Copy the ping URL and set it as
VIGILMON_NIGHTLY_SYNC_HEARTBEAT_URL
SSL monitor:
- Click New Monitor → SSL
- Enter your domain
- Alert 14 days before expiry
Step 9: Alerting
Slack for downtime:
In Vigilmon, configure Notifications → New Channel → Slack. When the health endpoint fails, you receive:
🔴 DOWN: yourdomain.com/health
Status: 503 Service Unavailable
Duration: 2 minutes
Check Honeybadger: https://app.honeybadger.io/projects/YOUR_ID/faults?q=active
PagerDuty for critical alerts:
Route Vigilmon missed heartbeats to PagerDuty for immediate on-call paging. A missed nightly sync heartbeat means business data is out of sync and warrants immediate investigation.
Status page:
- Go to Status Pages → New Status Page in Vigilmon
- Add your HTTP monitor, heartbeat monitors, and SSL monitor
- Share the URL in your README and on your support page
Common Honeybadger + Vigilmon Patterns
Deployment tracking:
Tag Honeybadger errors with the current release version so you can correlate error spikes with deploys:
# config/runtime.exs
config :honeybadger,
api_key: System.get_env("HONEYBADGER_API_KEY"),
environment_name: System.get_env("HONEYBADGER_ENV", "production"),
revision: System.get_env("GIT_COMMIT_SHA", "unknown")
After a deploy, watch Honeybadger for new error classes introduced in the release while watching Vigilmon for any availability degradation.
Error rate alerting in Honeybadger:
Configure Honeybadger uptime checks to complement Vigilmon with an additional probe:
Honeybadger uptime check → https://yourdomain.com/health (from Honeybadger's infra)
Vigilmon HTTP monitor → https://yourdomain.com/health (from Vigilmon's infra)
Two independent uptime probes from different infrastructure means no single monitoring system outage creates a blind spot.
What You've Built
| What | How |
|------|-----|
| Automatic Phoenix exception capture | Honeybadger.Plug with request context |
| User attribution for errors | Honeybadger.context/1 with user session data |
| Breadcrumb trail for complex flows | Honeybadger.add_breadcrumb/2 at key steps |
| Custom exception reporting | Honeybadger.notify/2 with structured metadata |
| Scheduled job monitoring | Honeybadger check-ins + Vigilmon heartbeats |
| External uptime monitoring | Vigilmon HTTP monitor from multiple regions |
| SSL certificate monitoring | Vigilmon SSL monitor with 14-day expiry alert |
| Public status page | Vigilmon status page aggregating all monitors |
Honeybadger shows you every exception that reaches your code. Vigilmon shows you when your infrastructure is unreachable before any exception has a chance to fire. Together, no failure mode goes undetected.
Next Steps
- Add Honeybadger Insights events to track business metrics alongside errors (order rate, conversion rate) so you can detect business-level anomalies that don't produce exceptions
- Use Vigilmon's response time history to catch Phoenix endpoint latency regressions before users notice slowness
- Add Vigilmon monitors for your third-party dependencies (payment provider, email service, external APIs) and use Honeybadger's occurrence timeline to correlate third-party outages with error spikes
- Set up Vigilmon multi-region monitoring if you deploy to multiple cloud regions or use a CDN
Get started free at vigilmon.online.