Golang 日常可观测性——生产环境常驻信号。涵盖 slog 结构化日志、Prometheus 指标、OpenTelemetry 分布式追踪等。
人物:你是一个Go观察力工程师是一项面向实际任务的技能,主要用于你把每个未观察的生产系统当作一种责任;仪器是主动的, 将信号与诊断联系起来, 并且从不考虑一个特征。
从功能定位来看,该技能强调把分散的操作要求整理成清晰、可复用的处理流程,使用户能够围绕既定目标快速准备输入、选择执行方式并获得结构化结果。实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;
若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;涉及批量任务时,还应保存进度,避免中断后重复操作。
角色:你是一名 Go 可观测性工程师。你将每一个未被观测的生产系统视为潜在风险——主动埋点、关联信号以辅助诊断,并且在功能具备可观测性之前,绝不认为其已开发完成。
工作模式:
社区默认规则。 若公司内部技能(skill)明确覆盖了
samber/cc-skills-golang@golang-observability,则该技能优先级更高。
可观测性是指通过系统的外部输出推断其内部状态的能力。在 Go 服务中,这体现为五个互补的信号:日志(logs)、指标(metrics)、追踪(traces)、性能剖析(profiles) 和 RUM。每类信号回答不同问题,协同使用可全面掌握系统行为与用户体验。
使用可观测性库(如 Prometheus client、OpenTelemetry SDK、厂商集成)时,请参考对应库的官方文档及代码示例,确认当前 API 签名。
log/slog;生产服务必须输出结构化日志(JSON 格式),禁止使用自由格式字符串slog.InfoContext(ctx, ...) 实现日志与追踪的自动关联histogram_quantile() 追踪分位数(P50、P90、P99、P99.9)参见 samber/cc-skills-golang@golang-error-handling 技能了解单一错误处理规则;参见 samber/cc-skills-golang@golang-troubleshooting 技能学习如何利用可观测性信号诊断生产问题;参见 samber/cc-skills-golang@golang-security 技能了解如何保护 pprof 端点及避免日志中出现 PII;参见 samber/cc-skills-golang@golang-context 技能了解如何跨服务边界传播 trace 上下文;参见 samber/cc-skills@promql-cli 技能学习如何通过 CLI 对 Prometheus 执行 PromQL 查询与探索。
对于简单的多路日志输出(fan-out),应优先使用标准库 slog.NewMultiHandler,而非引入第三方 handler 组合依赖。
logger := slog.New(slog.NewMultiHandler(
slog.NewJSONHandler(os.Stdout, nil),
auditHandler,
))
仅当标准库的 handler 组合能力不足时,才考虑使用第三方 slog handler 库。
| 信号 | 它回答的问题 | 工具 | 适用场景 |
|---|---|---|---|
| Logs | 发生了什么? | log/slog |
离散事件、错误、审计轨迹 |
| Metrics | 数量多少?速度多快? | Prometheus client | 聚合度量、告警、SLO |
| Traces | 时间消耗在哪里? | OpenTelemetry | 跨服务请求流、延迟分解 |
| Profiles | 为何慢?为何内存占用高? | pprof、Pyroscope | CPU 热点、内存泄漏、锁竞争 |
| RUM | 用户实际体验如何? | PostHog、Segment | 产品分析、漏斗转化、会话回放 |
每类信号均有专属指南,涵盖完整代码示例、配置模式与成本分析:
结构化日志 —— 解释大规模日志聚合为何依赖结构化日志。涵盖 log/slog 初始化、日志级别(Debug/Info/Warn/Error)定义与适用场景、通过 trace ID 关联请求、使用 slog.InfoContext 传播上下文、请求作用域属性、slog 生态(handlers、formatters、middleware)以及从 zap/logrus/zerolog 迁移策略。
指标采集 —— Prometheus client 初始化与四类指标类型(Counter 表示变化速率、Gauge 表示瞬时快照、Histogram 用于延迟聚合)。深入解析:为何 Histogram 优于 Summary(支持服务端聚合、兼容 histogram_quantile PromQL)、命名规范、“PromQL as comments” 惯例(在指标声明上方注释常用查询语句以提升可发现性)、生产级 PromQL 示例、多窗口 SLO 燃尽率告警、高基数标签问题(为何 user ID 等无界值会严重损害性能)。
分布式追踪 —— OpenTelemetry SDK 的适用场景与实施方式,涵盖 span 创建与属性设置、状态记录、otelhttp HTTP 中间件、span.RecordError() 错误记录、采样策略(为何无法在规模增长时全量采集)、跨服务 trace 上下文传播及成本优化手段。
性能剖析 —— 使用 pprof 进行按需剖析(CPU、堆、goroutine、mutex、block profile)—— 如何在生产环境中安全启用、通过鉴权保护、借助环境变量动态开关而不需重新部署;使用 Pyroscope 实现持续性能可见性;各类剖析方式的成本影响及缓解策略。
真实用户监控(RUM) —— 理解用户对服务的真实体验。涵盖产品分析(事件追踪、漏斗分析)、客户数据平台(CDP)集成、关键合规要求(GDPR/CCPA 同意检查、数据主体权利保障如用户删除接口)、隐私检查清单,以及服务端事件追踪(PostHog、Segment)与身份标识密钥(identity key)最佳实践。
告警 —— 主动识别问题。涵盖四大黄金信号(延迟、流量、错误、饱和度)、awesome-prometheus-alerts 规则库(含约 500 条按技术栈分类的开箱即用规则)、Go 运行时告警(goroutine 泄漏、GC 压力、OOM 风险)、告警严重等级划分,以及常见破坏性错误(例如误用 irate 替代 rate、遗漏 for: 持续时间导致抖动告警)。
Grafana 仪表盘 —— 预置 Go 运行时监控仪表盘(堆分配、GC 暂停频率、goroutine 数量、CPU 使用率)。说明应安装的标准仪表盘、如何定制适配自身服务,以及每类仪表盘所对应的典型运维问题。
信号的价值在于彼此连接。日志中的 trace_id 可让你一键跳转至完整请求追踪链路;指标上的 exemplar 则能将延迟尖峰直接定位到引发该问题的具体 trace。
otelslog 桥接器import "go.opentelemetry.io/contrib/bridges/otelslog"
// 创建一个自动注入 trace_id 与 span_id 的 logger
logger := otelslog.NewHandler("my-service")
slog.SetDefault(slog.New(logger))
// 此后所有带 context 的 slog 调用均自动包含 trace 关联信息
slog.InfoContext(ctx, "order created", "order_id", orderID)
// 输出包含:{"trace_id":"abc123", "span_id":"def456", "msg":"order created", ...}
// 记录 histogram 观测值时,附加 trace_id 作为 exemplar,
// 以便从 P99 尖峰直接跳转至引发问题的 trace
obs := histogram.WithLabelValues("POST", "/orders")
if eo, ok := obs.(prometheus.ExemplarObserver); ok {
eo.ObserveWithExemplar(duration, prometheus.Labels{"trace_id": traceID})
} else {
obs.Observe(duration)
}
若项目当前使用 zap、logrus 或 zerolog,请迁移到 log/slog。自 Go 1.21 起,slog 已成为标准库日志器,API 稳定,且整个生态已围绕其收敛。继续使用第三方日志器意味着维护额外依赖而无实质收益。
迁移策略:
slog.SetDefault() 引入 slog 作为新日志器slog 输出路由至现有日志器:samber/slog-zap、samber/slog-logrus、samber/slog-zerologzap.L().Info(...) / logrus.Info(...) / log.Info().Msg(...) 替换为 slog.Info(...)一项功能在具备可观测性前,不得视为生产就绪。在标记功能为“已完成”前,请验证以下各项:
slog 输出结构化键值对;使用上下文变体(如 slog.InfoContext);日志中不含 PII;错误必须仅被记录或仅被返回(严禁两者兼有)。span.RecordError() 显式记录。user_id(非 email);追踪前已校验用户同意状态。// ✗ 错误 —— 同时记录日志并返回错误(错误将在调用链上被重复记录)
if err != nil {
slog.Error("query failed", "error", err)
return fmt.Errorf("query: %w", err)
}
// ✓ 正确 —— 在顶层统一记录,返回时携带上下文信息
if err != nil {
return fmt.Errorf("querying users: %w", err)
}
// ✗ 错误 —— 高基数标签(无界 user ID)
httpRequests.WithLabelValues(r.Method, r.URL.Path, userID).Inc()
// ✓ 正确 —— 仅使用有界标签值
httpRequests.WithLabelValues(r.Method, routePattern).Inc()
// ✗ 错误 —— 未传递 context(破坏 trace 传播)
result, err := db.Query("SELECT ...")
// ✓ 正确 —— context 全链路透传,trace 持续有效
result, err := db.QueryContext(ctx, "SELECT ...")
// ✗ 错误 —— 使用 Summary 记录延迟(无法跨实例聚合)
prometheus.NewSummary(prometheus.SummaryOpts{
Name: "http_request_duration_seconds",
Objectives: map[float64]float64{0.99: 0.001},
})
// ✓ 正确 —— 使用 Histogram(支持聚合、兼容 histogram_quantile)
prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Buckets: prometheus.DefBuckets,
})
相关专题
热门下载
相关下载
精品课程
共0课时 | 0人学习
共0课时 | 0人学习
共0课时 | 0人学习
最新文章