ES的索引的建立过程通常是先建立索引,然后添加mapping关系,最后进行POST,PUT,GET等操作。那么filebeat以及logstash和ES交互的时候mapping关系是怎么定义呢?
先说说filebeat,之前提到过,这个东西是个采集器,虽然也有output功能,但是由于没有拆分字段的能力(module功能是否可以拆分还有待确定, 暂时没也打算去用这个功能),因此采集到的日志一行就是一个message, 实际上也就不存在字段mapping概念。但是问题是,偏偏filebeat需要mapping模板,这个地方我理解不了,我思考了很久,ES本身支持动态插入,根本无需mapping, filebeat把一行直接插入到ES即可,字段默认就用message, 额外的字段,比如@timestamp 插入也行,不插入也无所谓,想怎么处理filebeat定义好就行了。因为这个原因,我也不会打算去用filebeat的output功能。
logstash的mapping有3种方法,1. 动态插入,通过grok拆分字段,然后定义字段名称,插入的时候根据定义的字段名字插入到ES中,由于没有事先定义mapping, 因此字段类型由ES自己判断,很显然这个地方会有很大误差,比如IP地址有可能为TEXT, 或者int类型也为text。 2. 定义模板,并在output的时候通过template来指定模板。3. 自己建立索引,并设置 mapping关系,logstash配置中不使用任何模板。
我个人比较倾向使用第3种方法,因为定义模板的时间,我已经更新了索引的mapping, 又何必去用模板? 接下来演示一下2者区别,我先自己定义mapping关系来采集,然后再测试动态插入。
1. 自己定义mapping关系,为了便于区分,关注start_time字段即可,我这里定义为date,这样在做展示的时候,可以通过这个时间来排序
{
"mappings" : {
"doc" : {
"properties" : {
"@timestamp" : {
"type" : "date"
},
"@version" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"appid" : {
"type" : "long"
},
"beat" : {
"properties" : {
"hostname" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"name" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"version" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
},
"db" : {
"type" : "keyword"
},
"host" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"last_insert_id" : {
"type" : "long"
},
"lock_time" : {
"type" : "text"
},
"message" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"offset" : {
"type" : "long"
},
"prospector" : {
"properties" : {
"type" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
},
"query_time" : {
"type" : "text"
},
"rows_examined" : {
"type" : "long"
},
"rows_sent" : {
"type" : "long"
},
"server_id" : {
"type" : "long"
},
"source" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"sql_text" : {
"type" : "text"
},
"start_time" : {
"type" : "date",
"format" : "yyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
},
"tags" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"time" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"type" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"user_host" : {
"type" : "text"
}
}
}
}
}
通过plugin-head工具添加索引,再更新mapping.
然后开始启动filebeat, logstash 来采集数据。logstash配置如下:
input {
beats {
type => "slowlog"
port => "5044"
}
beats {
type => "nginx"
port => "5045"
}
}
filter {
if [type] == "slowlog" {
grok {
match => {
"message" => "\"(?<start_time>%{YEAR}[./-]%{MONTHNUM}[./-]%{MONTHDAY}[- ]%{TIME}).(?<time>.*)\",\"(?<user_host>.*)\",\"(?<query_time>.*)\",\"(?<lock_time>.*)\",%{INT:rows_sent},%{INT:rows_examined},\"(?<db>.*)\",%{INT:last_insert_id},%{INT:insert_id},%{INT:server_id},\"(?<sql_text>.*)\",%{INT:thread_id}"
}
}
mutate {
remove_field => "thread_id"
}
mutate {
remove_field => "insert_id"
}
date {
match => ["start_time", "YYYY-MM-DD HH:mm:ss"]
}
} else {
grok {
match => {
"message" => "\"(?<start_time>%{YEAR}[./-]%{MONTHNUM}[./-]%{MONTHDAY}[- ]%{TIME}).(?<time>.*)\",\"(?<user_host>.*)\",\"(?<query_time>.*)\",\"(?<lock_time>.*)\",%{INT:rows_sent},%{INT:rows_examined},\"(?<db>.*)\",%{INT:last_insert_id},%{INT:insert_id},%{INT:server_id},\"(?<sql_text>.*)\",%{INT:thread_id}"
}
}
}
}
output {
if [type] == "slowlog" {
elasticsearch {
hosts => ["10.215.4.166:9200", "10.215.4.167:9200"]
index => "slow_log"
}
} else {
elasticsearch {
hosts => ["10.215.4.166:9200", "10.215.4.167:9200"]
index => "slowlog"
}
}
}
数据已经进入:
2. 测试动态插入,我什么也不干,索引不建立,mapping 不设置,模板也不提供,看看动态插入结果如何。
上面动态插入的start_time是text,不属于date了,就连server_id这种明显是int类型的也定义为text了。关于模板方式就不测试了,我觉得模板毫无意义。