go应用必须将日志输出到os.stdout或os.stderr,kubernetes仅采集主进程的stdout/stderr;写入文件(如/var/log/app.log)或未显式调用log.setoutput(os.stdout)会导致kubectl logs为空;需本地验证go run main.go | cat -有输出,且zap等结构化日志库须配置outputpaths为["stdout"]并defer logger.sync()。

Go应用必须把日志写到os.Stdout或os.Stderr
Kubernetes只采集容器主进程的stdout和stderr,其他路径一概无视。写/var/log/app.log、./logs/或log.Printf默认输出到文件,kubectl logs永远为空。
- 启动时显式调用
log.SetOutput(os.Stdout),别依赖默认行为 - 用
slog.NewJSONHandler(os.Stdout, nil)替代log.Printf,避免无结构文本 - 本地验证:运行
go run main.go | cat -,没输出就别推镜像 - Dockerfile里禁止出现
RUN mkdir -p /var/log/app或COPY app.log /var/log/这类操作
zap.NewProduction()直接用会丢日志
默认配置下zap.NewProduction()看似能用,但Pod重启时未flush的日志永久丢失——因为Kubernetes在SIGTERM后只等几秒就强制kill,logger.Sync()根本来不及执行。
- 必须在
main()退出前调用logger.Sync(),建议用defer logger.Sync() - 显式设置
cfg.OutputPaths = []string{"stdout"},防止误写stderr(某些采集器只监听stdout) - 关键字段如
service_name、trace_id需在根logger初始化时注入,而不是每条日志临时加 - 禁用颜色:
cfg.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
kubectl logs返回空不是命令问题,是Pod没跑起来
90%的“查不到日志”本质是Pod没进入Running状态。盲目加--previous或换参数,只会掩盖真正的问题。
- 先执行
kubectl get pods:看到Pending查资源配额,CrashLoopBackOff立刻kubectl describe pod看Events -
describe里重点盯Events区域:镜像拉取失败?端口被占?Liveness探针超时?OOMKilled?这些都比日志本身更优先 - 多容器Pod必须指定
-c <container-name></container-name>,否则kubectl logs默认读第一个容器且不报错 - Pod重启过,当前实例日志为空,但上一个崩溃实例可能有线索:
kubectl logs <pod-name> --previous</pod-name>
goroutine里trace_id消失的真正原因
全局变量存trace_id、中间件提取后不传context、启动goroutine时不带logger——这三步漏掉任何一步,下游日志就彻底断联。
- HTTP handler中从
r.Header.Get("X-Request-ID")取值,为空则生成ulid.Make().String() - 用
ctx := r.Context(); logger := logger.With(zap.String("trace_id", tid))创建子logger - 启动goroutine时显式传入该
logger,而非引用包级全局logger - gRPC场景下,在
UnaryServerInterceptor中同样提取并注入metadata.MD向下游透传
With()或一次context.WithValue(),整条trace就断成碎片,再好的采集系统也拼不回来。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











