feat(traefik): a 4xx share is worth a look only once it is a lot of 4xx (#6105)
Three 404s on a quiet router read 7.5% and were raised as an anomaly; most of the 4xx anomalies on review stood for 3 to 7 errors in 15 minutes. A new Settings → Thresholds minimum, traefik.threshold.min_4xx (default 20, more than one a minute held for the window), applies to both the router's grade on its page and to anomaly detection, as min_5xx does for 5xx. learn_normal takes `fewest`: for a share gated on a per-second rate, the fewest events (rate × window × share) it must stand for to be judged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -103,6 +103,12 @@ DEFAULTS: dict[str, Any] = {
|
||||
# ...and a 5xx share past its cutoff is failing only once this many 5xx
|
||||
# were answered in those 15 minutes: one 502 of 70 is 1.4% (#5865).
|
||||
"traefik.threshold.min_5xx": 3,
|
||||
# ...and a 4xx share past its cutoff, or departing its normal, is worth a
|
||||
# look only once this many 4xx were answered: some 404s and failed
|
||||
# sign-ins are expected all day; it is a lot of them that needs acting on.
|
||||
# 20 is more than one a minute held for the window; the blips it drops
|
||||
# were 3 to 7 errors (#6105).
|
||||
"traefik.threshold.min_4xx": 20,
|
||||
# The smallest departure from a metric's normal worth calling an anomaly
|
||||
# (#5742), on top of being a quarter over its usual hold: a share's floor
|
||||
# is in percentage points, a response time's in ms. 5 points is the
|
||||
@@ -560,6 +566,9 @@ def to_thresholds_cfg(settings: dict[str, Any]) -> dict:
|
||||
# Fewer 5xx than this in 15 minutes and a router isn't failing, whatever the share.
|
||||
"router_5xx": {"warn": g("traefik.threshold.min_5xx", 3),
|
||||
"crit": g("traefik.threshold.min_5xx", 3), "dir": "low"},
|
||||
# Fewer 4xx than this in 15 minutes and a router's 4xx share isn't news.
|
||||
"router_4xx": {"warn": g("traefik.threshold.min_4xx", 20),
|
||||
"crit": g("traefik.threshold.min_4xx", 20), "dir": "low"},
|
||||
# Not a cutoff: each normal's floor (norms.floor.*), read by floor_of().
|
||||
"floors": {name: float(g(f"norms.floor.{name}", DEFAULTS[f"norms.floor.{name}"]))
|
||||
for name in NORM_FLOORS},
|
||||
|
||||
Reference in New Issue
Block a user