fix: logging discipline in the webhook send loop

- promote the three swallow-to-failed-result catch-alls (unexpected send error, proxy
  get() fault, proxy burn() fault) from WARNING to ERROR: each swallows an unexpected
  failure into a WebhookResult(ok=False) - the op failed, the result is lost, and this
  lib is the last code that sees the exception, so it is an ERROR not a routine WARNING.
- add the attempt count to the two per-attempt 429 WARNING lines (rule 3: per-attempt
  retry lines carry N/M), using the per-call counter already in scope.

Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
2026-08-09 02:09:53 -04:00
parent 8a8d99bbb3
commit e2f78bd80f
+15 -7
View File
@@ -141,8 +141,10 @@ class Webhook:
except _Retryable as exhausted: except _Retryable as exhausted:
return exhausted.result return exhausted.result
except Exception as error: except Exception as error:
# never-raises net: anything else (closed session, bad proxy url, ...) -> failed result # never-raises net: anything else (closed session, bad proxy url, ...) -> failed result.
log.warning("webhook send failed unexpectedly on %s: %s", url, error, exc_info=True) # ERROR: an UNEXPECTED failure is swallowed here into a failed result (op failed, result
# lost, a human may need to act) - the lib is the last code that sees this exception.
log.error("webhook send failed unexpectedly on %s: %s", url, error, exc_info=True)
return WebhookResult( return WebhookResult(
ok=False, status=None, url=url, attempts=counter[0] or 1, ok=False, status=None, url=url, attempts=counter[0] or 1,
error=f"{type(error).__name__}: {error}", error=f"{type(error).__name__}: {error}",
@@ -165,7 +167,8 @@ class Webhook:
if pending_wait[0] is not None: if pending_wait[0] is not None:
wait = pending_wait[0] wait = pending_wait[0]
pending_wait[0] = None pending_wait[0] = None
log.warning("webhook 429 on %s; honoring retry_after %.3fs", url, wait) log.warning("webhook 429 on %s; honoring retry_after %.3fs (attempt %d/%d)",
url, wait, counter[0] + 1, self.max_retries + 1)
await asyncio.sleep(wait) await asyncio.sleep(wait)
counter[0] += 1 counter[0] += 1
attempts = counter[0] attempts = counter[0]
@@ -174,8 +177,10 @@ class Webhook:
try: try:
proxy_dict = self._proxies.get() proxy_dict = self._proxies.get()
except Exception: except Exception:
# duck-typed provider: any get() error means no proxy available # duck-typed provider: any get() error means no proxy available.
log.warning("webhook: proxy get() failed; no proxy available", # ERROR: swallowed into a failed result (op failed, result lost) - the
# provider fault vanishes here unless it is logged loudly.
log.error("webhook: proxy get() failed; no proxy available",
exc_info=True) exc_info=True)
return WebhookResult( return WebhookResult(
ok=False, status=None, url=url, attempts=attempts, ok=False, status=None, url=url, attempts=attempts,
@@ -209,7 +214,8 @@ class Webhook:
if wait is not None: if wait is not None:
pending_wait[0] = wait pending_wait[0] = wait
else: else:
log.warning("webhook 429 on %s; no retry_after, backing off", url) log.warning("webhook 429 on %s; no retry_after, backing off (attempt %d/%d)",
url, attempts, self.max_retries + 1)
raise _Retryable(result) raise _Retryable(result)
if status >= 500: if status >= 500:
raise _Retryable(result) raise _Retryable(result)
@@ -241,7 +247,9 @@ class Webhook:
self._proxies.burn(proxy) self._proxies.burn(proxy)
return True return True
except Exception: except Exception:
log.warning("webhook: proxy burn failed; ending rotation", exc_info=True) # ERROR: an unexpected provider fault (burn() raising) is swallowed here and ends
# rotation, failing the send - it must be loud, not a quiet WARNING.
log.error("webhook: proxy burn failed; ending rotation", exc_info=True)
return False return False
@staticmethod @staticmethod