tutorial

How to Monitor Rollbax with Vigilmon

Combine Rollbax error tracking with Vigilmon uptime monitoring to get full-stack observability for your Elixir application — detect both code-level exceptions and infrastructure-level outages.

How to Monitor Rollbax with Vigilmon

Rollbax is the Elixir client for Rollbar, the error tracking and exception monitoring platform. It captures uncaught exceptions, error logs, and custom telemetry events from your Elixir application and forwards them to the Rollbar dashboard with metadata: environment, host, request context, and custom user-defined attributes. It integrates with Phoenix and Plug to automatically capture HTTP request context alongside exceptions.

Rollbax solves the "what went wrong in the code" problem. But exception tracking alone has a blind spot: some failure modes produce no exceptions at all. A server that's unreachable from the internet fails silently — no request reaches the application, so no exception is captured. A health check endpoint that times out leaves no trace in Rollbar. A Kubernetes pod that's stuck in CrashLoopBackOff may restart before any exception is sent.

Vigilmon closes this gap with external uptime monitoring: probe your application from outside, detect when it's unreachable, and alert before users report issues — complementing the code-level visibility that Rollbax provides.


Why You Need Both Error Tracking and Uptime Monitoring

| Failure type | Visible in Rollbax | Visible in Vigilmon | |---|---|---| | Unhandled exception in request handler | Yes | No | | Memory leak causing OOM crash | Maybe (if Rollbax flushes before crash) | Yes | | Server unreachable (network/firewall issue) | No | Yes | | Database connection pool exhausted | Yes | Yes (500 responses) | | Deployment failure (app won't start) | No | Yes | | Silent background job failure | Yes (if logged) | Yes (via heartbeat) | | Certificate expiry | No | Yes | | Third-party API timeout | Yes | Only if it causes downstream 500s |

Running both gives you defense in depth: Rollbax catches application logic failures, Vigilmon catches infrastructure failures.


Key Metrics to Track

| Metric | Source | What it tells you | |--------|--------|-------------------| | Exception rate by class | Rollbax/Rollbar | Whether error frequency is rising or stable | | Error occurrences by environment | Rollbax | Whether prod is noisier than staging | | Uptime percentage | Vigilmon | Whether your app is reachable externally | | Health check response time | Vigilmon | Whether your app is slow before it goes down | | Heartbeat interval | Vigilmon | Whether background jobs are completing on schedule | | Error rate per deploy | Rollbax tags | Whether a release introduced new exceptions |


Step 1: Add Rollbax to Your Project

# mix.exs
defp deps do
  [
    {:rollbax, "~> 0.11"}
  ]
end

Configure in your application:

# config/runtime.exs
config :rollbax,
  access_token: System.get_env("ROLLBAR_ACCESS_TOKEN"),
  environment: System.get_env("ROLLBAR_ENVIRONMENT", "production"),
  enabled: System.get_env("ROLLBAR_ACCESS_TOKEN") != nil

Start the Rollbax reporter in your supervision tree:

# lib/my_app/application.ex
children = [
  Rollbax,
  MyApp.Repo,
  MyAppWeb.Endpoint
]

Step 2: Plug Integration for Phoenix

Capture request context alongside exceptions automatically:

# lib/my_app_web/endpoint.ex
defmodule MyAppWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :my_app

  plug Plug.RequestId
  plug Plug.Logger

  # ... other plugs

  plug MyAppWeb.Router
end

Add the Rollbax Plug exception reporter:

# lib/my_app_web/router.ex
defmodule MyAppWeb.Router do
  use MyAppWeb, :router

  use Rollbax.PlugMiddleware  # ← captures unhandled exceptions with request context

  pipeline :api do
    plug :accepts, ["json"]
  end

  # ... routes
end

The Rollbax.PlugMiddleware captures the exception, attaches Plug connection metadata (path, method, params, headers), and re-raises so Phoenix can render the error page normally.


Step 3: Custom Exception Reporting

For errors you catch and handle, report them explicitly with context:

# lib/my_app/payment_processor.ex
defmodule MyApp.PaymentProcessor do
  require Logger

  def charge(user_id, amount_cents, payment_method_id) do
    case Stripe.Charge.create(%{
      amount: amount_cents,
      currency: "usd",
      customer: payment_method_id
    }) do
      {:ok, charge} ->
        {:ok, charge}

      {:error, %Stripe.Error{code: code, message: message} = error} ->
        Rollbax.report_message(:error, "Stripe charge failed", %{
          user_id: user_id,
          amount_cents: amount_cents,
          payment_method_id: payment_method_id,
          stripe_error_code: code,
          stripe_message: message
        })

        Logger.error("Payment charge failed",
          user_id: user_id,
          stripe_code: code
        )

        {:error, error}
    end
  rescue
    e ->
      Rollbax.report(:error, e, __STACKTRACE__, %{
        user_id: user_id,
        amount_cents: amount_cents,
        context: "payment_processor.charge"
      })

      reraise e, __STACKTRACE__
  end
end

Step 4: Error-Level Routing and Custom Data

Differentiate warnings from critical errors and attach structured metadata:

# lib/my_app/error_reporter.ex
defmodule MyApp.ErrorReporter do
  @moduledoc """
  Centralized error reporting with consistent metadata.
  Report to Rollbax with structured context for faster debugging.
  """

  def report_exception(exception, stacktrace, context \\ %{}) do
    base_context = %{
      hostname: System.get_env("HOSTNAME", "unknown"),
      release: Application.spec(:my_app, :vsn) |> to_string(),
      node: node() |> to_string()
    }

    Rollbax.report(:error, exception, stacktrace, Map.merge(base_context, context))
  end

  def report_warning(message, custom_data \\ %{}) do
    Rollbax.report_message(:warning, message, custom_data)
  end

  def report_critical(message, custom_data \\ %{}) do
    Rollbax.report_message(:critical, message, custom_data)
  end
end

Step 5: Health Check Endpoint

Add a health endpoint that Vigilmon can poll externally:

# lib/my_app_web/controllers/health_controller.ex
defmodule MyAppWeb.HealthController do
  use MyAppWeb, :controller

  def index(conn, _params) do
    checks = %{
      database: check_database(),
      rollbax: check_rollbax()
    }

    all_ok = Enum.all?(checks, fn {_, v} -> v.status == "ok" end)
    status = if all_ok, do: 200, else: 503

    conn
    |> put_status(status)
    |> json(%{
      status: if(all_ok, do: "ok", else: "degraded"),
      checks: checks
    })
  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_rollbax do
    # Rollbax is healthy if it's running (its GenServer is alive)
    case Process.whereis(Rollbax.Notifier) do
      nil -> %{status: "error", reason: "Rollbax.Notifier not running"}
      pid when is_pid(pid) -> %{status: "ok"}
    end
  end
end

Route it:

# lib/my_app_web/router.ex
scope "/", MyAppWeb do
  get "/health", HealthController, :index
end

Step 6: Heartbeat Monitoring for Background Jobs

Report to both Rollbax (on failure) and Vigilmon (on success):

# lib/my_app/workers/report_generator.ex
defmodule MyApp.Workers.ReportGenerator do
  use Oban.Worker, queue: :reports

  require Logger

  @impl Oban.Worker
  def perform(%Oban.Job{args: args}) do
    with {:ok, data} <- fetch_report_data(args),
         {:ok, report} <- generate_report(data),
         {:ok, _} <- deliver_report(report) do

      ping_vigilmon_heartbeat()
      :ok
    else
      {:error, reason} ->
        MyApp.ErrorReporter.report_warning("ReportGenerator job failed", %{
          args: args,
          reason: inspect(reason)
        })

        Logger.error("Report generation failed", args: args, reason: inspect(reason))
        {:error, reason}
    end
  rescue
    e ->
      MyApp.ErrorReporter.report_exception(e, __STACKTRACE__, %{
        worker: "ReportGenerator",
        args: args
      })

      reraise e, __STACKTRACE__
  end

  defp ping_vigilmon_heartbeat do
    url = Application.get_env(:my_app, :vigilmon)[:report_heartbeat_url]

    if url do
      case :httpc.request(:get, {String.to_charlist(url), []}, [{:timeout, 5_000}], []) do
        {:ok, _} -> :ok
        {:error, reason} ->
          Logger.warning("Vigilmon heartbeat ping failed", reason: inspect(reason))
      end
    end
  end

  defp fetch_report_data(_args), do: {:ok, %{}}
  defp generate_report(_data), do: {:ok, %{}}
  defp deliver_report(_report), do: {:ok, :delivered}
end

Step 7: Set Up Monitors in Vigilmon

HTTP uptime monitor:

  1. Sign in at vigilmon.online
  2. Click New Monitor → HTTP
  3. URL: https://yourdomain.com/health
  4. Interval: 1 minute (paid) or 5 minutes (free)
  5. Alert condition: non-200 response

Heartbeat monitor for Oban jobs:

  1. Click New Monitor → Heartbeat
  2. Set expected interval matching your job schedule (e.g., 25 hours for a daily job with grace period)
  3. Copy the ping URL
  4. Set as VIGILMON_REPORT_HEARTBEAT_URL in your application config

SSL certificate monitor:

  1. Click New Monitor → SSL
  2. Enter your domain
  3. Alert 14 days before expiry — certificate failures produce no Rollbax exceptions

Step 8: Alerting

Slack notification for downtime:

In Vigilmon, go to Notifications → New Channel → Slack and configure your webhook. You'll receive:

🔴 DOWN: yourdomain.com/health
Status: 503 Service Unavailable  
Duration: 4 minutes
Action: Check application logs and Rollbar dashboard for root cause

Link Vigilmon alerts to Rollbar:

In your Slack downtime notification, include a deep link to your Rollbar project's error stream filtered by time:

Vigilmon detected downtime at 14:32 UTC.
Check Rollbar: https://rollbar.com/my-org/my-app/items/?status=active&since=1420032000

This gives on-call engineers one-click access to the exceptions that caused the downtime.

Rollbax critical error → Vigilmon status:

Emit a Vigilmon manual check failure when Rollbax reports a critical error that you know will cause downtime:

defmodule MyApp.CriticalErrorHandler do
  require Logger

  def report_critical_and_degrade(exception, stacktrace, context) do
    Rollbax.report(:critical, exception, stacktrace, context)

    # Optionally notify a Vigilmon status page webhook to show degraded status
    # while your team investigates, even before external monitoring detects it
    Logger.error("Critical error reported to Rollbar",
      exception: Exception.message(exception)
    )
  end
end

What You've Built

| What | How | |------|-----| | Exception tracking with request context | Rollbax + Rollbax.PlugMiddleware | | Custom error reporting with metadata | Rollbax.report/4 and Rollbax.report_message/3 | | External uptime monitoring | Vigilmon HTTP monitor on /health | | Background job monitoring | Oban worker pinging Vigilmon heartbeat on success, Rollbax on failure | | SSL certificate monitoring | Vigilmon SSL monitor | | Linked Slack alerts | Vigilmon downtime alert with Rollbar deep link |

Rollbax tells you what your code did wrong. Vigilmon tells you when your infrastructure went dark. Together they give you full-stack observability: no failure mode is invisible.


Next Steps

  • Add deploy tracking in Rollbar so errors are grouped by release version
  • Use Rollbar's rate limiting rules to suppress expected errors (e.g., 404s) so your error stream stays actionable
  • Set up Vigilmon response time history to catch latency regressions before they cause timeouts that Rollbax would report as exceptions
  • Add a Vigilmon monitor for each external API your application depends on, and correlate those outages with spikes in Rollbar error counts

Get started free at vigilmon.online.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →