Logstash:获取业务时间并替换@timestamp

业务场景

使用logstashMysql迁移数据到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,千万不要在filterremove_field删除了。

  • 开始先要在logstash安装目录下,新建一个test.conf,logstash会根据你的配置去对数据修改过滤,达到你想要的下效果。

  • 新建配置文件,加入自定义配置,大体规则如input { stdin { } } filter{} output { stdout {} }

$ touch test.conf
$ vim test.conf
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
@timestamp时间跟控制台时区一致

启动成功后,还可以在控制台输入测试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时间,保证按原有数据时间顺序,进行数据分片

第一种方式:使用logstashjosn插件,解析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"}

@timestamp时间被createtime时间替换
重点注意

注意下入参时间格式,我的是UTC时间格式,因为这个2021-08-05T07:16:00.571Z时间,json插件解析会报错,因为它里面包含了特殊字符-TZ,必须用双引号成为字符串,而大多数数据库时间字段都设置的是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就是UTCgrok的正则表达式标识,也就是这种时间格式的数据: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;
image.png

====================
如果

如果确实解决你的实际问题,请给赞起来!!!!!!!!!!!!!

你可能感兴趣的:(Logstash:获取业务时间并替换@timestamp)