问题背景
问题描述
Osprey 线上ES的logger出现问题如下:
Fluentd 介绍
Fluentd 是一款ES插件,负责将msg中的字段解析出来。 例如输入:
{"msg":"新闻搜索消耗时间", "cost":0.0739479}
将会被解析成下图存放进ES里, 从而便于后续对记录日志的筛选、分析。
问题追踪
从状态码400来看,很明显是推送的格式错误,但因为没有给出具体错误的原因,所以仍然无法得知是哪里格式不正确。
由于之前在policy-brain项目中也是用了ES日志推送的服务,当时也发生了相同的问题,如下:
- 将日期时间格式改为
ISO8601 - 将
level规定为大写
改动完毕后,Policy-brain的日志信息不再出现dump error的问题,因此对问题产生的原因进行猜测:
- 是否是接收端限定了日志等级的格式?
- 是否是接收端限定了时间的格式?
- 是否是接收端限定了能够接收的字段?
- 根据官方github项目中有人提出的类似 issue,是否因为部分ES服务器结点没有更新新的6.x 的template,导致部分日志出现格式错误?
相关问题检索
Fluent-plugin-elasticsearch 插件官方文档
glich 没猜错的话应该是作者拼错了,应该是名词glitch 故障,看上去像是因为元素类型导致的错误,下面的 solution1 给出了 动态类型的配置方法。solution2 给出了将输入值transform的方法。
Simon blog
确定问题
综上所述,问题的原因就是字段的类型不正确。 通过检查宁夏线上集群kibana的mapping:
{
"fluentd-2019.12.16": {
"mappings": {
"properties": {
"@timestamp": {
"type": "date"
},
"MESSAGE": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"caller": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"clientIP": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"code": {
"type": "long"
},
"comment": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"cost": {
"type": "float"
},
"current remaining": {
"type": "long"
},
"docker": {
"type": "object"
},
"duration": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"errcode": {
"type": "long"
},
"errmsg": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"error": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"field": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"from": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"ip": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"json": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"kubernetes": {
"properties": {
"container_image": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"container_name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"host": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"namespace_name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"pod_name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"level": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"logger": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"message": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"msg": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"namespace": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"query": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"reqsize": {
"type": "long"
},
"size": {
"type": "long"
},
"source": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"status": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"statusCode": {
"type": "long"
},
"stream": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"target": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"total": {
"type": "long"
},
"ts": {
"type": "date"
},
"version": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
可以发现ts这个字段的类型是 date类型, 而报错信息中的ts是个时间戳,为float类型,从而导致了类型不一致,进而导致错误。
解决方法
将时间戳改为与Readygo相同的时间戳记录方式即可:
func GetLogger() (*zap.Logger, error) {
cfg := zap.NewProductionConfig()
// 标准输出,一般只需要配置这个即可
cfg.OutputPaths = []string{
"stdout",
}
// level只是一个简单的过滤, 只有level >= cfg.level 的日志才会被继续输出
cfg.Level = zap.NewAtomicLevelAt(zap.InfoLevel)
// 输出为 JSON 格式
cfg.Encoding = "json"
// 使用默认生产环境日志编码设置
cfg.EncoderConfig = zap.NewProductionEncoderConfig()
cfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
cfg.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
return cfg.Build()
}
总结
400 dump error 的错误原因在于前后输入的值类型不一致,解决方法是采用一套相同标准来规定一些通用字段。建议全部采用Readygo采取的标准,即将时间格式为ISO8601.。
参考文献
- Elasticsearch rejects data #493, github.com/uken/fluent…
- fluent-plugin-elasticsearch issue, rubydoc.info/gems/fluent…Rejected_by_Elasticsearch_is_occured__why
- Elasticsearch 400 Error When Upgrading Fluent Winston Log Message Format, ramsay.xyz/2018/10/13/…
- fluent-plugin-elasticsearch usage , github.com/uken/fluent…
- elasticsearch 索引出现值冲突, aliasmee.github.io/post/resolv…