Python进程需主动暴露运行状态:用psutil采集CPU/内存/线程数,结合threading.active_count()和gc.get_stats()补充应用层指标;HTTP接口应分三级(/health、/ready、/metrics),避免耗时操作;日志与监控需统一OpenTelemetry或通过threading.local()关联;异步任务须用自定义registry+异常安全清理防内存泄漏。

怎么让 Python 进程主动暴露运行状态
Python 默认不对外暴露运行时指标,得靠自己加钩子。核心思路是:用 psutil 抓进程级基础数据(CPU、内存、线程数),再配合 threading.active_count()、gc.get_stats()(3.12+)或 len(gc.get_objects()) 补充应用层状态。别依赖 sys.getsizeof() 查对象大小——它不递归,结果严重偏低。
常见错误是只采集启动时的 PID,后续 fork 或 reload 后失效。正确做法是在采集逻辑里每次调用 os.getpid() 动态获取,尤其在多进程(如 Gunicorn worker)场景下。
HTTP 接口暴露健康与指标是否足够可靠
单纯加个 /health 返回 {"status": "ok"} 没用,它不反映真实负载。生产环境至少要分三级:
-
/health:只检查自身 socket 可连、主线程存活(用threading.main_thread().is_alive()) -
/ready:额外验证下游依赖(DB 连接池可用、Redis ping 通),超时严格控制在 1s 内 -
/metrics:输出 Prometheus 格式,含python_gc_collected_total、process_cpu_seconds_total等标准指标
注意:不要在 /metrics 里执行耗时操作(如遍历所有 request 对象),否则会拖慢抓取,导致监控系统误判。
立即学习“Python免费学习笔记(深入)”;
日志里埋点为什么总对不上监控曲线
根本原因是日志打点和指标采集时间窗口不一致。比如用 logging.info("req_time=%.3f", time.time() - start) 记耗时,但 Prometheus 的 http_request_duration_seconds 是直方图聚合,两者统计口径不同。
解决办法只有两个:
- 统一用 OpenTelemetry SDK 上报 trace + metric,让
http.server.request.duration和日志里的trace_id关联 - 如果不用 OTel,至少把关键耗时存进
threading.local(),在日志 handler 里读取并附加到 log record,再用 Loki 做日志指标联动
别手动在日志里拼 "cpu: {}/mem: {}".format(...) —— 这会让日志解析器崩溃,也污染结构化字段。
异步任务(Celery/AIOHTTP)的状态怎么不丢不重
Celery 的 task-sent、task-received 事件默认不持久化,Broker 重启就丢失。必须配 worker_send_task_events = True 并用 celery events 实时消费,或者直接对接 celery.app.control.inspect().active() 定期拉取。
AIOHTTP 场景更麻烦:每个 request handler 是独立 task,但 asyncio.all_tasks() 会混入系统 task(如 DNS resolver)。安全做法是用 asyncio.current_task().get_name() 打标记,或在 middleware 里把 task 加入自定义 registry:
active_requests = set()
# middleware 中
task = asyncio.current_task()
active_requests.add(task)
try:
await handler(request)
finally:
active_requests.discard(task)这里容易忽略的是 task cancel 异常——如果没在 finally 清理,集合会持续膨胀,最终吃光内存。









