go服务必须输出结构化json到stdout,否则promtail无法有效采集;需用zap.newproduction()或logrus.jsonformatter确保每行合法json,字段命名统一(如request_id),避免手动拼接,并匹配k8s实际日志路径/var/log/pods/,同时正确配置promtail pipeline_stages与loki标签策略。

Go服务必须输出结构化JSON到stdout,否则Promtail收不到有效日志
不是“连上Loki就行”,而是Loki本身不直接读你的log.Printf——它只依赖Promtail从stdout/stderr采集。如果Go服务还用log包或logrus.TextFormatter输出带颜色、换行、时间戳混排的文本,Promtail会把整条日志当做一个字段,trace_id、status_code等关键信息全丢。
- 必须用
zap.NewProduction()或logrus.JSONFormatter{},确保每行都是合法JSON - 禁用
logrus.SetFormatter(&logrus.TextFormatter{}),尤其在容器中会导致多行日志被截断 - 字段命名要统一:比如都用
request_id而非reqId或RequestId,否则Loki查{service="auth"} | logfmt request_id会漏数据 - 避免手动拼接:
logger.Info("user " + uid + " login")→ 改成logger.With("user_id", uid).Info("login")
Promtail配置里__path__必须匹配Kubernetes实际日志路径
很多人本地跑Docker Compose能通,一上K8s就查不到日志,根本原因是Promtail的__path__写成了/var/log/*.log,但Kubelet实际把容器stdout软链到了/var/log/pods/*/*/*.log。
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
- K8s环境下,用
__path__: /var/log/pods/*_{{.Values.app.name}}_*/*.log(Helm模板)或硬编码匹配命名空间+pod名 - 确认权限:Promtail DaemonSet需挂载
hostPath: /var/log,且容器内用户有读权限(常见坑:fsGroup: 1001没设,日志文件属主是root) - 加
pipeline_stages提取字段:比如用dockerstage自动解析container_name和pod_name,不用自己正则 - 标签别写死:
labels: {job: "auth-service", env: "prod"}→ 改用static_labels: {env: "$ENV"},通过环境变量注入
Loki配置里storage_config用filesystem时必须保证目录可写且有足够inode
loki-local-config.yaml里看着只是改个路径,但CentOS 7.9默认/tmp可能被systemd-tmpfiles清理,或者XFS文件系统inode耗尽导致Loki静默失败——日志照收,但查不到。
- 不要用
/tmp/loki,改用/data/loki并chown -R loki:loki /data/loki - 检查inode:运行
df -i /data,剩余Inodes低于10%就危险 - v2.5.0起
boltdb-shipper要求active_index_directory和cache_location必须分开,不能共用同一级目录 - 禁用所有
query_range缓存配置项,v2.5.0已移除该功能,留着会导致Loki启动报错unknown field "results_cache"
Grafana里查Loki日志时,|~和|=性能差异极大
写{job="auth-service"} |~ "timeout"看着方便,但Loki得扫描整个块解压后逐行匹配正则,10万行日志可能卡住;而{job="auth-service", level="error"}只查标签索引,毫秒返回。
- 优先用标签过滤:
level、service、request_id这些必须作为label打到日志流里,而不是藏在JSON body里 - JSON字段搜索用
| json | .error_code == "500"比|~ "error_code.*500"快一个数量级 - 避免
| line_format "{{.msg}}"后再|~,这等于放弃索引直奔全文扫 - 查慢请求加
| duration > 5s前,确认你的Go日志里真有"duration"字段且类型是number(不是string)
request_id打成label还是log line,决定了你能不能在Grafana里一键下钻到某次调用全链路。这点比选什么日志库更关键,也最容易被跳过。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










