from __future__ import annotations import threading import time from dataclasses import dataclass @dataclass class MetricsSnapshot: jobs: int errors: int pages: int total_ms: int last_job_ms: int def as_dict(self) -> dict[str, int]: avg = int(self.total_ms / self.jobs) if self.jobs else 0 return { "jobs": self.jobs, "errors": self.errors, "pages": self.pages, "total_ms": self.total_ms, "last_job_ms": self.last_job_ms, "avg_job_ms": avg, } class Metrics: def __init__(self) -> None: self._lock = threading.Lock() self._jobs = 0 self._errors = 0 self._pages = 0 self._total_ms = 0 self._last_job_ms = 0 self.started_at = time.time() def record_success(self, pages: int, elapsed_ms: int) -> None: with self._lock: self._jobs += 1 self._pages += max(0, pages) self._total_ms += max(0, elapsed_ms) self._last_job_ms = max(0, elapsed_ms) def record_error(self) -> None: with self._lock: self._errors += 1 def snapshot(self) -> MetricsSnapshot: with self._lock: return MetricsSnapshot( jobs=self._jobs, errors=self._errors, pages=self._pages, total_ms=self._total_ms, last_job_ms=self._last_job_ms, ) metrics = Metrics()