Sourceror is an Elixir library for reading, analyzing, and transforming Elixir source code at the quoted expression (AST) level. It preserves source location metadata, handles comments, and provides a zipper-based traversal API that makes surgical code transformations safe and composable. Libraries like Igniter use Sourceror under the hood to apply precise patches to config files and module definitions. In production, Sourceror shows up wherever static analysis, automated refactoring, or code quality enforcement runs at scale: CI pipelines that audit module structure, migration generators that patch existing files, or build-time tools that enforce architectural constraints. When these jobs fail silently, bad code accumulates undetected. Vigilmon gives you heartbeat and HTTP monitoring so Sourceror-powered pipelines surface failures immediately.
What You'll Set Up
- HTTP health endpoint for services that run Sourceror analysis at request time
- Heartbeat monitor for CI jobs and background workers that run Sourceror pipelines
- Slack alerts on analysis pipeline failure
- SSL monitoring for code analysis API services
Prerequisites
- Elixir 1.14+ with
sourcerorinmix.exs - A pipeline that runs Sourceror analysis or transformation (CI job, Mix task, GenServer worker)
- A free Vigilmon account
Step 1: Identify What to Monitor in Your Sourceror Pipeline
Sourceror usage falls into a few categories, each with a different monitoring approach:
| Usage pattern | Monitoring approach | |---------------|-------------------| | One-off local refactoring | Nothing — failure is immediate | | CI code quality gate | Heartbeat per scheduled CI run | | Background analysis service | HTTP health + heartbeat | | Build-time code mod (Mix task) | Heartbeat in deploy pipeline |
This tutorial covers the two runtime patterns: background analysis services and scheduled CI pipelines.
Step 2: Add a Health Endpoint for Analysis Services
If you run a service that accepts source code for analysis (e.g. an internal API that checks module structure or enforces naming conventions), add a health endpoint that exercises Sourceror end-to-end:
# lib/my_analysis_web/controllers/health_controller.ex
defmodule MyAnalysisWeb.HealthController do
use MyAnalysisWeb, :controller
@sample_source """
defmodule Sample do
def hello, do: :world
end
"""
def index(conn, _params) do
checks = %{
sourceror_parse: check_sourceror_parse(),
sourceror_traverse: check_sourceror_traverse(),
database: check_database()
}
status = if Enum.all?(checks, fn {_, v} -> v == :ok end), do: 200, else: 503
conn
|> put_status(status)
|> json(%{status: if(status == 200, do: "ok", else: "degraded"), checks: checks})
end
defp check_sourceror_parse do
case Sourceror.parse_string(@sample_source) do
{:ok, _quoted} -> :ok
{:error, _} -> :error
end
end
defp check_sourceror_traverse do
# Verify the zipper traversal works end-to-end
case Sourceror.parse_string(@sample_source) do
{:ok, quoted} ->
result =
Sourceror.Zipper.zip(quoted)
|> Sourceror.Zipper.traverse(fn zipper ->
zipper
end)
if match?(%Sourceror.Zipper{}, result), do: :ok, else: :error
{:error, _} ->
:error
end
end
defp check_database do
case Ecto.Adapters.SQL.query(MyAnalysis.Repo, "SELECT 1", []) do
{:ok, _} -> :ok
_ -> :error
end
end
end
Wire it to a route:
# lib/my_analysis_web/router.ex
scope "/", MyAnalysisWeb do
get "/health", HealthController, :index
end
Step 3: Monitor Background Sourceror Analysis Workers
For long-running GenServers or Oban workers that run Sourceror pipelines on a schedule (e.g. nightly module structure audits), add a heartbeat:
# lib/my_app/workers/code_analysis_worker.ex
defmodule MyApp.Workers.CodeAnalysisWorker do
use Oban.Worker, queue: :analysis, max_attempts: 3
require Logger
@impl Oban.Worker
def perform(%Oban.Job{args: %{"repo_path" => repo_path}}) do
with {:ok, files} <- discover_elixir_files(repo_path),
{:ok, results} <- analyze_files(files),
:ok <- persist_results(results) do
ping_heartbeat()
:ok
else
{:error, reason} ->
Logger.error("Code analysis failed: #{inspect(reason)}")
{:error, reason}
end
end
defp discover_elixir_files(repo_path) do
files =
Path.wildcard(Path.join(repo_path, "**/*.ex"))
|> Enum.reject(&String.contains?(&1, "_build"))
{:ok, files}
end
defp analyze_files(files) do
results =
files
|> Task.async_stream(
fn file ->
source = File.read!(file)
analyze_source(file, source)
end,
max_concurrency: System.schedulers_online(),
timeout: 30_000
)
|> Enum.reduce([], fn
{:ok, result}, acc -> [result | acc]
{:error, reason}, acc ->
Logger.warning("Analysis failed for a file: #{inspect(reason)}")
acc
end)
{:ok, results}
end
defp analyze_source(file, source) do
case Sourceror.parse_string(source) do
{:ok, quoted} ->
# Example: collect all public function definitions
functions =
Sourceror.Zipper.zip(quoted)
|> Sourceror.Zipper.traverse_while([], fn zipper, acc ->
node = Sourceror.Zipper.node(zipper)
case node do
{:def, _meta, [{name, _, _args} | _]} ->
{:cont, zipper, [{file, name} | acc]}
_ ->
{:cont, zipper, acc}
end
end)
|> elem(1)
%{file: file, functions: functions, status: :ok}
{:error, reason} ->
%{file: file, error: reason, status: :error}
end
end
defp persist_results(results) do
# Store results in your database or cache
Logger.info("Analysis complete: #{length(results)} files processed")
:ok
end
defp ping_heartbeat do
url = Application.get_env(:my_app, :vigilmon)[:analysis_heartbeat_url]
if url do
case Req.get(url, receive_timeout: 5_000) do
{:ok, _} -> :ok
{:error, reason} ->
Logger.warning("Vigilmon heartbeat ping failed: #{inspect(reason)}")
end
end
end
end
Configure the heartbeat URL:
# config/runtime.exs
config :my_app, :vigilmon,
analysis_heartbeat_url: System.get_env("VIGILMON_ANALYSIS_HEARTBEAT_URL")
Step 4: Wrap Mix Task Pipelines with Heartbeat Pings
For Sourceror-powered Mix tasks in your deploy pipeline (e.g. mix code.audit that uses Sourceror to verify module structure before promotion), add a heartbeat ping at the end:
#!/bin/bash
# scripts/run_code_audit.sh
set -euo pipefail
echo "Running Sourceror code audit..."
mix code.audit --strict
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
echo "Audit passed — pinging Vigilmon heartbeat..."
curl -fsS "$VIGILMON_AUDIT_HEARTBEAT_URL" > /dev/null
echo "Heartbeat sent."
else
echo "Audit failed with exit code $EXIT_CODE"
exit $EXIT_CODE
fi
Set the expected heartbeat interval to match your deploy cadence. If you deploy daily, set a 26-hour window with a 2-hour tolerance.
Step 5: Set Up Monitoring in Vigilmon
HTTP health monitor (for analysis API services):
- Sign in at vigilmon.online
- New Monitor → HTTP
- URL:
https://your-analysis-service.com/health - Expected status:
200 - Interval: 1 minute
Heartbeat monitor (for background analysis workers):
- New Monitor → Heartbeat
- Name:
sourceror-code-analysis - Expected interval: match your schedule (e.g. 24 hours for nightly audits)
- Tolerance: 1 hour
- Copy the ping URL and set it as
VIGILMON_ANALYSIS_HEARTBEAT_URL
Heartbeat monitor (for deploy-time Mix task):
- New Monitor → Heartbeat
- Name:
sourceror-audit-pipeline - Expected interval: match your deploy frequency
- Tolerance: 2 hours
Step 6: Configure Slack Alerts
- In Vigilmon: Notifications → New Channel → Slack
- Paste your Slack incoming webhook URL
- Enable on all monitors
When the nightly analysis job misses its window, you'll see:
🔴 MISSED: sourceror-code-analysis
Last ping: 25 hours ago (expected: 24 hours)
When it recovers:
✅ RECOVERED: sourceror-code-analysis
Gap: 1 hour 4 minutes
Step 7: SSL Monitoring for Code Analysis APIs
If your Sourceror-based service is exposed over HTTPS:
- New Monitor → SSL Certificate
- URL:
https://your-analysis-service.com - Alert: 14 days before expiry
An expired certificate blocks external callers from reaching your analysis API, and the service itself continues running — the error is only visible to callers, not to internal health checks.
What You've Built
| What | How |
|------|-----|
| Active health check | /health exercising Sourceror parse and zipper traversal |
| Background analysis worker | Heartbeat monitor with 24-hour window |
| Deploy-time audit pipeline | Heartbeat in CI/CD bash script |
| Slack alerts | Downtime and missed-heartbeat notifications |
| SSL monitoring | Certificate expiry alert |
Sourceror gives you precise control over your Elixir codebase. Vigilmon makes sure the pipelines running Sourceror don't fail quietly in the background.
Next Steps
- Add a separate heartbeat per analysis dimension (module naming, public API surface, dependency graph) to isolate which check is failing
- Use Vigilmon's response time history to track audit job duration — growth indicates accumulating tech debt or a growing codebase
- Add a monitor per repository if you run cross-repo analysis
- If you use Sourceror inside a Phoenix LiveDashboard custom page, add a monitor for the LiveDashboard endpoint
Get started free at vigilmon.online.