解决elasticsearch索引里面的值的类型冲突,导致新提交的值与预期的key 类型不一致.引发了日志会丢失的情况。

问题

最近在使用kibana时,发现管理索引那里,某个索引下面有字段冲突的提示,然后去查看fluentd客户端的运行情况,发现在fluent程序自己的日志里,偶尔会出现几条错误信息:’ElasticsearchError error=“400 - Rejected by Elasticsearch”’ , 这是怎么回事呢?下面是错误的日志:

2018-08-13 18:02:55 +0800 [warn]: #0 dump an error event: error_class=Fluent::Plugin::ElasticsearchErrorHandler::ElasticsearchError error="400 - Rejected by Elasticsearch" location=nil tag="athena" time=2018-08-13 18:01:51.000000000 +0800 record={"method"=>"GET", "path"=>"/", "format"=>"html", "controller"=>"HomeController", "action"=>"home", "status"=>302, "duration"=>26.69, "view"=>0.0, "db"=>10.06, "location"=>"https://x.x.com/x/xxoo", "params"=>"{}", "referrer"=>"https://x.o.com/x/ooooo/166888", "user_id"=>101010101010, "session_id"=>"199000000000000"}  

分析

在kibana 那里,对提示conflict 的location字段进行编辑,发现页面最下方有些重要提示,如图1所示:

kibana

kibana 冲突提示

从上面提示可以看出,location 字段在不同索引里的类型发生了变化,之前是text 类型,后来变成了geo_point. 为了确认这点,我们可以通过es客户端,或者直接用curl,对es的mapping进行查看。

验证

我这里使用curl方式,先查看为text类型的索引。

curl -X GET "localhost:9200/logs-app-athena-2018.08.10/_mapping/" -H 'Content-Type: application/json' | jq '.'

这是location字段的定义:

"location": {
  "type": "text",
  "fields": {
    "keyword": {
      "type": "keyword",
      "ignore_above": 256
    }
  }
},

然后再去看下提示location 为geo_point 类型的索引看下。还是用上面的命令,只是改下索引的名字。得到的结果如预期一样。

"location": {
  "type": "geo_point"
},

但为什么从12号开始的索引里,这个值的类型就变成geo_ip了呢?仔细想了想,我11号好像做了一点操作。就是在logs-app-nginx* 索引模版里都添加了一个_default_,但为什么这个会影响到logs-app-athena 的索引呢?这个问题一直没想明白。这个是我当时做的操作:

curl -X GET "localhost:9200/_template/logs-app-nginx-* -d '{"order":0,"template":"*","settings":{},"mappings":{"_default_":{"properties":{"location":{"type":"geo_point"}}}}}'

解决方案

curl -X PUT "localhost:9200/logs-app-athena-*/_mappings/"  -H 'Content-Type: application/json' -d'
{
  "_default_": {
    "properties": {
      "location": {
        "type": "text"
      }
    }
  }
}

Note: 在elasticsearch 6.0以后默认不在使用_default_ mappings.default mapping

总结

这次问题的出现,对es又多了一点点了解,还要多看看官方文档发布的更新日志,可能某些功能在相应的版本里就不推荐使用了。

另外做这次改动的根本原因是nginx日志都在logs-app-nginx-* 索引里,而location 字段没有被自动解析成geo_point类型。后来不得已,换成了logstash-* 为前缀的索引后,类型才自动变成了geo_point。不知道还有没有其它方法可以实现。

参考

change lat lon from float to geo_point

geo point for all indexs template-mappings

logstash lesson elasticsearch mapping