Initial commit
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
"""Helpers shared by collectors: failure isolation and timing."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import time
|
||||
from collections.abc import Callable
|
||||
|
||||
from us2kr.logging_setup import mask_secrets
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def run_safely[T](name: str, fn: Callable[[], T], default: T, gaps: list[str]) -> T:
|
||||
"""Run a collector; on any exception log it, append a gap note and return `default`.
|
||||
|
||||
The pipeline must never die because one data source is down.
|
||||
"""
|
||||
t0 = time.monotonic()
|
||||
try:
|
||||
result = fn()
|
||||
log.info("collector %s ok in %.1fs", name, time.monotonic() - t0)
|
||||
return result
|
||||
except Exception as exc: # deliberate: isolate every failure
|
||||
log.exception("collector %s failed", name)
|
||||
# Gap notes end up in the briefing / Telegram / LLM prompt: never let a URL with a key through.
|
||||
detail = mask_secrets(str(exc)).split(" for url: ", 1)[0][:120]
|
||||
gaps.append(f"{name}: {type(exc).__name__}: {detail}")
|
||||
return default
|
||||
Reference in New Issue
Block a user