import logging import math import os import time from contextlib import asynccontextmanager from dataclasses import dataclass logger = logging.getLogger(__name__) RECENT_PHASE_CAPACITY = 40 @dataclass class PhaseSnapshot: name: str started_at: float ended_at: float duration_ms: float cpu_user_ms: float cpu_system_ms: float cpu_total_ms: float cpu_core_ratio: float details: dict[str, str | int | float | bool] | None = None _active_phase_stack: list[dict] = [] _recent_phases: list[PhaseSnapshot] = [] def _round_metric(value: float, digits: int = 1) -> float: if not math.isfinite(value): return 0.0 factor = 10**digits return round(value * factor) / factor def _push_recent(snapshot: PhaseSnapshot) -> None: _recent_phases.append(snapshot) if len(_recent_phases) > RECENT_PHASE_CAPACITY: del _recent_phases[: len(_recent_phases) - RECENT_PHASE_CAPACITY] def _record_phase(snapshot: PhaseSnapshot) -> None: _push_recent(snapshot) try: from yuxi.channel.monitoring.diagnostic_stability import record_stability_event record_stability_event( "diagnostic.phase.completed", name=snapshot.name, started_at=snapshot.started_at, ended_at=snapshot.ended_at, duration_ms=snapshot.duration_ms, cpu_user_ms=snapshot.cpu_user_ms, cpu_system_ms=snapshot.cpu_system_ms, cpu_total_ms=snapshot.cpu_total_ms, cpu_core_ratio=snapshot.cpu_core_ratio, ) except Exception: pass def get_current_phase() -> str | None: if not _active_phase_stack: return None return _active_phase_stack[-1]["name"] def get_recent_phases(limit: int = 8) -> list[PhaseSnapshot]: return list(_recent_phases[-max(0, limit) :]) def record_diagnostic_phase(snapshot: PhaseSnapshot) -> None: _record_phase(snapshot) def reset_diagnostic_phases_for_test() -> None: _active_phase_stack.clear() _recent_phases.clear() @asynccontextmanager async def diagnostic_phase(name: str, details: dict[str, str | int | float | bool] | None = None): cpu_started = os.times() active: dict = { "name": name, "started_at": time.time(), "started_wall": time.perf_counter(), "cpu_started_user": cpu_started.user, "cpu_started_system": cpu_started.system, "details": details, } _active_phase_stack.append(active) try: yield finally: ended_at = time.time() duration_ms = _round_metric((time.perf_counter() - active["started_wall"]) * 1000) cpu_ended = os.times() cpu_user_ms = _round_metric((cpu_ended.user - active["cpu_started_user"]) * 1000) cpu_system_ms = _round_metric((cpu_ended.system - active["cpu_started_system"]) * 1000) cpu_total_ms = _round_metric(cpu_user_ms + cpu_system_ms) cpu_core_ratio = _round_metric(cpu_total_ms / max(1.0, duration_ms), 3) _active_phase_stack[:] = [e for e in _active_phase_stack if e is not active] snapshot = PhaseSnapshot( name=name, started_at=active["started_at"], ended_at=ended_at, duration_ms=duration_ms, cpu_user_ms=cpu_user_ms, cpu_system_ms=cpu_system_ms, cpu_total_ms=cpu_total_ms, cpu_core_ratio=cpu_core_ratio, details=active["details"], ) _record_phase(snapshot) if cpu_core_ratio > 0.9: logger.debug( "DiagnosticPhase '%s': %sms, cpu_user=%.1fms, cpu_system=%.1fms, cpu_total=%.1fms, ratio=%.2f", name, duration_ms, cpu_user_ms, cpu_system_ms, cpu_total_ms, cpu_core_ratio, )