tutorial

How to Monitor Algae with Vigilmon

Learn how to monitor Elixir applications using the Algae algebraic data types library — add health checks, uptime monitoring, and alerts to ensure your functional data pipelines stay healthy.

How to Monitor Algae with Vigilmon

Algae brings algebraic data types to Elixir — monoids, semirings, setoids, lattices, and other structures that enable purely functional, composable data modeling. When your business logic is built on these composable structures, correctness and predictability go hand in hand.

But algebraic purity doesn't make your process immune to infrastructure failures. Database connections drop, message queues back up, and nodes become unreachable — all while your supervisor tree cheerfully reports everything is fine. External monitoring fills that gap.

This tutorial adds production observability to an Elixir application that uses Algae:

  • A health check endpoint that reflects the state of your critical dependencies
  • HTTP uptime monitoring with Vigilmon
  • Heartbeat monitoring for long-running data pipeline workers
  • Slack alerts and a public status page

Why Monitor Algae-Based Applications?

Algae encourages building data transformations as pipelines of composable algebraic structures. A typical Algae-based app might:

  • Stream data through Maybe and Result monadic chains
  • Accumulate values with monoid-folding pipelines
  • Validate input using setoid-based equality and lattice ordering

When these pipelines process real data from external sources (databases, APIs, queues), failures in those sources can cause silent degradation: your pipeline runs, but returns Nothing or empty results for every record because the upstream is down. External monitoring catches that the application is reachable but not the data quality issue — heartbeat monitoring catches both.


Step 1: Add a Health Check Endpoint

Add a lightweight health check using Phoenix or Plug. It should verify that every external dependency your Algae pipelines depend on is alive.

# lib/my_app_web/plugs/health_check.ex
defmodule MyAppWeb.Plugs.HealthCheck do
  import Plug.Conn

  def init(opts), do: opts

  def call(%Plug.Conn{request_path: "/health"} = conn, _opts) do
    checks = %{
      database: check_database(),
      cache: check_cache(),
      pipeline_worker: check_pipeline_worker()
    }

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

    conn
    |> put_resp_content_type("application/json")
    |> send_resp(status, Jason.encode!(%{
      status: if(status == 200, do: "ok", else: "degraded"),
      checks: checks
    }))
    |> halt()
  end

  def call(conn, _opts), do: conn

  defp check_database do
    case Ecto.Adapters.SQL.query(MyApp.Repo, "SELECT 1", []) do
      {:ok, _} -> :ok
      _ -> :error
    end
  end

  defp check_cache do
    case Redix.command(:redix, ["PING"]) do
      {:ok, "PONG"} -> :ok
      _ -> :error
    end
  end

  defp check_pipeline_worker do
    if Process.whereis(MyApp.Pipeline.Worker) != nil, do: :ok, else: :error
  end
end

Register it in your endpoint before the router:

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

  plug MyAppWeb.Plugs.HealthCheck  # before router

  plug MyAppWeb.Router
end

Test it locally:

mix phx.server
curl http://localhost:4000/health
# {"status":"ok","checks":{"database":"ok","cache":"ok","pipeline_worker":"ok"}}

Step 2: Set Up HTTP Monitoring in Vigilmon

Point Vigilmon at your health endpoint:

  1. Sign up at vigilmon.online
  2. Click New Monitor → HTTP
  3. Enter https://yourdomain.com/health
  4. Set the check interval to 1 minute (paid) or 5 minutes (free)
  5. Save

Vigilmon probes from multiple geographic regions so a network partition affecting one region doesn't mask a real outage.


Step 3: Heartbeat Monitoring for Data Pipeline Workers

The subtlest failure mode in Algae-based apps is a pipeline that runs but produces no output. Your GenServer is alive, the supervisor is green, but every call to your pipeline returns Nothing because the upstream data source is unreachable.

Use heartbeat monitoring to detect this. Your worker pings a unique Vigilmon URL on every successful processing cycle. If Vigilmon doesn't receive the ping within the expected window, it alerts you.

GenServer Pipeline Worker

# lib/my_app/pipeline/worker.ex
defmodule MyApp.Pipeline.Worker do
  use GenServer
  require Logger

  alias Algae.Maybe
  alias MyApp.Pipeline.Transform

  @interval :timer.minutes(1)

  def start_link(_), do: GenServer.start_link(__MODULE__, %{}, name: __MODULE__)

  @impl true
  def init(state) do
    schedule_tick()
    {:ok, state}
  end

  @impl true
  def handle_info(:tick, state) do
    case run_pipeline() do
      {:ok, _results} ->
        ping_heartbeat()
        Logger.info("Pipeline cycle complete")
      {:error, reason} ->
        Logger.error("Pipeline cycle failed: #{inspect(reason)}")
        # No heartbeat ping → Vigilmon alerts after the window expires
    end

    schedule_tick()
    {:noreply, state}
  end

  defp run_pipeline do
    MyApp.DataSource.fetch_batch()
    |> Maybe.map(&Transform.normalize/1)
    |> Maybe.map(&Transform.validate/1)
    |> case do
      %Maybe.Just{just: results} -> {:ok, results}
      %Maybe.Nothing{} -> {:error, :empty_pipeline}
    end
  end

  defp schedule_tick, do: Process.send_after(self(), :tick, @interval)

  defp ping_heartbeat do
    url = Application.get_env(:my_app, :vigilmon)[:pipeline_heartbeat_url]
    if url do
      case Req.get(url, receive_timeout: 5_000) do
        {:ok, _} -> :ok
        {:error, reason} -> Logger.warning("Heartbeat failed: #{inspect(reason)}")
      end
    end
  end
end

Set the heartbeat URL as an environment variable:

# config/runtime.exs
config :my_app, :vigilmon,
  pipeline_heartbeat_url: System.get_env("VIGILMON_PIPELINE_HEARTBEAT_URL")

Create the Heartbeat Monitor in Vigilmon

  1. Click New Monitor → Heartbeat
  2. Set the expected interval to match your worker tick (e.g. 1 minute)
  3. Copy the ping URL and set it as VIGILMON_PIPELINE_HEARTBEAT_URL in your release environment
  4. Save

Now if your pipeline stops producing output — even if the GenServer is still alive — you get an alert.


Step 4: Alerts via Slack

In Vigilmon, go to Notifications → New Channel → Slack and paste your Slack incoming webhook URL.

To create a Slack webhook:

  1. Visit api.slack.com/appsCreate New App → From scratch
  2. Enable Incoming WebhooksAdd New Webhook to Workspace
  3. Select your alerts channel and copy the webhook URL

Assign the Slack channel to your monitors. On failure, Vigilmon sends:

🔴 DOWN: yourdomain.com/health
Status: 503 Service Unavailable
Detected from: EU-West, US-East
3 minutes ago

And on recovery:

✅ RECOVERED: yourdomain.com/health
Downtime: 8 minutes

Step 5: Status Page and Badge

Status page:

  1. Go to Status Pages → New Status Page in Vigilmon
  2. Add your HTTP and heartbeat monitors
  3. Copy the public URL

Share it with your team and in your README so anyone can check current status before filing a bug report.

README badge:

![Uptime](https://vigilmon.online/badge/your-monitor-slug)

Key Metrics to Watch

| Metric | What it signals | |--------|----------------| | Health endpoint HTTP status | Overall application reachability | | Database check result | Ecto repo connectivity | | Pipeline heartbeat | End-to-end data processing is running | | Response time trend | Latency increase before timeout failures | | Downtime frequency | Infrastructure instability patterns |


What You've Built

| What | How | |------|-----| | Health check endpoint | Plug-based, checks DB + cache + worker | | HTTP uptime monitoring | Vigilmon HTTP monitor → /health | | Pipeline heartbeat | GenServer pings Vigilmon on each successful cycle | | Slack alerts | Vigilmon Slack notification channel | | Status page | Vigilmon public status page | | README badge | /badge/{slug} SVG embed |

Algae makes your data modeling correct and composable. Vigilmon makes sure the infrastructure underneath it stays reachable.


Next Steps

  • Add per-queue heartbeats if you use Oban to schedule pipeline runs
  • Use Vigilmon's response time history to detect slow upstream sources before they cause timeouts
  • Add a keyword check on your status page to verify content — not just HTTP 200

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 →