Frozen dataclass. The watchdog runs as a daemon thread and measures the gap since the last pinger tick on your event loop. Three thresholds map to three severities — warning for a regular stall, error and critical for progressively longer blocks. Repeated hits inside escalation_window escalate severity; cooldown_sec is the minimum gap between identical alerts.

Invariants enforced in __post_init__: threshold_ms > 0, error_threshold_ms > threshold_ms, critical_threshold_ms strictly greater than whichever of the two lower floors is set, cooldown_sec >= 0. Violations raise InvalidAnomalyConfigError.

Fields

NameTypeDescriptionDefault
enabledboolMaster switch.True
threshold_msintMinimum loop-block duration to emit a warning.500
error_threshold_msint | NoneBlock duration that escalates to error. Must exceed threshold_ms.2000
critical_threshold_msint | NoneBlock duration that escalates to critical. Must exceed both lower thresholds.5000
cooldown_secintMinimum gap between two alerts of the same severity.10
escalation_windowstr | intDuration string ("1m", "30s") or seconds. Repeated hits inside this window bump severity."1m"

Computed: escalation_window_sec returns the parsed window in seconds.

Example

import snitchbot
from snitchbot import AnomalyConfig, WatchdogConfig

snitchbot.init(
    "orders-api",
    anomaly=AnomalyConfig(
        watchdog=WatchdogConfig(
            threshold_ms=300,
            error_threshold_ms=1500,
            critical_threshold_ms=4000,
            cooldown_sec=15,
        ),
    ),
)

Telegram shows:

🟠 watchdog · worker · 7c6497
Event loop blocked for 612 ms (threshold 500 ms)
Details
  time   11:25:10 UTC
  pid    1580
  loop   main

🔴 watchdog · worker · 7c6497 × 2
Event loop blocked for 1700 ms (threshold 500 ms)
Details
  first  11:25:10 UTC
  last   11:25:20 UTC
  pid    1580
  loop   main

🟣 watchdog · worker · 732334
Event loop blocked for 4200 ms (threshold 500 ms)
Details
  time   11:25:24 UTC
  pid    1580
  loop   main

Severity escalates as the block duration crosses each threshold inside escalation_window.

Notes

Disable the watchdog entirely by passing watchdog=None to AnomalyConfig, or toggle enabled=False on the instance.