tutorial

How to Monitor PaperTrail (Ecto) with Vigilmon

PaperTrail for Ecto records every database change with timestamps, actors, and JSON diffs — but if the version table falls behind or actors go missing, your audit trail becomes unreliable. Here's how to monitor PaperTrail with Vigilmon.

How to Monitor PaperTrail (Ecto) with Vigilmon

PaperTrail gives your Ecto schemas a full version history: every insert, update, and delete creates a PaperTrail.Version record with a JSON diff, a timestamp, and optional actor metadata. That history is often what legal, compliance, or support teams reach for first — which means when it breaks, you need to know immediately, not when someone opens a ticket.

This tutorial adds monitoring for the PaperTrail version pipeline:

  • HTTP health endpoint confirming the versions table is writable
  • Heartbeat tracking that new versions are being created at the expected rate
  • Actor context coverage check for compliance
  • Vigilmon uptime and heartbeat monitors

Why Monitor PaperTrail?

| Signal | What it catches | |---|---| | Version table availability | Database connection or schema migration failures | | Write rate | Application code no longer calling PaperTrail.insert/update/delete | | Actor coverage | Missing originator metadata in version records | | Diff accuracy | Serialization errors producing null or empty diffs | | Table growth | Version table bloat degrading query performance |

PaperTrail is passive — it doesn't raise if writes fail in silent configurations. External monitoring is the only way to know when the trail goes cold.


Step 1: Add a Health Check

Create a module that queries the versions table and confirms it is reachable:

# lib/my_app/paper_trail_health.ex
defmodule MyApp.PaperTrailHealth do
  alias MyApp.Repo
  import Ecto.Query

  def check do
    since = DateTime.utc_now() |> DateTime.add(-300, :second)

    count =
      from(v in PaperTrail.Version,
        where: v.inserted_at >= ^since,
        select: count(v.id)
      )
      |> Repo.one()

    {:ok, %{versions_last_5min: count}}
  rescue
    e -> {:error, Exception.message(e)}
  end
end

Step 2: Expose a Health Endpoint

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

  def paper_trail(conn, _params) do
    case MyApp.PaperTrailHealth.check() do
      {:ok, info} ->
        json(conn, Map.put(info, :status, "ok"))

      {:error, reason} ->
        conn
        |> put_status(503)
        |> json(%{status: "error", reason: reason})
    end
  end
end

Add the route:

# lib/my_app_web/router.ex
scope "/health" do
  get "/paper-trail", HealthController, :paper_trail
end

Test it:

curl http://localhost:4000/health/paper-trail
# => {"status":"ok","versions_last_5min":15}

Step 3: Add Vigilmon Uptime Monitoring

  1. Log in to vigilmon.online and click Add Monitor.
  2. Set Type to HTTP / HTTPS.
  3. Enter your URL: https://yourapp.com/health/paper-trail.
  4. Set Check interval to 2 minutes.
  5. Set Expected HTTP status to 200.
  6. Under Advanced, add a Response body contains check: "status":"ok".
  7. Click Save.

Step 4: Heartbeat for Version Write Rate

If the application stops calling PaperTrail.insert/2 / PaperTrail.update/2, the version count drops to zero but the health endpoint still returns 200. A heartbeat guard catches this:

# lib/my_app/paper_trail_heartbeat.ex
defmodule MyApp.PaperTrailHeartbeat do
  use GenServer
  alias MyApp.Repo
  import Ecto.Query

  @interval :timer.minutes(10)
  @heartbeat_url "https://vigilmon.online/api/heartbeat/YOUR_HEARTBEAT_ID"
  # Tune to your expected minimum write rate over 10 minutes
  @min_versions_per_interval 1

  def start_link(_), do: GenServer.start_link(__MODULE__, nil, name: __MODULE__)

  def init(_) do
    schedule()
    {:ok, nil}
  end

  def handle_info(:check, state) do
    if versions_flowing?() do
      HTTPoison.get!(@heartbeat_url)
    end

    schedule()
    {:noreply, state}
  end

  defp versions_flowing? do
    since = DateTime.utc_now() |> DateTime.add(-600, :second)

    count =
      from(v in PaperTrail.Version,
        where: v.inserted_at >= ^since,
        select: count(v.id)
      )
      |> Repo.one()

    is_integer(count) and count >= @min_versions_per_interval
  end

  defp schedule, do: Process.send_after(self(), :check, @interval)
end

Add to your supervision tree:

children = [
  MyApp.Repo,
  MyAppWeb.Endpoint,
  MyApp.PaperTrailHeartbeat
]

Create the Vigilmon heartbeat monitor:

  1. Click Add MonitorCron / Heartbeat.
  2. Set expected interval to 10 minutes.
  3. Paste the generated URL into @heartbeat_url.

Step 5: Check Actor Coverage

PaperTrail records an originator_id when you pass :originator to each operation. If that's missing from version records, you lose the "who changed it" part of your audit trail:

# lib/my_app/paper_trail_actor_check.ex
defmodule MyApp.PaperTrailActorCheck do
  alias MyApp.Repo
  import Ecto.Query

  def missing_actors_last_hour do
    since = DateTime.utc_now() |> DateTime.add(-3600, :second)

    from(v in PaperTrail.Version,
      where: v.inserted_at >= ^since,
      where: is_nil(v.originator_id),
      select: count(v.id)
    )
    |> Repo.one()
  end
end

Wire this into a separate heartbeat that pings Vigilmon only when missing_actors_last_hour() == 0. Any gap means your code paths aren't passing :originator.


Step 6: Set Up Alerts

In Vigilmon:

  1. Go to Alert ChannelsAdd Channel.
  2. Configure Slack, email, or PagerDuty.
  3. Set Notify after to 2 consecutive failures.
  4. Assign to:
    • The HTTP monitor on /health/paper-trail
    • The version write-rate heartbeat
    • The actor coverage heartbeat (if created)

For regulatory environments, create a Status Page in Vigilmon and add all PaperTrail monitors. Share the URL with your compliance team so they can self-serve on audit trail availability.


Key Metrics to Watch

| Metric | Vigilmon feature | Alert threshold | |---|---|---| | Versions table availability | HTTP monitor | Any 5xx or timeout | | Version write rate | Heartbeat | Missing for > 12 minutes | | Actor coverage | Heartbeat | Missing for > 1 heartbeat | | SSL certificate | Cert expiry monitor | Expires in < 14 days | | Response time | Response time chart | P95 > 500ms |


Conclusion

PaperTrail's version trail is only as reliable as the code paths that call it. Vigilmon's HTTP monitor catches database failures, the write-rate heartbeat catches code paths that silently stop versioning, and the actor check ensures compliance metadata is flowing. Together they give you early warning before an auditor asks "where are the logs?"

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 →