用Python构建服务监控系统时,最核心的不是采集工具本身,而是先想清楚要监控什么。很多团队接了监控SDK之后随便报几个系统指标就以为万事大吉,结果线上故障还是靠用户投诉才发现。指标设计直接决定了监控系统的有用程度。

一、监控指标设计的四个黄金维度
业界常用的黄金信号(Golden Signals)包含延迟、流量、错误率和饱和度,这四个维度基本能刻画一个服务的外部健康状态。延迟指请求处理耗时,流量是单位时间内处理的请求数,错误率是失败请求占比,饱和度描述资源被占用的程度。在Python服务里,这些信号既可以从Web框架中间件里提取,也能通过装饰器包裹业务函数来获得。
除了外部信号,内部指标也不能忽略。比如Python进程的内存增长趋势、垃圾回收次数、线程池排队长度,这些属于饱和度指标的细化。如果只采外部错误率,可能进程已经快被内存撑爆但错误率暂时还正常。设计时应把外部信号作为红色指标,内部资源信号作为黄色指标分开管理。
1.1 红色指标与黄色指标的区别
红色指标是那些越过阈值就必须立刻告警、人工介入的,例如错误率超过百分之五、接口P99延迟大于两秒。黄色指标用于观察趋势,比如内存每日缓增、GC频率变高,它们不直接触发告警,但应在仪表盘上持续展示。Python里可以用不同前缀命名指标来区分类别,例如red_http_error_total和yellow_gc_count。
这样的分类能减少告警疲劳。不少监控系统的失败就在于什么都告警,最后工程师把告警全屏蔽了。明确红色与黄色边界,是指标设计的第一步。
二、用Python定义和采集自定义指标
Prometheus官方提供了client_python库,可以很方便地在代码里定义Counter、Gauge、Histogram等类型。Counter适合累计错误数,Histogram适合记录延迟分布。下面示例展示如何用装饰器自动采集接口延迟和错误:
from prometheus_client import Counter, Histogram, start_http_server
import time
import functools
REQ_LATENCY = Histogram('app_request_latency_seconds', '请求延迟分布')
REQ_ERRORS = Counter('app_request_errors_total', '请求错误总数')
def monitor(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
try:
return func(*args, **kwargs)
except Exception:
REQ_ERRORS.inc()
raise
finally:
REQ_LATENCY.observe(time.time() - start)
return wrapper
@monitor
def handle_order(order_id):
# 模拟业务处理
time.sleep(0.1)
return {'order_id': order_id}
if __name__ == '__main__':
start_http_server(8000)
while True:
handle_order('123')
time.sleep(1)
上面代码通过Histogram自动计算延迟的分位数,Counter记录异常次数。start_http_server会在本地开一个端点暴露指标,Prometheus定时拉取即可。注意装饰器要包住真正可能出错的逻辑,否则错误数统计会不准。
如果服务不是HTTP拉模模式,也可以用pushgateway主动推送。但推送模式容易丢数据,一般建议短生命周期任务才用。长驻Python进程优先用拉取方式,这样监控系统挂了也不会影响业务。
2.1 避免指标基数爆炸
Histogram和Label组合使用时要小心。假如给每个用户ID打一个label,指标序列会随用户量线性膨胀,Prometheus存储和查询都会出问题。Python代码里不要在热路径上动态创建带高基数label的指标。下面是错误的示范:
from prometheus_client import Counter
# 错误做法:用用户ID作为label
ERR_BY_USER = Counter('err_by_user', '按用户统计错误', ['user_id'])
def bad_monitor(uid):
try:
# 业务逻辑
pass
except Exception:
ERR_BY_USER.labels(user_id=uid).inc()
正确做法是只按有限枚举值打label,比如接口名、错误类型。用户级问题应交由日志系统处理,监控负责宏观。指标设计本质上是在可观测性和系统开销之间找平衡。
三、业务指标与技术指标的配合
纯技术指标如CPU、内存无法反映业务好坏。一个支付接口返回200但内部扣款失败,技术指标全绿,业务却已资损。Python里应显式埋点业务状态,例如支付成功数、订单取消率。这些指标往往比系统指标更早暴露问题。
可以用事件驱动方式更新业务指标。例如Django的signal或者Flask的after_request钩子里,根据响应内容判断业务结果并inc对应的Counter。这样业务和监控解耦,不改核心逻辑也能加观测点。
3.1 指标命名规范
好的命名让指标自解释。建议格式为:领域_对象_动作_单位。例如red_payment_success_total、yellow_queue_length。单位要写在名字末尾,total表示累计量,seconds表示时间。Python变量名可和暴露名一致,减少映射混乱。
团队内部应维护指标字典,避免重复造轮子。新接手的人看名字就知道红色还是黄色、什么含义,这比写文档还管用。
四、告警阈值怎么定
阈值不能拍脑袋。先用Python把历史指标拉出来算分位数,例如取过去两周P99延迟的1.5倍作为临时阈值,跑一周看误报率再调。静态阈值适合错误率这类有明确容忍度的,动态阈值适合流量有周期波动的接口。
可以用简单的统计脚本辅助决策:
import numpy as np
def suggest_threshold(latency_samples):
arr = np.array(latency_samples)
p99 = np.percentile(arr, 99)
return p99 * 1.5
samples = [0.1, 0.2, 0.15, 0.3, 0.12, 1.2, 0.18]
print(suggest_threshold(samples))
这个脚本输出建议阈值,实际生产可接时序数据库直接算。阈值设定后还要定期回顾,业务量级变了老阈值可能失效。监控指标设计不是一次性工作,而是跟着系统演进的持续过程。
五、小结
Python做服务监控,重点不在会调几个库,而在指标设计是否戳中痛点。从黄金信号出发,区分红黄指标,控制基数,补齐业务埋点,再用数据驱动定阈值,才能搭建出真正有用的监控体系。写监控代码前,先画一张指标脑图,比急着接SDK更有价值。