Skip to main content
RunBook Academy

← All checklists in Observability

Quarterlyalert-review

Alert Review Checklist

21 items ·13 critical ·7 warn ·1 info

Run this quarterly against the rule files as they exist in Git and as Prometheus has actually loaded them, and additionally after any exporter upgrade, instrumentation change or service decommission.

This checklist reads the rules. Whether an alert that fires reaches a human is the Alerting Readiness checklist; whether the alerts that fired last quarter deserved their pages is the Alert Quality Review. This one asks a narrower and more mechanical question: is each rule still a correct statement about a system that still exists.

Read the loaded rules, not only the files

Most of the commands here query the Prometheus rules API rather than grepping the repository, and the difference is the point. The repository is what somebody intended; the API is what is evaluating. A rule file that failed to load, a group that was renamed, a change that was merged but never reloaded — all of them leave the two views disagreeing, and every one of those disagreements is a rule that is not doing what its author believes it is doing.

Where an item names a path such as /etc/prometheus/rules/, substitute the path your estate uses.

What a failure means

The failure shape this review exists to catch is the rule that has stopped working without breaking. A renamed metric, a deleted service, a rotted fixture, a broken recording-rule name: in every case the rule still parses, still loads, still reports healthy, and simply never fires again. Nothing in the alerting chain reports a rule that has gone quiet, because a quiet rule is the normal state of a rule.

That is why so many items here are worded as “print the ones that do not” rather than as a pass/fail command. The finding is a list, and the list is the review.

Sign-off

  • Reviewer: ________________ Date: ___________
  • Service owner: ____________ Date: ___________
  • Platform owner: ___________ Date: ___________

Critical13 items

  1. comm -23 <(grep -rhoE '[a-z_][a-z0-9_]*_(total|seconds|bytes|count|sum|bucket|info)' /etc/prometheus/rules/ | sort -u) <(curl -s http://prometheus:9090/api/v1/label/__name__/values | jq -r '.data[]' | sort -u)
  2. curl -s http://prometheus:9090/api/v1/rules | jq -r '.data.groups[].rules[] | select(.type=="alerting") | select((.labels.severity // "") == "" or (.labels.team // "") == "") | .name'
  3. curl -s http://prometheus:9090/api/v1/rules | jq -r '.data.groups[].rules[] | select(.type=="alerting") | select((.annotations.runbook_url // "") == "" or (.annotations.dashboard_url // "") == "" or (.annotations.summary // "") == "") | .name'
  4. for url in $(grep -rh "runbook_url:" /etc/prometheus/rules/ | awk -F"'" '{print $2}'); do echo "$(curl -s -o /dev/null -w '%{http_code}' "$url") $url"; done
  5. curl -s http://prometheus:9090/api/v1/rules | jq -r '.data.groups[].rules[] | select(.type=="alerting") | select(.labels.severity=="page" or .labels.severity=="critical") | "\(.name): \(.query | gsub("\\s+"; " "))"'
  6. curl -s http://prometheus:9090/api/v1/rules | jq -r '.data.groups[].rules[] | select(.type=="alerting") | select(.query | test("ratio_rate|burn")) | "\(.name): \(.query | gsub("\\s+"; " "))"'
  7. curl -s http://prometheus:9090/api/v1/rules | jq -r '.data.groups[].rules[] | select(.type=="alerting") | select(.query | test("absent")) | .name'
  8. curl -s http://prometheus:9090/api/v1/rules | jq -r '.data.groups[].rules[] | select(.type=="alerting") | select((.duration // 0) == 0) | .name'
  9. promtool test rules /etc/prometheus/rules/test/*.yml
  10. RULES_REPO=/srv/observability; git -C "$RULES_REPO" log -1 --format='%ad %an %s' -- prometheus/rules/test/
  11. curl -s http://prometheus:9090/api/v1/rules | jq -r '.data.groups[].rules[] | select(.type=="alerting") | .labels.service // "UNSET"' | sort -u
  12. curl -s http://prometheus:9090/api/v1/rules | jq -r '.data.groups[].rules[] | select(.type=="alerting") | .labels.team // "UNSET"' | sort | uniq -c

Warning7 items

  1. curl -s http://prometheus:9090/api/v1/rules | jq -r '.data.groups[].rules[] | .labels.severity // "UNSET"' | sort | uniq -c; grep -n severity /etc/alertmanager/alertmanager.yml
  2. curl -s http://prometheus:9090/api/v1/rules | jq -r '.data.groups[].rules[] | select(.type=="alerting") | .labels.environment // .labels.env // "UNSET"' | sort | uniq -c
  3. curl -s http://prometheus:9090/api/v1/rules | jq -r '.data.groups[].rules[] | select(.type=="recording") | .name' | grep -v ":"
  4. curl -s http://prometheus:9090/api/v1/rules | jq -r '.data.groups[] | select(.evaluationTime > (.interval * 0.8)) | "\(.name) eval=\(.evaluationTime)s interval=\(.interval)s"'
  5. curl -s -G http://prometheus:9090/api/v1/query --data-urlencode 'query=increase(prometheus_rule_evaluation_failures_total[7d])' | jq -r '.data.result[] | "\(.metric.rule_group) \(.value[1])"'

Info1 item