Files
Aether/_deprecated_py_src/utils/perf.py
fawney19 1d9c77522a refactor: 移除 Python 后端源码,全面迁移至 Rust gateway 架构
- 删除全部 Python 源码 (src/) 及 Alembic 迁移脚本,归档至 _deprecated_py_src/
- 重构 Rust gateway ai_pipeline: 拆分 planner/finalize 模块,新增 contracts/adaptation 层
- 重组 handlers 模块为 admin/public/proxy/internal/shared 子模块结构
- 新增 executor 模块,引入 Rust 原生数据库迁移 (aether-data/migrations)
- 简化 CI/Docker 构建流程,移除 base image 二级构建,统一为单一 app image
- 移除 Python 相关基础设施文件 (entrypoint.sh, gunicorn_conf.py, Dockerfile.base)
2026-04-03 16:26:16 +08:00

136 lines
3.9 KiB
Python

from __future__ import annotations
import random
import time
from typing import Any
from src.config.settings import config
from src.core.logger import logger
class PerfRecorder:
"""轻量性能记录器(可选启用)"""
@staticmethod
def enabled() -> bool:
return bool(config.perf_metrics_enabled or config.perf_log_slow_ms > 0)
@staticmethod
def start(force: bool = False) -> float | None:
if not force and not PerfRecorder.enabled():
return None
return time.perf_counter()
@staticmethod
def stop(
start: float | None,
name: str,
labels: dict[str, str] | None = None,
*,
sample_rate: float | None = None,
log_hint: str | None = None,
) -> float | None:
if start is None:
return None
duration = time.perf_counter() - start
PerfRecorder.record_timing(
name,
duration,
labels=labels,
sample_rate=sample_rate,
log_hint=log_hint,
)
return duration
@staticmethod
def record_timing(
name: str,
duration: float,
labels: dict[str, str] | None = None,
*,
sample_rate: float | None = None,
log_hint: str | None = None,
) -> None:
if not PerfRecorder.enabled():
return
if not PerfRecorder._should_sample(sample_rate):
return
duration_ms = duration * 1000.0
if config.perf_log_slow_ms > 0 and duration_ms >= float(config.perf_log_slow_ms):
hint = f" | {log_hint}" if log_hint else ""
logger.info("[PERF] {} took {:.2f}ms{}", name, duration_ms, hint)
if not config.perf_metrics_enabled:
return
plugin = PerfRecorder._get_monitor_plugin()
if not plugin:
return
PerfRecorder._create_task(plugin.timing(PerfRecorder._metric_name(name), duration, labels))
@staticmethod
def record_counter(
name: str,
value: float = 1,
labels: dict[str, str] | None = None,
*,
sample_rate: float | None = None,
) -> None:
if not config.perf_metrics_enabled:
return
if not PerfRecorder._should_sample(sample_rate):
return
plugin = PerfRecorder._get_monitor_plugin()
if not plugin:
return
PerfRecorder._create_task(
plugin.increment(PerfRecorder._metric_name(name), value=value, labels=labels)
)
@staticmethod
def should_store() -> bool:
return bool(getattr(config, "perf_store_enabled", False))
@staticmethod
def should_store_sample() -> bool:
if not PerfRecorder.should_store():
return False
rate = float(getattr(config, "perf_store_sample_rate", 1.0))
return PerfRecorder._should_sample(rate)
@staticmethod
def _should_sample(sample_rate: float | None) -> bool:
rate = float(sample_rate if sample_rate is not None else config.perf_sample_rate)
if rate >= 1:
return True
if rate <= 0:
return False
return random.random() < rate
@staticmethod
def _get_monitor_plugin() -> Any | None:
# Lazy import to avoid circular deps during app startup.
try:
from src.plugins.manager import get_plugin_manager
except Exception:
return None
try:
plugin = get_plugin_manager().get_plugin("monitor")
except Exception:
return None
if plugin and getattr(plugin, "enabled", True):
return plugin
return None
@staticmethod
def _metric_name(name: str) -> str:
return name if name.startswith("perf_") else f"perf_{name}"
@staticmethod
def _create_task(coro: Any) -> None:
from src.utils.async_utils import safe_create_task
safe_create_task(coro)