容器中 log.Println 日志不可见,因 Go 默认输出到 os.Stderr,需用 log.SetOutput(os.Stdout) 统一输出至 stdout/stderr 才能被 Docker/K8s 日志机制捕获并采集。

为什么容器里 log.Println 的日志看不到?
因为 Go 默认把日志写到 os.Stderr,而容器运行时(如 Docker)会把 stderr 和 stdout 重定向为流式输出。如果没配置日志驱动或没挂载日志收集器,这些日志就只是“飘过”,既不落盘也不转发。
- Docker 默认用
json-file驱动,日志存在宿主机/var/lib/docker/containers/,但不可读、无结构、难过滤/ -json.log - Kubernetes 中 Pod 日志默认通过
kubelet读取容器的stdout/stderr流,再暴露给kubectl logs—— 这只是临时查看,不是集中采集 - Go 程序若自己打开文件写日志(比如用
os.OpenFile),反而绕过了容器日志机制,导致日志彻底丢失
用 log.SetOutput 统一输出到 os.Stdout 是最简方案
别写文件,别开 goroutine 轮询,让容器运行时接管输出流。这是所有日志采集工具(Fluentd / Filebeat / Loki / Datadog Agent)能工作的前提。
package mainimport ( "log" "os" )
func main() { // 关键:强制所有 log.* 输出到 stdout log.SetOutput(os.Stdout) // 可选:加前缀让结构更清晰(但注意:这会让日志变非 JSON,影响解析) log.SetPrefix("[app] ") log.SetFlags(log.LstdFlags | log.Lshortfile)
log.Println("service started")}
- 不要用
log.Printf混合格式和内容;统一用log.Print/log.Println,便于后续用正则或 parser 提取字段 - 避免在日志里拼接敏感信息(如密码、token),否则会被采集进日志系统并长期留存
- 如果要用结构化日志(推荐),改用
go.uber.org/zap并配置zapcore.Lock(os.Stdout)
如何让日志带 trace_id 或 request_id?
纯 log 包做不到上下文透传,必须用支持 context 的日志库,否则每个请求的日志都是孤岛,无法关联分析。
立即学习“go语言免费学习笔记(深入)”;
-
zap+middleware是主流组合:HTTP handler 中从请求提取X-Request-ID,注入context.Context,再传给 zap 的With或Named - 示例中不要用
log.Printf("req_id=%s, msg=%s", reqID, msg)—— 这种字符串拼接破坏结构,且无法被 Loki 的logfmt或 ES 的dissect正确解析 - 如果坚持用标准库,至少封装一层:
func LogWithReqID(reqID string, v ...interface{}) { log.Printf("[req_id=%s] %v", reqID, fmt.Sprint(v...)) }但依然不推荐,缺失字段类型、易被截断、难做聚合统计
Docker/K8s 下 Fluentd/Filebeat 怎么识别 Go 日志?
它们不关心语言,只认输出位置和格式。关键配置点只有两个:源头路径 + 解析规则。
- Docker 场景:Fluentd 的
需指向/var/lib/docker/containers/**/*-json.log,并启用parse json插件(Docker 的 json-file 驱动输出是每行一个 JSON 对象) - K8s 场景:Filebeat DaemonSet 默认采集
/var/log/containers/*.log,该路径下是软链到 Docker JSON 日志,同样需开启json.keys_under_root: true - 如果你在 Go 里用了
zap并启用了zapcore.JSONEncoder,日志已经是合法 JSON 行,无需额外 parse;但如果用了ConsoleEncoder,就得配dissect或grok规则,容易出错
真正容易被忽略的是时间字段:Docker JSON 日志自带 time 字段(ISO8601 格式),但 Go 的 log.SetFlags(log.LstdFlags) 输出的是本地时区+无毫秒的时间字符串,和采集端时间戳对不上,查问题时序会乱。要么禁用 LstdFlags,要么统一用 UTC+毫秒的 JSON 时间字段。










