from typing import Any from sqlalchemy import text from sqlalchemy.orm import Session from app.core.config import get_settings from app.modules.events.constants import EventStatus from app.modules.events.service import EventService from app.modules.observability.constants import ( ObservabilityKey, ObservabilityMetricKey, ObservabilityStatus, ) from app.modules.workflows.constants import WorkflowStatus from app.modules.workflows.service import WorkflowService class ObservabilityService: """Build health, readiness, and JSON metrics for V3 operations.""" def __init__(self, db: Session): self.db = db def live(self) -> dict[str, str]: return {ObservabilityKey.STATUS: ObservabilityStatus.OK} def ready(self) -> dict[str, Any]: checks = { ObservabilityKey.DATABASE: self._database_check(), ObservabilityKey.REDIS: self._redis_check(), ObservabilityKey.EVENTS: self._events_check(), ObservabilityKey.WORKFLOWS: self._workflows_check(), } degraded = any( item[ObservabilityKey.STATUS] in {ObservabilityStatus.DEGRADED, ObservabilityStatus.ERROR} for item in checks.values() ) return { ObservabilityKey.STATUS: ( ObservabilityStatus.DEGRADED if degraded else ObservabilityStatus.OK ), ObservabilityKey.CHECKS: checks, } def metrics(self) -> dict[str, Any]: return { ObservabilityKey.METRICS: { ObservabilityKey.EVENTS: EventService(self.db).count_by_status(), ObservabilityKey.WORKFLOWS: WorkflowService(self.db).count_by_status(), } } def _database_check(self) -> dict[str, Any]: try: self.db.execute(text("select 1")).scalar() except Exception as exc: return { ObservabilityKey.STATUS: ObservabilityStatus.ERROR, ObservabilityMetricKey.ERROR: str(exc), } return {ObservabilityKey.STATUS: ObservabilityStatus.OK} def _redis_check(self) -> dict[str, Any]: settings = get_settings() if not settings.task_queue_enabled: return {ObservabilityKey.STATUS: ObservabilityStatus.SKIPPED} try: from redis import Redis Redis.from_url(settings.redis_url, socket_connect_timeout=1).ping() except Exception as exc: return { ObservabilityKey.STATUS: ObservabilityStatus.DEGRADED, ObservabilityMetricKey.ERROR: str(exc), } return {ObservabilityKey.STATUS: ObservabilityStatus.OK} def _events_check(self) -> dict[str, Any]: counts = EventService(self.db).count_by_status() failed = counts.get(EventStatus.FAILED, 0) return { ObservabilityKey.STATUS: ( ObservabilityStatus.DEGRADED if failed else ObservabilityStatus.OK ), ObservabilityMetricKey.PENDING: counts.get(EventStatus.PENDING, 0), ObservabilityMetricKey.FAILED: failed, } def _workflows_check(self) -> dict[str, Any]: counts = WorkflowService(self.db).count_by_status() failed = counts.get(WorkflowStatus.FAILED, 0) return { ObservabilityKey.STATUS: ( ObservabilityStatus.DEGRADED if failed else ObservabilityStatus.OK ), ObservabilityMetricKey.RUNNING: counts.get(WorkflowStatus.RUNNING, 0), ObservabilityMetricKey.FAILED: failed, }