Getting Started
This walks through one alert for an HTTP API, from declaration to a rule file Prometheus loads and an alertmanager.yml that routes the alert.
To start from a scaffold instead, chant init --lexicon prometheus <dir> writes the rule group and routing this page builds. --template picks one of three others:
| Template | What it writes |
|---|---|
rules | The rule group only, for a setup whose Alertmanager config lives elsewhere |
slo-style | An error ratio recorded over two windows and a burn-rate alert that needs both, written out as plain rules |
slo | An Slo, which builds the error ratios, the error budget and the page and ticket burn-rate alerts, and the routing for both severities, with a page muting the same SLO’s ticket. See SLOs |
Each one builds clean and passes the PROM checks.
1. Install and register the lexicon
Section titled “1. Install and register the lexicon”npm install --save-dev @intentius/chant @intentius/chant-lexicon-prometheusimport type { ChantConfig } from "@intentius/chant";
export default { lexicons: ["prometheus"] } satisfies ChantConfig;2. Declare a rule group
Section titled “2. Declare a rule group”A RuleGroup takes the rule file’s own keys. Rules are plain objects: record and expr for a recording rule, alert and expr for an alerting rule.
import { RuleGroup, type Rule } from "@intentius/chant-lexicon-prometheus";
const rules: Rule[] = [ { record: "job:http_errors:ratio5m", expr: 'sum by (job) (rate(http_requests_total{code=~"5.."}[5m])) / sum by (job) (rate(http_requests_total[5m]))', }, { alert: "ApiErrorRatioHigh", expr: "job:http_errors:ratio5m > 0.05", for: "10m", labels: { severity: "page" }, annotations: { summary: "{{ $labels.job }} is failing over 5% of requests" }, },];
const api = new RuleGroup({ name: "api", interval: "30s", rules });
export { api };3. Route the alert
Section titled “3. Route the alert”import { Receiver, Route, type RouteProps, type WebhookConfig } from "@intentius/chant-lexicon-prometheus";
const hook: WebhookConfig[] = [{ url: "http://alert-sink.monitoring:8080/" }];const oncall = new Receiver({ name: "oncall", webhook_configs: hook });const fallback = new Receiver({ name: "default" });
const children: RouteProps[] = [{ matchers: ['severity="page"'], receiver: oncall }];const root = new Route({ receiver: fallback, group_by: ["alertname", "job"], routes: children });
export { oncall, fallback, root };The root route is the Route no other route nests. Its receiver takes every alert no child route matches.
4. Build
Section titled “4. Build”chant build src --lexicon prometheus -o dist/rules.ymldist/rules.yml holds the group, and dist/alertmanager.yml is written beside it:
groups: - name: api interval: 30s rules: - record: job:http_errors:ratio5m expr: sum by (job) (rate(http_requests_total{code=~"5.."}[5m])) / sum by (job) (rate(http_requests_total[5m])) - alert: ApiErrorRatioHigh expr: job:http_errors:ratio5m > 0.05 for: 10m labels: severity: page annotations: summary: "{{ $labels.job }} is failing over 5% of requests"5. What the build checked
Section titled “5. What the build checked”Every expr was parsed as PromQL (PROM104), durations were checked (PROM103), the route’s receiver exists (PROM201), and the page severity has a route (PROM202). Change severity: "page" to severity: "critical" and rebuild: PROM202 reports that no route matches severity="critical", so the alert would go to the default receiver.
6. Check with the upstream tools
Section titled “6. Check with the upstream tools”With promtool and amtool installed:
promtool check rules dist/rules.ymlamtool check-config dist/alertmanager.ymlThe same checks run from TypeScript with promtoolCheckRules and amtoolCheckConfig.
- Rule groups and Alertmanager list every field.
- On Kubernetes renders the same groups into a
PrometheusRule.