go服务应输出json格式日志(如用zerolog),filebeat设json.keys_under_root: true,logstash用codec => "json",es需预定义index template将trace_id设为keyword类型,并统一中间件与业务日志字段名及类型。

Go 服务怎么把日志发给 Logstash 或 Filebeat
直接写文本到 stdout 是最常见但最坑的做法——ELK 里搜不到字段、level 和 trace_id 全变成字符串里的一坨,根本没法过滤或聚合。必须让 Go 日志输出 JSON 格式,且每个字段是独立 key。
推荐用 zerolog 或 logrus 配合 JSON 输出器,别手写 json.Marshal。比如 zerolog.New(os.Stdout).With().Timestamp().Str("service", "order").Logger(),这样每条日志自动带 time、service、level 字段。
- Filebeat 要开启
json.keys_under_root: true,否则所有字段会包在json.前缀下,Kibana 里查error得写成json.error - Logstash 的
jsonfilter 必须配source => "message",且确保输入插件没把原始消息转义(比如codec => "json"比filter { json { } }更稳) - 如果用了 Docker,
stdout日志会被 Docker daemon 二次封装,导致时间戳错乱、换行截断;建议容器内直接发 HTTP 到 Logstash,或用filebeat -c /etc/filebeat.yml挂载日志文件目录
为什么 Kibana 里查不到 trace_id 字段
不是日志没打,是字段没被 Elasticsearch 当作可搜索字段索引。默认情况下,ES 对新出现的 JSON 字段走 dynamic mapping,但遇到空值、类型不一致(比如有时是 string,有时是 null),就会 fallback 成 text 类型,而 text 不支持精确匹配查询。
解决办法只有一个:提前定义 index template,强制 trace_id 为 keyword 类型。
立即学习“go语言免费学习笔记(深入)”;
- 在 Logstash output 或 Filebeat 中加
fields.trace_id: ${TRACE_ID},确保字段名统一(别一会儿traceId一会儿trace_id) - ES 里 PUT 一个 index template,匹配
logs-golang-*索引,其中"trace_id": {"type": "keyword"} - 已有索引不会自动重映射,得 reindex;新索引才生效
Logstash filter 写正则提取字段太慢?换结构化输出
用 grok 解析 Go 默认的 log 输出(如 INFO[0012] user login id=123 ip=10.0.1.5)在高并发下 CPU 占用飙升,延迟明显。这不是配置问题,是设计缺陷——文本解析本质就是 O(n) 且无法并行加速。
真正省事又高效的方式,是让 Go 直接输出结构化内容,绕过 grok。
- 禁用所有
grokfilter,改用dissect(仅限固定分隔符)或干脆删掉 filter,靠上游 JSON 输出 - 如果必须兼容旧日志格式,用
dissect { mapping => { "message" => "%{level}[%{ts}] %{msg} id=%{user_id} ip=%{ip}" } },比 grok 快 3–5 倍 - 注意
dissect不支持正则,空格/分隔符必须严格一致,否则整条丢弃
Golang 中间件里埋点日志怎么和 ES 查询对齐
中间件(比如 Gin 的 logger)打的日志,经常缺 request_id、status_code、duration_ms,或者字段名和业务日志不一致,导致在 Kibana 里没法跨日志关联请求链路。
关键不是“加字段”,而是“字段命名和类型对齐”。比如 duration_ms 在中间件里是 float64,在业务日志里是 int,ES 会建两个字段,聚合时出不来结果。
- 统一用
zerolog.Ctx(r.Context()).Fields(map[string]interface{}{"status_code": status, "duration_ms": duration.Seconds() * 1000}),确保数值类型一致 - 所有中间件和 handler 都从 context 取
trace_id和request_id,别自己生成;用ctx.Value()+ 自定义 key,避免字符串 magic constant - 在 Logstash 或 Filebeat 里加
drop_fields过滤掉无用字段(如ecs.version、host.name),减少 ES 存储压力和 mapping 膨胀
字段对不齐比日志漏发更难排查——它不会报错,只会让你在 Kibana 里点半天也找不到那条本该存在的请求。










