Go应用必须将结构化JSON日志输出到stdout,否则Kubernetes日志采集链路(Fluent Bit/Filebeat→Loki/ELK)完全失效;需用zerolog/zap编码、禁用文本拼接、适配containerd CRI解析器,并通过multiline合并panic堆栈、trace_id透传确保日志可查可溯。

Go 应用写结构化日志到 stdout 是 Kubernetes 日志集中采集能正常工作的前提;不这么做,后续所有采集配置、Operator、Loki 查询都会失效。
Go 程序必须用 JSON 格式写到 stdout/stderr
Fluent Bit、Filebeat 等采集器默认只解析 CRI 格式(containerd)或 Docker JSON 格式日志。Go 应用若用 log.Printf 或第三方库(如 logrus)输出纯文本,采集器无法提取 timestamp、level、msg 等字段,最终入库的只是整行字符串,丧失可过滤、可聚合能力。
- 推荐用
zerolog或zap(带jsonencoder),例如:logger := zerolog.New(os.Stdout).With().Timestamp().Str("service", "api").Logger()<br>logger.Info().Str("path", "/health").Int("status", 200).Msg("request handled") - 避免手动拼接 JSON 字符串——容易漏转义、缺字段、格式不一致
- 不要同时启用
zerolog.ConsoleWriter(终端美化)和os.Stdout输出,会导致非 JSON 内容混入 - 若必须兼容调试输出,可通过环境变量控制 encoder 类型:
if os.Getenv("ENV") == "prod" { use JSON } else { use console }
DaemonSet 采集器必须配对 containerd 的 cri 解析器
当前主流集群已基本完成从 Docker 到 containerd 的迁移,但很多 Fluent Bit 配置仍沿用旧的 docker parser,导致日志时间戳错乱、容器名为空、甚至采集失败。
评估 Kubernetes 集群安全态势,覆盖 RBAC、工作负载安全、网络策略、基础设施即代码(IaC)、运行时监控和密钥管理等 30 项控制项……
- 输入插件中必须显式指定:
parser cri,而非parser docker -
Path要匹配 containerd 实际路径:/var/log/pods/*/*.log(不是/var/log/containers/*.log,后者是软链接,部分版本不可靠) - 启用
db文件(如db /var/log/flb_kube.db)防止节点重启后重复采集——否则同一条日志可能被发两次 - 若日志含 Java 堆栈等多行内容,需开启
multiline并设BeginLineTimeoutMs 3000(不能低于 1000)
Sidecar 模式下 stdout 重定向必须显式声明
当 Go 应用因历史原因无法改日志输出方式(比如写文件到 /app/logs/app.log),用 Sidecar 采集时,kubectl logs 就查不到内容——因为 kubelet 只读 stdout/stderr,不读容器内任意文件。
- 主容器启动命令末尾必须加:
2>&1 | tee /proc/1/fd/1(把 stderr 合并并刷到 init 进程 stdout),否则 Sidecar 读不到 stderr - Sidecar 容器挂载的
emptyDir卷,要与主容器写日志路径严格一致,且权限匹配(Go 应用常以非 root 用户运行) - Sidecar 配置里
tail输入的Path必须指向该共享卷内路径,例如:/var/log/app/*.log,不能写成/app/logs/*.log(宿主机视角路径) - Sidecar 自身日志也应走 stdout,避免嵌套采集问题
Operator 管理的 Flow 中字段增强易被忽略
用 Logging Operator(如 Banzai Cloud)时,Flow CRD 可自动注入 namespace、pod_name、container_name,但这些字段默认来自日志文件路径解析——如果 Go 应用日志没打标,或 Sidecar 采集路径不标准,这些字段就为空。
- 务必在
Flow中显式添加filter插件补全元数据,例如:record_modifier插件注入cluster_id或env - 若日志中已有
trace_id字段,可在Flow中配置match规则路由到专用 Loki 日志流,避免污染通用索引 - Operator 不会自动处理 Go 应用 panic 堆栈——需在
filter阶段用multiline插件识别^panic:开头的行,否则每行都当独立事件
真正卡住人的往往不是配置语法,而是 Go 日志是否真写了 JSON 到 stdout、containerd 解析器是否启用、以及 Operator 的字段注入是否依赖了错误的路径来源——这三个点任一出错,整个链路就断在源头。

















