#!/usr/bin/env python3 import argparse import asyncio import json import logging from dataclasses import dataclass from typing import Any, Dict, Optional, Tuple import aiohttp logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s") log = logging.getLogger("health_check") @dataclass class HealthReport: base_url: str alive: bool ready: bool health_status: int info_status: int metrics_status: int details: Dict[str, Any] async def fetch_json(session: aiohttp.ClientSession, url: str) -> Tuple[int, Dict[str, Any]]: try: async with session.get(url) as resp: status = resp.status try: data = await resp.json(content_type=None) except Exception: text = await resp.text() data = {"raw": text} return status, data except Exception as e: return 0, {"error": str(e)} async def probe(base_url: str, timeout: int = 8) -> HealthReport: timeout_cfg = aiohttp.ClientTimeout(total=timeout) async with aiohttp.ClientSession(timeout=timeout_cfg) as session: details: Dict[str, Any] = {} async def _g(path: str): url = f"{base_url.rstrip('/')}{path}" s, d = await fetch_json(session, url) details[path] = {"status": s, "data": d} return s, d health_s, _ = await _g("/api/system/health") info_s, _ = await _g("/api/system/info") metrics_s, _ = await _g("/api/system/metrics") live_s, _ = await _g("/api/system/live") ready_s, _ = await _g("/api/system/ready") alive = live_s in (200, 204) ready = ready_s in (200, 204) return HealthReport( base_url=base_url, alive=alive, ready=ready, health_status=health_s, info_status=info_s, metrics_status=metrics_s, details=details, ) def main() -> int: ap = argparse.ArgumentParser(description="Проверка здоровья системы (health/info/metrics/ready/live)") ap.add_argument("base_url", help="Базовый URL FastAPI, например http://localhost:8000") ap.add_argument("-t", "--timeout", type=int, default=8) ap.add_argument("--json", action="store_true", help="Вывод в JSON") args = ap.parse_args() report = asyncio.run(probe(args.base_url, timeout=args.timeout)) if args.json: print(json.dumps(report.__dict__, ensure_ascii=False, indent=2)) else: print(f"Health check for {report.base_url}") print(f"- alive={report.alive} ready={report.ready}") print(f"- /api/system/health: {report.health_status}") print(f"- /api/system/info: {report.info_status}") print(f"- /api/system/metrics: {report.metrics_status}") # Код возврата: 0 если живой и готов, иначе 2 return 0 if (report.alive and report.ready) else 2 if __name__ == "__main__": raise SystemExit(main())