Vitest Testing
提供 Vitest 单元测试与集成测试的模式与最佳实践,涵盖断言、异步测试与模拟方法。
基于Go语言的生产环境可观测性指南,涵盖日志、指标、追踪等五类信号。
openclaw skills install @samber/golang-observability命令、参数、文件名以原文为准
角色: 你是一名 Go 语言可观测性工程师。你将每一个未被观测的生产系统视为潜在风险——主动进行可观测性建设,关联信号以诊断问题,并且在功能具备可观测性之前,绝不认为其已开发完成。
模式:
社区默认设置。 若公司自定义技能明确取代
samber/cc-skills-golang@golang-observability技能,则以公司技能为准。
可观测性是指通过系统的外部输出来理解其内部状态的能力。在 Go 服务中,这体现为五种互补的信号:日志、指标、追踪、剖析 和 RUM。每种信号回答不同的问题,共同提供对系统行为和用户体验的全面可见性。
使用可观测性库(如 Prometheus 客户端、OpenTelemetry SDK、厂商集成)时,请参考对应库的官方文档和代码示例,获取当前 API 的准确签名。
log/slog —— 生产环境的服务必须输出结构化日志(JSON 格式),而非自由格式字符串slog.InfoContext(ctx, ...) 将日志与追踪关联histogram_quantile() 函数samber/cc-skills-golang@golang-error-handling 技能中的单一错误处理原则samber/cc-skills-golang@golang-troubleshooting 技能中如何利用可观测性信号诊断生产问题samber/cc-skills-golang@golang-security 技能中保护 pprof 端点及避免日志中泄露敏感信息samber/cc-skills-golang@golang-context 技能中跨服务边界传播追踪上下文的方法samber/cc-skills@promql-cli 技能中如何通过 CLI 查询和探索 PromQL 表达式对于简单的多处理器分发场景,优先使用标准库 slog.NewMultiHandler,避免引入第三方处理器组合依赖。
logger := slog.New(slog.NewMultiHandler(
slog.NewJSONHandler(os.Stdout, nil),
auditHandler,
))仅当标准库的处理器组合无法满足需求时,才考虑使用第三方 slog 处理器库。
| 信号 | 回答的问题 | 工具 | 适用场景 |
|---|---|---|---|
| 日志 | 发生了什么? | log/slog | 离散事件、错误、审计记录 |
| 指标 | 数量多少 / 速度如何? | Prometheus 客户端 | 聚合度量、告警、SLO 监控 |
| 追踪 | 时间消耗在哪里? | OpenTelemetry | 跨服务请求链路、延迟分解 |
| 剖析 | 为何变慢 / 内存占用高? | pprof、Pyroscope | CPU 热点、内存泄漏、锁竞争 |
| RUM | 用户体验如何? | PostHog、Segment | 产品分析、转化漏斗、会话回放 |
每种信号都有独立的指南,包含完整的代码示例、配置模式和成本分析:
log/slog 的配置、日志级别(Debug/Info/Warn/Error)的使用时机、通过 trace_id 关联请求、使用 slog.InfoContext 传播上下文、请求作用域属性、slog 生态(处理器、格式化器、中间件)以及从 zap/logrus/zerolog 迁移的策略。histogram_quantile PromQL)、命名规范、PromQL 作为注释的约定(在指标声明上方编写查询语句以提升可发现性)、生产级 PromQL 示例、多窗口 SLO 燃烧率告警,以及高基数标签问题(为何用户 ID 等无界值会严重损害性能)。otelhttp 中间件用于 HTTP 指标采集、通过 span.RecordError() 记录错误、追踪采样(为何无法在大规模下收集所有数据)、跨服务边界传播追踪上下文,以及成本优化策略。irate 而非 rate,缺少 for: 持续时间导致频繁抖动)。当信号相互关联时,其价值最大。日志中包含 trace_id 可让你从一条日志跳转到完整的请求追踪。指标上的示例(exemplar)可将延迟突增直接关联到引发问题的具体追踪。
otelslog 桥接import "go.opentelemetry.io/contrib/bridges/otelslog"
// 创建一个自动注入 trace_id 和 span_id 的日志处理器
logger := otelslog.NewHandler("my-service")
slog.SetDefault(slog.New(logger))
// 现在每次调用 slog 且携带上下文时都会包含追踪信息
slog.InfoContext(ctx, "order created", "order_id", orderID)
// 输出包含:{"trace_id":"abc123", "span_id":"def456", "msg":"order created", ...}// 在记录直方图观测值时,附加 trace_id 作为示例
// 从而可以从 P99 延迟峰值直接跳转至造成问题的追踪
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 起的标准库日志器,API 稳定,生态已统一采用。继续使用第三方日志器只会增加不必要的依赖而无额外收益。
迁移策略:
slog 作为新日志器,并通过 slog.SetDefault() 设置默认日志器。slog 输出路由至现有日志器:[samber/slog-zap](https://github.com/samber/slog-zap)、[samber/slog-logrus](https://github.com/samber/slog-logrus)、[samber/slog-zerolog](https://github.com/samber/slog-zerolog)。zap.L().Info(...) / logrus.Info(...) / log.Info().Msg(...) 调用替换为 slog.Info(...)。一个功能未达到可观测性就不可视为生产就绪。在标记功能为完成前,请确认:
slog),正确使用上下文变体(slog.InfoContext),日志中不含个人身份信息(PII),错误必须被记录或返回(二者必选其一,不可同时存在)。span.RecordError() 记录。user_id(而非邮箱),追踪前执行同意检查。// ✗ 错误 — 同时记录日志并返回(错误会在调用链中重复记录)
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)
}// ✗ 错误 — 高基数标签(用户 ID 无限制)
httpRequests.WithLabelValues(r.Method, r.URL.Path, userID).Inc()
// ✓ 正确 — 仅使用有限数量的标签值
httpRequests.WithLabelValues(r.Method, routePattern).Inc()// ✗ 错误 — 未传递上下文(破坏追踪传播)
result, err := db.Query("SELECT ...")
// ✓ 正确 — 上下文传递,追踪持续
result, err := db.QueryContext(ctx, "SELECT ...")go
// ✗ 错误 — 用 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,
})
已收录 2 个 Skill