深色模式
可观测性数据建模
摘要:同样的埋点数据,标签设计得好能回答十个问题,设计得差连一个都回答不了。本文给出指标命名规范、标签分层方法、语义约定的落地方式,以及一套可执行的建模检查清单。
适用环境
bash
# 先看现状:现有指标名与标签有多少种写法
curl -s http://127.0.0.1:9090/api/v1/label/__name__/values | head -c 300
curl -s http://127.0.0.1:9090/api/v1/label/service_name/values 2>/dev/null | head -c 200
# 统计每个指标的标签基数(基数过高的会暴露出来)
curl -s 'http://127.0.0.1:9090/api/v1/status/tsdb' | head -c 3001
2
3
4
5
6
2
3
4
5
6
操作步骤
1. 指标命名三要素
Prometheus 命名规范:单位_后缀 结构,形如 <namespace>_<subject>_<action>_<unit>。
promql
# 好例子
http_server_request_duration_seconds # 直方图,单位秒
http_server_requests_total # 计数器,累计请求数
process_resident_memory_bytes # gauge,字节
# 坏例子
requestTime # 无单位
http_requests_sum # 语义不清
my_metric_v2 # 无信息量1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
2. 标签维度分层(决定聚合能力与成本)
| 层级 | 标签示例 | 基数 | 用途 |
|---|---|---|---|
| 身份层 | service.name、env、region | 低 | 必选,所有聚合的维度 |
| 拓扑层 | instance、pod、node | 中 | 定位到具体实例 |
| 请求层 | http.route、http.method、status_code | 中 | 接口维度分析 |
| 业务层 | tenant、plan、channel | 中 | 业务分析,需审批 |
| 高基层 | user_id、trace_id、url_full | 极高 | 禁止进入指标 |
3. 用语义约定统一命名
python
# 遵循 OTel 语义约定,不同语言产生的数据才能横向对比
span.set_attribute("http.request.method", "POST")
span.set_attribute("url.path", "/api/order")
span.set_attribute("http.response.status_code", 200)
span.set_attribute("db.system.name", "mysql")
span.set_attribute("server.address", "10.0.0.1")
span.set_attribute("server.port", 3306)1
2
3
4
5
6
7
2
3
4
5
6
7
4. 资源属性与数据属性的分工
yaml
# Resource(谁产生的,稳定性高):service.name / service.version / deployment.environment / host.name
# Attribute(数据本身的特征,变化较多):http.route / db.statement 摘要 / error.type
processors:
resource:
attributes:
- key: service.namespace
value: ecommerce
action: upsert1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
5. 日志结构化建模
json
{
"timestamp": "2026-10-09T10:00:00Z",
"level": "error",
"service.name": "order-svc",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"span_id": "00f067aa0ba902b7",
"event.name": "order.create.failed",
"error.type": "TimeoutError",
"order.id": "ORD123456",
"user.id": "u_10086"
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
原则:日志用结构化字段而非拼接字符串,字段命名与指标/链路保持一致,才能互相跳转。
6. 建模检查清单
bash
cat > /usr/local/bin/obs_model_check.sh <<'EOF'
#!/usr/bin/env bash
echo "== 1. 是否有未设置 service.name 的服务 =="
curl -s http://127.0.0.1:9090/api/v1/label/service_name/values \
| grep -o 'unknown_service[^"]*' | sort -u || echo " OK"
echo "== 2. 指标是否有单位后缀 =="
curl -s http://127.0.0.1:9090/api/v1/label/__name__/values \
| python3 -c "
import sys,json,re
names=json.load(sys.stdin)['data']
bad=[n for n in names if not re.search(r'(_total|_seconds|_bytes|_ratio|_count|_bucket|_sum)$',n)]
print(' 无单位后缀指标数:', len(bad)); print(' 示例:', bad[:5])"
EOF
chmod +x /usr/local/bin/obs_model_check.sh1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
7. 版本演进规则
标签一旦上线就可能被看板和告警引用。新增标签是安全的;重命名或删除标签必须先通知所有使用方,并提供过渡期双写。
验证
bash
/usr/local/bin/obs_model_check.sh
# 1) 语义约定是否统一(同一含义字段不应有多种写法)
curl -s http://127.0.0.1:9090/api/v1/label/__name__/values \
| grep -E 'http_|rpc_|db_' | head -10
# 2) 高基数字段是否被误加进指标
curl -s http://127.0.0.1:9090/api/v1/status/tsdb | python3 -c \
"import sys,json;d=json.load(sys.stdin)['data'];print('series:',d.get('headStats',{}).get('numSeries'))"
# 3) 日志里 trace_id 字段存在
kubectl logs deploy/demo --tail=5 | grep -c 'trace_id'1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
常见坑
WARNING
同一个含义出现多种字段名(service、svc、service_name、app),会让所有聚合失效。上线前先定死一份字段字典,并在 CI 里做静态检查。
WARNING
把 url 全路径作为标签(如 /user/10086/orders),每个用户都产生一条新时间序列,是典型的基数爆炸。应改用路由模板 /user/{id}/orders。
DANGER
上线后随意重命名标签,会导致所有看板瞬间变空、告警静默失效——这比指标缺失更危险,因为没人会立刻发现。任何标签变更都要走变更评审并双写过渡。