This commit is contained in:
2026-05-05 21:01:58 +03:30
parent 39efd537bf
commit 4e28bacad6
54 changed files with 2729 additions and 1115 deletions
+53
View File
@@ -0,0 +1,53 @@
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any
@dataclass
class FailureContract:
status: str = "error"
error_code: str = "internal_error"
message: str = ""
source: str = "application"
warnings: list[str] = field(default_factory=list)
retriable: bool = False
details: dict[str, Any] = field(default_factory=dict)
def to_dict(self) -> dict[str, Any]:
payload = {
"status": self.status,
"error_code": self.error_code,
"message": self.message,
"source": self.source,
"warnings": list(self.warnings),
"retriable": self.retriable,
}
if self.details:
payload["details"] = self.details
return payload
class StructuredServiceError(Exception):
def __init__(
self,
*,
error_code: str,
message: str,
source: str,
warnings: list[str] | None = None,
retriable: bool = False,
details: dict[str, Any] | None = None,
) -> None:
super().__init__(message)
self.contract = FailureContract(
error_code=error_code,
message=message,
source=source,
warnings=warnings or [],
retriable=retriable,
details=details or {},
)
def to_dict(self) -> dict[str, Any]:
return self.contract.to_dict()
+45
View File
@@ -0,0 +1,45 @@
from __future__ import annotations
from datetime import datetime
from typing import Any
def _isoformat(value: Any) -> Any:
if isinstance(value, datetime):
return value.isoformat()
return value
def build_integration_meta(
*,
flow_type: str,
source_type: str,
source_service: str,
ownership: str,
live: bool,
cached: bool,
generated_at: Any = None,
snapshot_at: Any = None,
sync_attempted: bool | None = None,
sync_status: str | None = None,
notes: list[str] | None = None,
) -> dict[str, Any]:
meta = {
"flow_type": flow_type,
"source_type": source_type,
"source_service": source_service,
"ownership": ownership,
"live": live,
"cached": cached,
}
if generated_at is not None:
meta["generated_at"] = _isoformat(generated_at)
if snapshot_at is not None:
meta["snapshot_at"] = _isoformat(snapshot_at)
if sync_attempted is not None:
meta["sync_attempted"] = sync_attempted
if sync_status is not None:
meta["sync_status"] = sync_status
if notes:
meta["notes"] = notes
return meta
+129
View File
@@ -0,0 +1,129 @@
from __future__ import annotations
import logging
import time
from collections import Counter
from contextvars import ContextVar
from dataclasses import dataclass
from typing import Any
logger = logging.getLogger(__name__)
_request_id_ctx: ContextVar[str | None] = ContextVar("backend_request_id", default=None)
METRICS: Counter[str] = Counter()
def set_request_id(request_id: str | None) -> None:
_request_id_ctx.set(request_id)
def get_request_id() -> str | None:
return _request_id_ctx.get()
def record_metric(name: str, value: int = 1, **tags: Any) -> None:
suffix = ",".join(f"{key}={tags[key]}" for key in sorted(tags) if tags[key] is not None)
metric_key = f"{name}|{suffix}" if suffix else name
METRICS[metric_key] += value
@dataclass
class ClassifiedFailure:
error_code: str
failure_type: str
retriable: bool
def classify_exception(exc: Exception) -> ClassifiedFailure:
exc_name = exc.__class__.__name__.lower()
message = str(exc).lower()
if "timeout" in exc_name or "timeout" in message:
return ClassifiedFailure("timeout", "timeout", True)
if "json" in exc_name or "json" in message:
return ClassifiedFailure("parse_error", "parse_error", False)
if "validation" in exc_name or "invalid" in message:
return ClassifiedFailure("validation_failure", "validation_failure", False)
if "connection" in exc_name or "unavailable" in message:
return ClassifiedFailure("dependency_unavailable", "dependency_unavailable", True)
return ClassifiedFailure("provider_error", "provider_error", True)
def log_event(
*,
level: int,
message: str,
source: str,
provider: str | None,
operation: str,
result_status: str,
duration_ms: float | None = None,
error_code: str | None = None,
**extra: Any,
) -> None:
payload = {
"source": source,
"provider": provider,
"operation": operation,
"result_status": result_status,
"duration_ms": round(duration_ms, 2) if duration_ms is not None else None,
"error_code": error_code,
"request_id": get_request_id(),
}
payload.update({key: value for key, value in extra.items() if value is not None})
logger.log(level, message, extra={"event": payload})
class observe_operation:
def __init__(self, *, source: str, provider: str | None, operation: str):
self.source = source
self.provider = provider
self.operation = operation
self.started_at = 0.0
def __enter__(self):
self.started_at = time.monotonic()
log_event(
level=logging.INFO,
message="backend operation started",
source=self.source,
provider=self.provider,
operation=self.operation,
result_status="started",
)
return self
def __exit__(self, exc_type, exc, _tb):
duration_ms = (time.monotonic() - self.started_at) * 1000
if exc is None:
log_event(
level=logging.INFO,
message="backend operation completed",
source=self.source,
provider=self.provider,
operation=self.operation,
result_status="success",
duration_ms=duration_ms,
)
record_metric("backend.operation.success", source=self.source, provider=self.provider, operation=self.operation)
return False
failure = classify_exception(exc)
log_event(
level=logging.ERROR,
message="backend operation failed",
source=self.source,
provider=self.provider,
operation=self.operation,
result_status="error",
duration_ms=duration_ms,
error_code=failure.error_code,
failure_type=failure.failure_type,
)
record_metric(
"backend.operation.failure",
source=self.source,
provider=self.provider,
operation=self.operation,
error_code=failure.error_code,
)
return False