Norm is a composable data contract library for Elixir inspired by Clojure's spec. It lets you define specs for data structures, validate and conform incoming data, and generate example data for testing. When Norm sits at the boundary between external inputs and your business logic — parsing API payloads, validating user-submitted forms, conforming event streams — silent validation failures or a crashed conformance pipeline cause corrupt data to propagate downstream. Vigilmon gives you uptime monitoring, heartbeat checks, and alerting so those failures surface immediately rather than days later during an audit.
What You'll Set Up
- HTTP health endpoint that exercises your Norm validation pipeline
- Cron heartbeat for background conformance workers
- Alerting when validation error rates spike
- SSL certificate monitoring for the services feeding data into your Norm specs
Prerequisites
- Elixir 1.14+ with the
normpackage added tomix.exs - A Phoenix or Plug-based application with at least one Norm spec in production use
- A free Vigilmon account
Step 1: Add a Health Endpoint That Exercises Your Norm Specs
Rather than a trivial ping endpoint, exercise a representative Norm spec so the health check fails if your validation logic is broken:
# lib/my_app_web/controllers/health_controller.ex
defmodule MyAppWeb.HealthController do
use MyAppWeb, :controller
alias MyApp.Specs
def index(conn, _params) do
case validate_norm_pipeline() do
:ok ->
json(conn, %{status: "ok", norm: "operational"})
{:error, errors} ->
conn
|> put_status(503)
|> json(%{status: "error", norm: "degraded", errors: inspect(errors)})
end
end
defp validate_norm_pipeline do
# Conform a known-good fixture through your core spec
sample = %{id: 1, email: "healthcheck@example.com", role: "user"}
case Norm.conform(sample, Specs.user_spec()) do
{:ok, _conformed} -> :ok
{:error, errors} -> {:error, errors}
end
end
end
Define a minimal spec that exercises the critical path:
# lib/my_app/specs.ex
defmodule MyApp.Specs do
import Norm
def user_spec do
schema(%{
id: spec(is_integer() and (&(&1 > 0))),
email: spec(is_binary()),
role: spec(&(&1 in ["user", "admin", "viewer"]))
})
end
end
Wire it to a route:
# lib/my_app_web/router.ex
scope "/health" do
get "/", HealthController, :index
end
Verify locally:
mix phx.server
curl http://localhost:4000/health
# {"status":"ok","norm":"operational"}
Step 2: Add the Monitor in Vigilmon
- Log in to vigilmon.online and click Add Monitor.
- Set Type to
HTTP / HTTPS. - Enter your health URL:
https://myapp.example.com/health. - Set Check interval to
1 minute. - Set Expected HTTP status to
200. - Under Response body, enable Contains keyword and enter
"operational". - Click Save.
The keyword check catches the case where your app returns 200 but the Norm pipeline is reporting "degraded" — a common pattern when health checks mask inner failures with a generic 200 response.
Step 3: Track Validation Errors with Telemetry
Norm doesn't emit Telemetry events by default, but you can wrap your Norm.conform/2 calls in a module that emits metrics, then alert when the failure rate climbs:
defmodule MyApp.Validation do
require Logger
def conform(data, spec, name \\ "unknown") do
start = System.monotonic_time(:millisecond)
result = Norm.conform(data, spec)
duration_ms = System.monotonic_time(:millisecond) - start
case result do
{:ok, conformed} ->
:telemetry.execute(
[:my_app, :norm, :conform],
%{duration_ms: duration_ms, failures: 0},
%{spec: name}
)
{:ok, conformed}
{:error, errors} ->
Logger.warning("Norm conformance failed for #{name}: #{inspect(errors)}")
:telemetry.execute(
[:my_app, :norm, :conform],
%{duration_ms: duration_ms, failures: length(errors)},
%{spec: name}
)
{:error, errors}
end
end
end
Attach a handler to log when failure counts spike:
# In your Application.start/2
:telemetry.attach(
"norm-conformance-failures",
[:my_app, :norm, :conform],
fn _event, %{failures: count}, %{spec: name}, _config ->
if count > 5 do
Logger.error("High Norm failure count (#{count}) for spec: #{name}")
end
end,
nil
)
Expose an aggregated failure count in your health endpoint so Vigilmon can alert on data quality degradation before it reaches critical levels.
Step 4: Heartbeat for Background Conformance Workers
If your app uses Norm to validate events from a message queue or external feed — conforming Kafka messages, validating webhook payloads, processing CSV imports — add a Vigilmon cron heartbeat to confirm the worker keeps running:
- In Vigilmon, click Add Monitor → Cron Heartbeat.
- Set Expected ping interval to match your worker schedule (e.g.
5minutes for a continuous consumer). - Copy the heartbeat URL (e.g.
https://vigilmon.online/heartbeat/abc123).
Ping Vigilmon after each successful batch:
defmodule MyApp.EventConsumer do
require Logger
def process_batch(events) do
results = Enum.map(events, fn event ->
MyApp.Validation.conform(event, MyApp.Specs.event_spec(), "event")
end)
errors = Enum.filter(results, fn
{:error, _} -> true
_ -> false
end)
Logger.info("Processed #{length(events)} events, #{length(errors)} errors")
if length(errors) < length(events) do
# At least some succeeded — ping heartbeat
ping_vigilmon()
end
results
end
defp ping_vigilmon do
url = System.get_env("VIGILMON_HEARTBEAT_URL")
if url, do: :httpc.request(:get, {String.to_charlist(url), []}, [], [])
end
end
If the consumer crashes mid-batch and never pings, Vigilmon alerts after the expected window expires.
Step 5: Monitor Upstream Data Sources
Norm specs protect you from bad data, but the root cause is usually an upstream service sending malformed payloads. Monitor the APIs or services feeding your Norm pipeline directly:
- In Vigilmon, click Add Monitor → HTTP / HTTPS.
- Enter the upstream API health endpoint:
https://upstream-service.example.com/status. - Set Check interval to
1 minute. - Enable SSL certificate monitoring with a
21 dayexpiry alert.
Correlate Norm conformance failure spikes with upstream monitor downtime events. A sudden jump in {:error, _} returns often traces back to the upstream service returning a schema-breaking response during a deployment.
Step 6: Configure Alert Channels
- Go to Alert Channels in Vigilmon and add Slack, email, or PagerDuty.
- Set Consecutive failures before alert to
2to suppress transient spikes. - Use Maintenance windows when you intentionally change a spec's shape during a deployment that will temporarily fail the health check:
curl -X POST https://vigilmon.online/api/maintenance \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{"monitor_id": "abc123", "duration_minutes": 15}'
Summary
| Monitor | Target | What It Catches |
|---|---|---|
| HTTP health check | /health on your app | Norm spec failures, broken validation pipeline |
| Response keyword | "operational" in body | App up but conformance degraded |
| Cron heartbeat | Heartbeat URL | Silent background consumer failure |
| Upstream HTTP monitor | Upstream API health | Bad data source causing conformance errors |
| SSL certificate | Upstream API endpoint | TLS expiry breaking the data feed |
Norm makes data contracts explicit at the Elixir boundary — Vigilmon keeps watch over the pipeline those contracts protect. With a spec-exercising health endpoint, a heartbeat for background consumers, and upstream source monitoring, you get full visibility from the data source through to the conformance layer.