- 8 DB models (services, incidents, monitors, subscribers, etc.) - Full CRUD API for services, incidents, monitors - Public status page with live data - Incident detail page with timeline - API key authentication - Uptime monitoring scheduler - 13 tests passing - TECHNICAL_DESIGN.md with full spec
59 lines
No EOL
2 KiB
Python
59 lines
No EOL
2 KiB
Python
"""Uptime monitoring service — performs HTTP health checks."""
|
|
|
|
import time
|
|
from datetime import datetime
|
|
|
|
import httpx
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.models.models import Monitor, MonitorResult
|
|
|
|
|
|
async def check_monitor(monitor: Monitor, db: AsyncSession) -> MonitorResult:
|
|
"""Perform a single HTTP health check for a monitor and store the result."""
|
|
start = time.monotonic()
|
|
|
|
try:
|
|
async with httpx.AsyncClient() as client:
|
|
response = await client.request(
|
|
method=monitor.method,
|
|
url=monitor.url,
|
|
timeout=monitor.timeout_seconds,
|
|
follow_redirects=True,
|
|
)
|
|
elapsed_ms = int((time.monotonic() - start) * 1000)
|
|
|
|
if response.status_code == monitor.expected_status:
|
|
# Check response time threshold for "degraded"
|
|
status = "up" if elapsed_ms < 5000 else "degraded"
|
|
result = MonitorResult(
|
|
monitor_id=monitor.id,
|
|
status=status,
|
|
response_time_ms=elapsed_ms,
|
|
status_code=response.status_code,
|
|
error_message=None,
|
|
checked_at=datetime.utcnow(),
|
|
)
|
|
else:
|
|
result = MonitorResult(
|
|
monitor_id=monitor.id,
|
|
status="down",
|
|
response_time_ms=elapsed_ms,
|
|
status_code=response.status_code,
|
|
error_message=f"Expected {monitor.expected_status}, got {response.status_code}",
|
|
checked_at=datetime.utcnow(),
|
|
)
|
|
except Exception as exc:
|
|
elapsed_ms = int((time.monotonic() - start) * 1000)
|
|
result = MonitorResult(
|
|
monitor_id=monitor.id,
|
|
status="down",
|
|
response_time_ms=elapsed_ms,
|
|
status_code=None,
|
|
error_message=str(exc)[:500],
|
|
checked_at=datetime.utcnow(),
|
|
)
|
|
|
|
db.add(result)
|
|
await db.flush()
|
|
return result |