必须用slog.With注入上下文字段并配slog.NewJSONHandler输出,确保每行是合法JSON;避免手拼字符串或误用fmt.Sprintf,否则Loki等系统无法解析结构化字段。

用 log/slog 做结构化日志输出,别手写 JSON
Go 1.21+ 自带的 slog 是目前最轻量又合规的选择。手动拼接 JSON 或用第三方库(如 logrus)容易漏字段、错格式,监控系统(比如 Loki、Prometheus)解析失败率高。
关键点:必须用 slog.With 注入上下文字段,避免在 slog.Info 的消息里塞 key-value 字符串;同时设置 slog.NewJSONHandler 输出,确保每行是合法 JSON。
slog.With("service", "api").With("trace_id", traceID).Info("request completed", "status", 200, "duration_ms", 12.3)- 不要写成:
slog.Info(fmt.Sprintf("request completed status=%d duration_ms=%.1f", 200, 12.3))—— 这类字符串无法被日志系统提取结构化字段 - 启动时加
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, nil))),否则slog默认用文本格式
把日志发到 Loki 而不是本地文件,promtail 配置要匹配 slog 字段
Loki 不索引日志内容,只靠 label 匹配和过滤。如果 slog 打出 service=auth 和 level=error,但 promtail 的 pipeline_stages 没提取它们,Loki 就收不到有效 label,查起来等于盲搜。
常见错误是 promtail 还在用旧的 regex 提取方式,而 slog 输出的是标准 JSON 行。直接用 json stage 最稳:
立即学习“go语言免费学习笔记(深入)”;
{"service":"auth","level":"error","msg":"db timeout","trace_id":"abc123","time":"2024-05-20T10:30:45Z"}
-
promtail配置中启用jsonstage:{ "json": { "labels": {"service": "service", "level": "level"} } } - 确保
slog输出的 key 名(如service)和promtail中写的完全一致,大小写敏感 - 别依赖
filename当 service 名——容器环境里/var/log/app.log对所有服务都一样,label 就失效了
用 prometheus/client_golang 暴露指标,报警规则写在 Prometheus 里而非代码中
日志解决“发生了什么”,指标解决“发生了多少次/多快/多慢”。想告警“5 分钟内 error 日志超 10 条”,不能在 Go 里起 goroutine 统计再发 HTTP 请求——这会拖慢主逻辑,且扩缩容后统计不一致。
正确路径:日志进 Loki → 用 loki-canary 或 promtail 的 metrics exporter 把日志转成 Prometheus 指标(如 loki_entry_count_total{level="error",service="api"})→ 在 Prometheus 写 rate(loki_entry_count_total{level="error"}[5m]) > 0.033(即 10/300 秒)→ Alertmanager 发通知。
- Go 代码里只需暴露基础指标(如
http_request_duration_seconds),用prometheus/client_golang的promhttp.Handler()挂在/metrics - 避免在代码里调
Alertmanager API—— 权限、重试、去重全得自己实现,且违反可观测性分层原则 - 如果非要用日志触发报警,优先走 Loki 的
Alerting Rules(v2.8+ 支持),而不是从 Go 进程里主动上报
本地开发时用 docker-compose 联调,但别信 stdout 重定向能代替日志收集链路
很多人本地跑 go run main.go | grep error 或用 docker logs 看输出,就以为日志通了。实际上 stdout 到 promtail 再到 Loki 有至少三层 buffer 和编码转换,任一环出问题都会静默丢日志。
- 本地必须跑全栈:
docker-compose up loki promtail grafana,并在 Go 应用里配置promtail的scrape_configs指向本机localhost:9080(不是容器名) -
promtail启动后看日志是否有discovered new targets和positions.yaml是否在更新,这是它真正在读日志的证据 - 用
curl http://localhost:3100/loki/api/v1/label/service/values查 Loki 是否收到service标签——比翻 Grafana 界面更快定位链路断在哪
最常被跳过的环节是 promtail 的权限和路径映射:Docker 容器里 /var/log/app.log 映射宿主机文件时,若宿主机该文件属主不是 root,promtail 可能因权限不足静默跳过。这时候日志看着在 stdout,其实根本没进 Loki。


















