深度解读
LGTM 可观测性四件套:Grafana、Prometheus、Loki、Tempo 的生产落地
用 Grafana、Prometheus、Loki 和 Tempo 搭出指标、日志、链路追踪与告警闭环,并把采集、存储、查询和排障路径工程化。
#核心结论
Grafana、Prometheus、Loki、Tempo 不是四个并列面板工具,而是一套可观测性闭环:Prometheus 负责指标,Loki 负责日志,Tempo 负责分布式追踪,Grafana 负责统一查询、展示、告警和跨信号跳转。对 AI 全栈架构师来说,这套组合的价值不只是“能看到监控”,而是把一次生产故障从发现、定位、证据收集到修复验证,压成可重复的工程路径。
需要先纠正一个常见说法:Grafana 官方常把 LGTM 写成 Loki、Grafana、Tempo、Mimir,其中 Mimir 是云原生长期指标存储。很多团队在开源自建或中小规模阶段会用 Prometheus 承担 metrics 层,所以本文讨论的是更常见的自建组合:Grafana + Prometheus + Loki + Tempo。这个组合可以直接起步,但进入大规模或长期保留场景后,要为 Prometheus 增加 remote write、Mimir、Thanos 或 VictoriaMetrics 这类长期存储能力。
#技术背景
可观测性不能只看单一信号。指标告诉团队“什么时候、哪个系统指标异常”,日志告诉团队“异常现场发生了什么”,链路追踪告诉团队“一次请求穿过哪些服务、慢在哪里、失败在哪个 span”。Grafana 的角色是把这些信号放到同一个排障界面里,通过 dashboard、Explore、alert、data source 和 correlation 把上下文串起来。
Prometheus 的核心模型是带 label 的时间序列。它通过 HTTP pull 模型抓取 /metrics,用 PromQL 做聚合、窗口计算和告警规则。Loki 的设计更像“日志版 Prometheus”:它不全文索引日志内容,而是主要索引 stream labels,再把日志压缩成 chunk 放到对象存储或文件系统。Tempo 则是追踪后端,用 OpenTelemetry、Jaeger 或 Zipkin 协议接收 trace,并通过 object storage 降低大规模 trace 保存成本。
新的采集侧建议优先使用 Grafana Alloy 或 OpenTelemetry Collector。Promtail 已经在 2026-03-02 进入 EOL,后续日志采集不要再把 Promtail 作为新项目默认选型。Alloy 可以把 Prometheus 和 OpenTelemetry 管线放到同一套 collector 配置里,覆盖 metrics、logs、traces,适合 Kubernetes 和多环境统一治理。
#组件边界
| 组件 | 主要职责 | 查询语言 / 接口 | 生产边界 |
|---|---|---|---|
| Grafana | 仪表盘、Explore、告警、数据源管理、跨信号跳转 | Dashboard query、Alert rule、Datasource plugin | 不存主数据,重点治理权限、文件夹、告警规则和 datasource 凭据 |
| Prometheus | 指标采集、短中期时序存储、规则计算、告警触发 | PromQL、remote write、Alertmanager | 单节点自治强,但长期存储和全局查询需要扩展层 |
| Loki | 日志写入、压缩存储、按 label 查询、LogQL 分析 | LogQL、Loki HTTP API | 成本低,但 label 设计错误会导致查询慢或存储膨胀 |
| Tempo | trace 接收、存储、TraceQL 查询、span metrics | OTLP、Jaeger、Zipkin、TraceQL | Trace 价值依赖应用埋点、采样策略和 trace_id 贯穿 |
| Alloy / OTel Collector | 采集、转换、批处理、转发 telemetry | Prometheus scrape、OTLP、Loki write | 应作为边车或 DaemonSet 标准件,而不是每个服务单独拼采集脚本 |
这几个边界要守住。Grafana 不应该被当成数据仓库,Prometheus 不适合保存完整业务事件,Loki 不适合当审计全文检索库,Tempo 也不会替代日志。真正可靠的排障体验来自“职责分开、上下文打通”。
#标准数据流
一条推荐的生产数据流如下:
Application / Runtime / Kubernetes / Node
|-- metrics: /metrics, exporter, OpenTelemetry metrics
| -> Prometheus scrape
| -> recording rules / alert rules
|
|-- logs: stdout, file log, structured JSON
| -> Alloy / Fluent Bit
| -> Loki labels + compressed chunks
|
|-- traces: OpenTelemetry SDK / auto instrumentation
-> Alloy / OTel Collector
-> Tempo object storage
Grafana
-> Prometheus datasource for RED / USE metrics
-> Loki datasource for service logs
-> Tempo datasource for request traces
-> alerting and drill-down links across the three signals
落地时,所有服务必须统一几个基础字段:service.name、deployment.environment、namespace、pod、version、trace_id。其中 service.name 和环境信息适合成为指标、日志和 trace 的共同过滤维度;trace_id 应写入结构化日志,并通过 Grafana derived fields 从 Loki 跳到 Tempo,但不建议把海量唯一 trace_id 当作 Loki label。
#指标层设计
Prometheus 指标层先从 RED 和 USE 两套方法起步。业务服务看 RED:请求速率 rate、错误率、持续时间 histogram。基础设施看 USE:使用率、饱和度、错误数。核心 SLI 应先变成 recording rules,再供 dashboard 和 alert rule 复用,避免每个面板都写一遍复杂查询。
示例 PromQL:
sum by (service) (
rate(http_requests_total{job="api",status=~"5.."}[5m])
)
/
sum by (service) (
rate(http_requests_total{job="api"}[5m])
)
histogram_quantile(
0.95,
sum by (service, le) (
rate(http_request_duration_seconds_bucket{job="api"}[5m])
)
)
指标命名要稳定,label 要克制。可以用 service、method、route、status_code,但不要把 user_id、order_id、request_id 放进 label。高基数会直接放大内存、索引、查询成本,也会让 Prometheus 在故障时先被监控数据拖垮。
#日志层设计
Loki 的关键不是“把所有日志都收进来”,而是把 label 设计好。推荐 label 控制在低基数字段:cluster、namespace、service、pod、container、level。高基数字段放在日志正文中,以 JSON 字段存在,再用 LogQL pipeline 解析。
示例 LogQL:
{namespace="prod", service="checkout"} |= "error"
{namespace="prod", service="checkout"}
| json
| trace_id != ""
| line_format "{{.timestamp}} {{.level}} {{.trace_id}} {{.message}}"
日志格式建议统一为结构化 JSON,至少包含 timestamp、level、message、service、trace_id、span_id、tenant、error.kind。如果日志仍是自由文本,Loki 也能查,但很难稳定地做 trace 跳转、错误聚类和告警去噪。
#追踪层设计
Tempo 的价值来自请求级上下文。接入时不要只给网关加 trace,要让入口服务、核心业务服务、数据库访问、外部 API、队列消费者都带上 span。否则 trace 图会在最关键的位置断掉,只能证明“入口慢了”,不能证明“慢在哪里”。
OpenTelemetry 侧应统一 resource attributes:
service.name: checkout-api
service.version: 2026.06.12-1
deployment.environment: prod
cloud.region: cn-shanghai
k8s.namespace.name: prod
采样策略要分层:开发和灰度环境可以高采样,生产环境默认 head sampling 或 tail sampling;错误请求、慢请求和关键租户请求应提高保留概率。Tempo 与 Prometheus 的 exemplars、Loki derived fields 结合后,可以从 P95 延迟面板直接跳到 trace,再从 trace 跳到同一 trace_id 的日志。
#Grafana 排障路径
一个成熟的 dashboard 不应该只堆图,而应该支持固定排障路径:
- 总览层:服务健康、请求量、错误率、P95/P99、饱和度、当前告警。
- 服务层:按
service、route、status_code拆解延迟和错误。 - Trace 层:从异常指标 exemplar 跳到 Tempo trace,确认慢 span 和失败 span。
- 日志层:从 trace 中的
trace_id跳到 Loki,查同一次请求的错误栈和业务上下文。 - 变更层:叠加 deploy version、feature flag、配置变更和扩缩容事件。
告警也要按这条路径设计。不要给每个指标都配告警,而是围绕用户影响做 SLO/SLI:错误率、延迟、可用性、队列积压、成本异常。告警内容里必须带 Grafana dashboard 链接、Prometheus 查询、Loki 查询模板、Tempo trace 入口和 runbook 链接,否则值班人员还要重新组装上下文。
#Kubernetes 部署建议
小规模集群可以这样起步:
kube-prometheus-stack部署 Prometheus、Alertmanager、Grafana 和基础 Kubernetes dashboard。- Loki 采用 single binary 或 simple scalable mode,日志先保留 7 到 14 天。
- Tempo 采用 single binary,后端接 S3 兼容对象存储。
- Alloy 以 DaemonSet 收集节点和容器日志,以 Deployment 接收 OTLP traces。
- 应用统一接入 OpenTelemetry SDK,并把
trace_id注入日志 MDC / context。
中大型集群要把存储和查询层拆开:Prometheus remote write 到长期存储;Loki 使用对象存储和读写分离;Tempo 使用对象存储、compactor、querier 和 ingester 分层;Grafana datasources、dashboards、alert rules 用 GitOps 管理。否则一旦 dashboard、告警和 datasource 都在 UI 里手工维护,环境复制和事故复盘会变得不可控。
#最小 Helm 落地样例
下面这组命令适合做实验环境或小规模起步模板,生产环境必须锁定 chart 版本,并把 values 文件纳入 GitOps。不要继续使用已经废弃的 loki-stack 图表,也不要让 Loki、Tempo 在生产里长期依赖本地磁盘。
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
kubectl create namespace observability --dry-run=client -o yaml | kubectl apply -f -
helm upgrade --install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
--namespace observability \
--values values/prometheus-stack.yaml
helm upgrade --install loki grafana/loki \
--namespace observability \
--values values/loki.yaml
helm upgrade --install tempo grafana/tempo \
--namespace observability \
--values values/tempo.yaml
helm upgrade --install alloy grafana/alloy \
--namespace observability \
--values values/alloy.yaml
最小 values 可以先这样分层:
# values/prometheus-stack.yaml
grafana:
enabled: true
adminPassword: "${GRAFANA_ADMIN_PASSWORD}"
prometheus:
prometheusSpec:
retention: 15d
scrapeInterval: 30s
evaluationInterval: 30s
externalLabels:
cluster: prod-a
# values/loki.yaml
deploymentMode: SingleBinary
loki:
auth_enabled: false
commonConfig:
replication_factor: 1
storage:
type: filesystem
singleBinary:
replicas: 1
# values/tempo.yaml
tempo:
reportingEnabled: false
retention: 168h
receivers:
otlp:
protocols:
grpc:
http:
# values/alloy.yaml
alloy:
configMap:
create: true
content: |
logging {
level = "info"
}
otelcol.receiver.otlp "default" {
grpc {}
http {}
output {
traces = [otelcol.exporter.otlp.tempo.input]
}
}
otelcol.exporter.otlp "tempo" {
client {
endpoint = "tempo.observability.svc.cluster.local:4317"
tls {
insecure = true
}
}
}
这组配置故意保持最小:Prometheus 先解决指标,Loki 和 Tempo 先能跑通本地链路,Alloy 先作为 OTLP 入口。进入生产前要把 Loki 切到对象存储和 scalable/microservices 模式,把 Tempo 切到对象存储或 distributed chart,把 Grafana 密码和 datasource 凭据改成 Secret,把 ingress、TLS、RBAC、NetworkPolicy、resource requests/limits、备份和升级策略补齐。
#Grafana Datasource 配置
Grafana datasource 建议用 provisioning 文件管理,而不是手工在 UI 里点。这样环境重建、回滚和审计都有明确版本。下面示例假设 Prometheus、Loki、Tempo 都在 observability namespace 内,并且 Grafana 能通过集群内 DNS 访问它们:
# grafana/provisioning/datasources/observability.yaml
apiVersion: 1
prune: true
datasources:
- name: Prometheus
type: prometheus
uid: prometheus
access: proxy
url: http://kube-prometheus-stack-prometheus.observability.svc.cluster.local:9090
isDefault: true
jsonData:
timeInterval: 30s
exemplarTraceIdDestinations:
- name: trace_id
datasourceUid: tempo
- name: Loki
type: loki
uid: loki
access: proxy
url: http://loki-gateway.observability.svc.cluster.local
jsonData:
derivedFields:
- name: TraceID
matcherRegex: '"trace_id":"([A-Za-z0-9]+)"'
datasourceUid: tempo
url: '$${__value.raw}'
- name: Tempo
type: tempo
uid: tempo
access: proxy
url: http://tempo.observability.svc.cluster.local:3200
jsonData:
tracesToLogsV2:
datasourceUid: loki
spanStartTimeShift: -5m
spanEndTimeShift: 5m
filterByTraceID: true
filterBySpanID: false
tracesToMetrics:
datasourceUid: prometheus
serviceMap:
datasourceUid: prometheus
这份配置的关键点是固定 uid。Prometheus exemplar、Loki derived fields、Tempo traces-to-logs 都依赖 datasource UID 做内部跳转。如果 UID 在不同环境里漂移,面板可以显示,但跨信号跳转会断。
日志字段也要和 datasource 配置匹配。应用输出 JSON 日志时至少要保证:
{
"timestamp": "2026-06-12T10:30:00.000Z",
"level": "error",
"service": "checkout-api",
"trace_id": "0af7651916cd43dd8448eb211c80319c",
"span_id": "b7ad6b7169203331",
"message": "payment provider timeout"
}
如果日志字段叫 traceId 或 traceID,就要同步调整 Loki matcherRegex,不要让应用、采集器和 Grafana 各用一套命名。
#成本与容量控制
成本控制的核心是三件事:label cardinality、采样、retention。
指标层:限制高基数 label,给关键指标建 recording rules,Prometheus 本地保留短周期数据,长期数据 remote write。
日志层:日志 label 只放低基数字段,正文压缩进 chunk;高频 debug 日志默认不进生产 Loki,或者只保留短周期;按 namespace、tenant、service 设置 ingestion limit。
追踪层:默认采样,错误和慢请求优先保留;trace payload 不要塞大对象、Prompt 全文或敏感数据;span attribute 要有治理清单,避免把隐私字段写入 trace。
Grafana 层:dashboard 查询要设时间窗口和变量默认值,避免打开首页就扫全量日志或超长时间序列;告警规则要做分组、抑制和静默策略,避免告警风暴。
#风险边界
第一,Loki 不等于 Elasticsearch。它非常适合按 label 和时间窗口查服务日志,但不适合做任意字段的强全文检索和复杂审计检索。如果合规审计需要精确全文查询,应保留专门审计存储。
第二,裸 Prometheus 不等于长期指标平台。Prometheus 单节点自治和故障时可用性很强,但跨集群全局查询、多年保留、海量 cardinality 和多租户隔离需要额外架构。
第三,Tempo 不会自动让系统可追踪。没有统一 OpenTelemetry 规范、没有跨服务 context propagation、日志不带 trace_id,Tempo 只能保存碎片化 trace,排障价值会大幅下降。
第四,不要在 2026 年的新项目里继续默认 Promtail。Promtail 已 EOL,采集标准件应转向 Grafana Alloy、OpenTelemetry Collector 或团队已经标准化的日志 agent。
#验证清单
- 每个服务是否暴露
/metrics或 OpenTelemetry metrics,并能被 Prometheus 或 Alloy 稳定采集。 - Prometheus 是否有 RED / USE dashboard、recording rules、核心 SLO alert rules。
- Loki label 是否只包含低基数字段,日志正文是否包含
trace_id、span_id、level、service。 - Tempo 是否能查到入口服务到核心依赖的完整 trace,慢请求和错误请求是否提高采样率。
- Grafana 是否配置了 Loki derived fields 和 Tempo datasource,能从日志跳 trace、从 trace 找日志。
- 告警是否包含 dashboard、PromQL、LogQL、TraceQL 或 trace 入口,以及 runbook。
- 是否演练过一次真实故障路径:指标触发、trace 定位、日志取证、修复后指标回落。
- 是否有 retention、采样、ingestion limit 和 dashboard 查询窗口,避免可观测系统本身成为成本和稳定性风险。
#AI 应用指标清单
AI 应用不要只复用传统 HTTP dashboard。模型调用、RAG、Agent 和成本都要变成一等可观测对象。OpenTelemetry 的 GenAI 语义约定仍处在发展阶段,但已经给出了值得采用的命名方向,例如 gen_ai.client.token.usage 和 gen_ai.client.operation.duration。落地时要明确版本策略,避免不同 SDK 发出互不兼容的字段。
| 方向 | 推荐指标 | 关键维度 | 告警或排障用途 |
|---|---|---|---|
| 模型调用 | gen_ai.client.operation.duration |
gen_ai.provider.name、gen_ai.request.model、gen_ai.operation.name |
判断模型供应商、模型版本或操作类型导致的延迟抖动 |
| Token 成本 | gen_ai.client.token.usage |
gen_ai.token.type、gen_ai.request.model、tenant |
识别输入膨胀、输出异常、缓存失效和租户成本异常 |
| RAG 检索 | rag_retrieval_duration_seconds、rag_retrieval_hits_total |
index、top_k、reranker、tenant |
判断向量库、重排器或知识源是否拖慢回答 |
| RAG 质量 | rag_answer_citation_coverage_ratio、rag_retrieval_empty_total |
knowledge_base、query_type |
识别无引用回答、召回为空和知识库新鲜度问题 |
| Agent 工具 | agent_tool_calls_total、agent_tool_call_duration_seconds、agent_tool_call_errors_total |
tool_name、approval_required、result |
定位外部工具失败、审批阻塞和超时重试 |
| Agent 任务 | agent_steps_total、agent_task_duration_seconds、agent_task_aborts_total |
agent_name、task_type、stop_reason |
发现步骤爆炸、循环调用和人工接管频率 |
| 安全治理 | ai_policy_blocks_total、ai_sensitive_output_blocks_total |
policy_name、risk_type、tenant |
追踪提示注入、越权工具调用和敏感信息拦截 |
| 业务结果 | ai_user_corrections_total、ai_handoff_total、ai_resolution_ratio |
feature、user_segment |
判断 AI 功能是否真的减少人工处理,而不是只增加调用量 |
这些指标的 label 仍然要克制。tenant、model、tool_name 可以成为稳定维度,prompt、user_id、document_id、request_id 不应该进入指标 label。Prompt、检索片段和模型输出如果需要审计,应进入受控日志或事件存储,并经过脱敏、采样和权限隔离。
AI dashboard 可以按三层组织:第一层看业务体验,包括成功率、人工接管率、P95 延迟和单位请求成本;第二层看模型与 RAG,包括 token、模型错误、召回为空、引用覆盖和重排耗时;第三层看 Agent 与安全,包括工具失败、审批耗时、策略阻断和异常终止。这样值班人员能先判断用户影响,再下钻到模型、数据或工具链。
#采用建议
建议先接入一条核心业务链路,而不是全站一次性铺开。第一阶段只做服务指标、基础日志和 Grafana dashboard;第二阶段接入 OpenTelemetry trace 和 trace_id 日志关联;第三阶段再做 SLO 告警、采样策略、长期存储和 GitOps 化配置。这样团队可以在一次真实故障里验证闭环,而不是先投入大量时间搭出一套没人用的监控平台。
对 AI 系统来说,还要把模型调用、RAG 检索、Agent 工具调用和成本指标纳入同一套可观测性语言。一个可用的 AI dashboard 至少应该回答:哪个模型慢、哪个检索源命中差、哪个工具调用失败、哪类用户请求成本异常、一次异常回答对应哪条 trace 和哪段日志。四件套的最终目标不是“监控很多”,而是让复杂系统在出问题时仍然能被解释、被定位、被修复。
证据来源
- https://grafana.com/docs/grafana/latest/
- https://prometheus.io/docs/introduction/overview/
- https://grafana.com/docs/loki/latest/get-started/overview/
- https://grafana.com/docs/tempo/latest/
- https://grafana.com/docs/alloy/latest/
- https://grafana.com/docs/loki/latest/send-data/promtail/
- https://grafana.com/docs/helm-charts/
- https://grafana.com/docs/grafana/latest/administration/provisioning/
- https://grafana.com/docs/grafana/latest/datasources/tempo/configure-tempo-data-source/provision/
- https://opentelemetry.io/docs/specs/semconv/gen-ai/
- https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-metrics/
修订记录
最近修订:2026-06-12。CONVEE 在原始证据或架构判断变化时更新本文。