go程序需用logrus/zerolog输出json日志,logstash用tcp/beats输入并配置json+date过滤器对齐时间戳,直连es须复用client实例且用docker服务名通信。

Go 程序怎么把日志发到 Logstash 或直接写入 Elasticsearch
Go 本身不内置日志转发能力,log 包只支持输出到 os.Stdout、文件或自定义 io.Writer。想进 ELK,得自己搭桥——要么用 Logstash 做中间接收(推荐初试),要么用客户端库直连 ES(适合结构化日志多、吞吐要求高的场景)。
常见错误是直接把 log.Printf 的输出重定向到文件,再指望 Filebeat 自动解析 JSON;结果日志没结构化,Filebeat 拿到的全是纯文本,Kibana 里字段全丢,@timestamp 也没法对齐。
- 用
logrus或zerolog替代原生log,强制输出 JSON 格式(字段名别用中文,ES 不友好) - Logstash 推荐用
tcp或beatsinput 插件接收,别用fileinput 读 Go 的日志文件——时序错乱、权限、轮转都会出问题 - 直连 ES 用
elastic/go-elasticsearch客户端时,别在每个 HTTP 请求里新建*elasticsearch.Client,复用实例,否则 fd 耗尽报too many open files
Logstash 配置里最常写错的 filter 和 output 设置
Logstash 的 filter 不是万能解析器,尤其面对 Go 输出的嵌套 JSON 日志,json 过滤器默认只解一层,深层字段如 req.user.id 会变成字符串字面量,不是真正嵌套对象。
错误现象:Kibana 里看到 message 字段里塞着一整段 JSON 字符串,而不是展开的字段树。
立即学习“go语言免费学习笔记(深入)”;
- 确保
json过滤器加source => "message",且 Go 日志输出前已把整个日志对象序列化为单行 JSON(zerolog.NewConsoleWriter()默认带换行,要关掉) -
output { elasticsearch { hosts => ["http://es:9200"] } }必须显式指定hosts,不能只写host(旧版参数,v8+ 已废弃,会静默失败) - 如果用了
if [type] == "go-app"做条件路由,注意 Logstash v7.0+ 默认禁用type字段,得在 input 里手动加type => "go-app"
Go 日志时间戳和 ES @timestamp 对不上怎么办
Go 默认用 time.Now().UTC() 打时间戳,但 Logstash 默认用它自己收到事件的时间设 @timestamp,两者差几毫秒到几秒都算正常;真要对齐,必须让 Go 日志自带精确时间,并让 Logstash 用那个字段覆盖 @timestamp。
典型表现:Kibana 折线图里请求延迟尖刺总比监控指标晚一秒出现,排查半天发现是时间源不一致。
- Go 里用
zerolog.TimeFieldFormat = time.RFC3339Nano,确保时间格式兼容 ES(别用UnixMilli()直接打数字,Logstashdate过滤器不认) - Logstash filter 里加:
date { match => ["time", "ISO8601"] target => "@timestamp" },其中time是 Go 日志里你写的字段名(比如zerolog.TimestampFieldName = "time") - ES 索引模板里别设
"@timestamp": {"type": "date_nanos"}——ES v8.0+ 不支持该类型,会拒绝写入,用"date"就够,纳秒精度 Logstash 会自动截断
Docker Compose 里 ELK + Go 服务网络连不通的几个硬坑
本地跑通不代表上线没问题,Docker 网络里最常卡在三处:Logstash 听不到 Go 容器的 IP、ES 的 discovery.type=single-node 没生效、Go 客户端连 ES 时用了 localhost。
错误信息示例:Failed to connect to http://localhost:9200: dial tcp 127.0.0.1:9200: connect: connection refused —— 这是因为 Go 容器里的 localhost 指向自己,不是宿主机。
- Go 代码里所有
http://localhost:9200或localhost:5044全部换成 Docker 内网服务名,比如http://elasticsearch:9200、logstash:5044 - Logstash 的
input { beats { port => 5044 } }默认只监听127.0.0.1,必须加host => "0.0.0.0"才能收其他容器来的流量 - ES 的
docker-compose.yml里除了environment: - discovery.type=single-node,还得加- xpack.security.enabled=false(否则 Go 客户端连不上,报401 Unauthorized)
时间戳对齐、网络地址、JSON 层级——这三个点没调对,ELK 看起来在跑,实际查不到日志,或者字段全是空的。调的时候别信“应该可以”,每个环节都抓包或打日志验证一下。










