SLO burn rate detection in the experimental alerting system
An SLO burn rate query asks a different question than a basic threshold: are you consuming your error budget faster than you can afford to? Rather than checking a single metric at a fixed limit, it calculates error rates across multiple time windows simultaneously and assigns a severity level based on how fast the budget is being consumed.
This version checks one window pair and fires only at critical severity. It shows the core two-window pattern with the minimum number of moving parts.
FROM metrics-*
| WHERE @timestamp >= NOW() - 1 hour
| STATS
errors_5m = COUNT_IF(outcome == "failure" AND @timestamp >= NOW() - 5 minutes),
total_5m = COUNT_IF(@timestamp >= NOW() - 5 minutes),
errors_1h = COUNT_IF(outcome == "failure"),
total_1h = COUNT(*)
BY slo.id
| EVAL
burn_5m = errors_5m / total_5m,
burn_1h = errors_1h / total_1h
| WHERE burn_5m > 14.4 AND burn_1h > 14.4
// the short window detects the spike, the long window confirms it's sustained
| EVAL severity = "critical"
| KEEP slo.id, severity, burn_5m, burn_1h
- Cover the longest window in the pair
- Each SLO is evaluated independently
- Both windows must exceed the threshold to fire;
The 14.4× multiplier reflects the rate at which a service would exhaust a 99.9% monthly error budget in one hour. Adjust it to match your SLO target.
This version extends the single-tier pattern to detect both critical and high severity in one pass. Each severity level uses its own window pair with different time scales and thresholds.
FROM metrics-*
| WHERE @timestamp >= NOW() - 3 days
// Keep this in sync with the rule's lookback setting.
| STATS
// CRITICAL window pair: 5 min fast signal, 1 hour sustained confirmation
errors_5m = COUNT_IF(outcome == "failure" AND @timestamp >= NOW() - 5 minutes),
total_5m = COUNT_IF(@timestamp >= NOW() - 5 minutes),
errors_1h = COUNT_IF(outcome == "failure" AND @timestamp >= NOW() - 1 hour),
total_1h = COUNT_IF(@timestamp >= NOW() - 1 hour),
// HIGH window pair: 30 min fast signal, 6 hours sustained confirmation
errors_30m = COUNT_IF(outcome == "failure" AND @timestamp >= NOW() - 30 minutes),
total_30m = COUNT_IF(@timestamp >= NOW() - 30 minutes),
errors_6h = COUNT_IF(outcome == "failure" AND @timestamp >= NOW() - 6 hours),
total_6h = COUNT_IF(@timestamp >= NOW() - 6 hours)
BY slo.id
| EVAL
burn_5m = errors_5m / total_5m,
burn_1h = errors_1h / total_1h,
burn_30m = errors_30m / total_30m,
burn_6h = errors_6h / total_6h
| EVAL severity = CASE(
burn_5m > 14.4 AND burn_1h > 14.4, "critical",
burn_30m > 6.0 AND burn_6h > 6.0, "high",
"none"
)
| WHERE severity != "none"
| KEEP slo.id, severity, burn_5m, burn_1h, burn_30m, burn_6h
- Cover the longest window pair used below.
The rule's lookback window must cover the longest window in the query. In this example that's 3 days, driven by the 6-hour window pair.
The severity column in KEEP maps directly to the severity field on each resulting alert episode. For accepted values and matching rules, refer to Severity.