python 45 lines · 6 steps

Rate-limiting noisy logs in Python

A wrapper that suppresses repeated log messages so a flapping error can't flood your logs.

Explained by highlit
1import time
2import logging
3from threading import Lock
4 
5logger = logging.getLogger(__name__)
6 
7 
8class ThrottledLogger:
9 def __init__(self, base_logger, interval=60.0):
10 self._logger = base_logger
11 self._interval = interval
12 self._last_emitted = {}
13 self._lock = Lock()
14 
15 def _should_emit(self, key):
16 now = time.monotonic()
17 with self._lock:
18 last = self._last_emitted.get(key)
19 if last is not None and now - last < self._interval:
20 return False
21 self._last_emitted[key] = now
22 return True
23 
24 def warning(self, msg, *args, key=None, **kwargs):
25 throttle_key = key or msg
26 if self._should_emit(throttle_key):
27 self._logger.warning(msg, *args, **kwargs)
28 
29 def error(self, msg, *args, key=None, **kwargs):
30 throttle_key = key or msg
31 if self._should_emit(throttle_key):
32 self._logger.error(msg, *args, **kwargs)
33 
34 
35throttled = ThrottledLogger(logger)
36 
37 
38def poll_upstream(client, endpoint):
39 try:
40 return client.get(endpoint, timeout=5)
41 except client.TimeoutError:
42 throttled.warning(
43 "upstream %s timed out; retrying", endpoint, key=f"timeout:{endpoint}"
44 )
45 raise
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Keying throttle state per message lets distinct warnings emit freely while duplicates get suppressed.
  2. 2time.monotonic() is the right clock for measuring intervals because it never jumps backward.
  3. 3Guarding shared mutable state with a Lock keeps the throttle correct under concurrent callers.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Rate-limiting noisy logs in Python — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code