业务场景
使用logstash
从Mysql
迁移数据到ES
,并根据Mysql
的业务创建时间,分批建立索引。
@timestam时区矫正
先做一下说明,如果我们仅仅只是需要按时间产生索引,并不关注Mysql
中的某个业务字段的创建时间,我们只要对@timestamp
调整8小时时区矫正就可以了,@timestamp
会默认创建当前时间。在outputs/elasticsearch
中这样设置的索引名+%{+YYYY.MM.dd}
既可,logstash
会获取@timestamp
时间建索引:
output部分设置如下:
output {
elasticsearch {
hosts => ["10.100.4.10:9200","10.100.4.10:9201"]
# 同步的索引名必须要有@timestamp ,如果你在fillter中 remove了 yyyyMM不起效
#ps_sign_log是索引前缀,yyyyMM是根据@timestamp得到的时间
index => "ps_sign_log%{+yyyyMM}"
#我选取的是mysql中,主键自增的id
document_id => "%{id}"
#自定义模板索引
template_name => "ps_seal_log"
template => "/usr/local/logstash-7.12.0/my/ps_seal_log_template.json"
template_overwrite => true
}
stdout {
codec => rubydebug
}
}
特别强调:ps_sign_log%{+yyyyMM}
这种写法,时间来源于@timestamp
,千万不要在filter
中remove_field
删除了。
开始先要在
logstash
安装目录下,新建一个test.conf
,logstash
会根据你的配置去对数据修改过滤,达到你想要的下效果。新建配置文件,加入自定义配置,大体规则如
input { stdin { } } filter{} output { stdout {} }
$ touch test.conf
$ vim test.conf
- 也可以使用这种方式,直接把设置写在启动运行命令中:
$ bin/logstash -e 'input { stdin { } } filter{} output { stdout {} }'
ok,基本的工作解释清楚了,下面说一下怎么把默认属性@timestamp
调整为东八区时间。
思路是新建的n_logstashStamp
临时字段,然后获取本地时间并给它赋值(注意一下logstash
不允许自建的特殊字段如带@
标识的字段),然后在把n_logstashStamp
赋值给@timestamp
,最后删除n_logstashStamp
临时字段,配置如下:
input {
stdin {
}
}
filter{
#把东八区时间赋值给新建的n_logstashStamp 字段,logstash不允许自建的特殊字段如带@标识的字段
ruby{
code => "event.set('n_logstashStamp', (event.get('@timestamp').time.localtime + 8*60*60).strftime('%Y-%m-%d %H:%M:%S'))"
}
date {
match => [ "n_logstashStamp", "yyyy-MM-dd HH:mm:ss" ]
target => "@timestamp"
}
mutate {
#将不需要的JSON字段过滤,且不会被存入 ES 中,@version 无用字段可删除
remove_field => ["n_logstashStamp", "@version"]
}
}
output{
#控制台输出
stdout {
}
}
input
输入部分,因为是测试,所以就先写成控制台输入了:
stdin {
}
output
这样设置,可以打印读取的json
数据:
stdout {
codec => json
}
- 启动
logstash
,使用test.conf
配置,直接回车,就可以看到如下结果,@timestamp
时间与n_logstashStamp
一致,赋值成功:
./bin/logstash -f ./my/test.conf
启动成功后,还可以在控制台输入测试json的数据:
{"createtime" :"2019-08-05T07:16:00.571Z","iphost" :"10.100.4.100:2891","nihao":2021-08-05T07:16:00.571Z}
说明:message
就是我们输入的内容。当我们在Mysql
同步数据的时候,message
就是从Mysql
查询的数据,可以从这个字段获取到你想要的内容,如某条数据的创建时间,下面就会用到它。
使用Mysql
时间字段替换@timestamp时间,保证按原有数据时间顺序,进行数据分片
第一种方式:使用logstash
的josn
插件,解析message
中的json数据,然后获取入参数据时间字段赋值给@timestamp
,配置如下:
input {
stdin {
}
}
filter{
#将从message字段解析JSON,然后就可以直接从json数据的key获取到指定的value
json {
source => "message"
}
date {
match => [ "createtime", "ISO8601"]
target => "@timestamp"
}
}
output {
stdout {
}
}
启动修改后的配置
./bin/logstash -f ./my/test.conf
启动成功后输入测试json数据:
{"createtime" :"2019-08-05T07:16:00.571Z","iphost" :"10.100.4.100:2891"}
重点注意
注意下入参时间格式,我的是UTC
时间格式,因为这个2021-08-05T07:16:00.571Z
时间,json
插件解析会报错,因为它里面包含了特殊字符-
、T
、Z
,必须用双引号成为字符串,而大多数数据库时间字段都设置的是Date
类型的UTC
时间,所以从数据库读取的时间绝对是不带双引号的,这样就会报错。所以最好使用date_format( psl. createtime, '%Y-%m-%d %H:%i:%S' ) AS createTime
来对时间格式化,如:
select id,
seal_id,
user_id,
business_type AS businessType,
date_format(
psl. createtime,
'%Y-%m-%d %H:%i:%S'
) AS createTime,
psl.action_time AS actionTimeStamp
from ps_seal_log psl
where id > :sql_last_value;
来个错误图:
第二种方式: 使用grok
是正则捕获过滤器。
使用grok
捕获message
中的数据,符合设定的正则表达式数据,会赋给新建立的临时字段,然后在赋值给@timestamp
。
input {
stdin {
}
}
filter {
grok {
#捕获mesaage内容中,符合TIMESTAMP_ISO8601时间格式的字段并赋值给tmpTime
match = > ["message", "%{TIMESTAMP_ISO8601:tmpTime}"]
}
date {
#对tmpTime格式化,然后赋值给@timestamp
match = > ["tmpTime", "ISO8601"]
target = > "@timestamp"
}
}
output {
stdout {
}
}
启动修改后的配置
./bin/logstash -f ./my/test.conf
启动成功后输入测试json数据:
{"createtime" :"2019-08-05T07:16:00.571Z","iphost" :"10.100.4.100:2891","nihao":2021-08-05T07:16:00.571Z}
说明:
TIMESTAMP_ISO8601
就是UTC
在grok
的正则表达式标识,也就是这种时间格式的数据:2021-08-05T07:16:00.571Z
。
一定要确定自己输入的数据是什么类型的,是什么格式的。不然就会报错: grok
的正则表达式解析失败
grokparsefailure
奉上一个很全的grok的正则表达式,这里面还有其他的正则表达式,比如对IP、邮箱的。
第三种方式:最终解决方案
本文是logstash 7.12
版本,但是使用上面两种方式都没法解决我的问题,一度以为我的时间是UTC
格式的timestamp
,但是一直解析失败,怀疑是版本的问题。但是最后在elastic
论坛找到答案。
日期过滤器无法解析时间戳 #95
https://github.com/logstash-plugins/logstash-filter-date/issues/95
https://discuss.elastic.co/t/trouble-matching-timestamp/83768/3
身心疲惫,附上最终解决方案:添加一个临时字段,然后actionTime
给其赋值(actionTime
是我从Mysql查出来的字段),最后格式,完成对@timestamp
的赋值,配置如下:
#过滤、格式化数据
filter{
#使用mutate插架,增加一个字段域,值从`message`中的字段获取
mutate {
add_field => {"temp_ts" => "%{actionTime}"}
}
#对actionTime格式化,然后赋值给@timestam
date {
match => ["temp_ts", "ISO8601"]
target => "@timestamp"
}
mutate {
remove_field => ["@version","temp_ts"]
}
}
注意哈,actionTime
在我数据库,设置的是timestamp
类型,没有做任何格式化
select id,
seal_id,
user_id,
business_type AS businessType,
psl.action_time AS actionTime
from ps_seal_log psl
where id > :sql_last_value;
====================
如果
如果确实解决你的实际问题,请给赞起来!!!!!!!!!!!!!