logstash使用经验

1,167 阅读5分钟

相关资料

  • Logstash最佳实践,内容简练,如果解决不了问题,再移步官方文档
  • 官方文档中有原理讲解,以及重要配置。最重要的三个章节为inputfilteroutput。很多时候都需要从这三个章节中学习。

使用步骤

  • 目前每个logstash数据流都是一个单独的目录。意思是当有新的logstash作业时,重新解压出一份或者拷贝一份出来,再编写配置文件。
  • 每个logstash作业核心内容是配置文件的编写。 这个配置文件我一般都直接放在logstash目录中。
  • 启动参数:
    • bin/logstash -f first-pipeline.conf --config.test_and_exit 能验证配置文件写的是否正确,但是也挺费时间的。因为logstash启动就是慢
    • bin/logstash -f first-pipeline.conf --config.reload.automatic 配置文件变化,能够感知并重新reload【和input的类型有关系,比如kafka就支持配置文件重载,读文件就不行】

配置相关logstash.yml

  • logstash写入时,es接受请求的线程池触发拒绝策略,从es日志中可以观察到。这种情况不是去调线程池队列大小,而是去优化logstash写入策略!解决方案:调整其吞吐策略。配置如下:
# 吞吐量相关
pipeline.batch.size: 5000
pipeline.batch.delay: 500
  • logstash官方文档对其能够配置的选项有详细的说明,建议仔细研读。
  • 高版本logstash和es通信是http,理论上升级es可以不升级logstash
  • logstash是用来解析处理消费的数据的,其中默认不支持转义,需要自行在配置中进行开启。深坑!配置如下:
# 支持转义
config.support_escapes: true

input

这里记录一下我用到过的input。

kafka

logstash-input-kafka官方文档

# 这就是最简单的一个例子
input{
    kafka{
        # 默认值就是latest
        auto_offset_reset => "latest"
        # 可以显示的指定消费者组的名字
        group_id => "xxxx"
        bootstrap_servers => "172.20.13.181:9092,172.20.13.182:9092,172.20.13.182:9092"
        # 可以消费多个topic,
        topics => ["test"]
    }
}

熟悉kafka消费模式的话,是可以消费一个正则表达式的,例如topic-*,那么topic-1,topic-2...这些topic都会被消费。配置项为topics_pattern,如果配置了这个那么上面配置中的topics就失效。详细信息见官方文档。

还有一个场景,就是kafka消息的元数据我想拿出来怎么办,比如我想知道每条消息的offset、key这种东西。关于如何获取kafka消息的元数据。如果要拿到元数据有一个配置项decorate_events需要设置成true。下面的demo为获取元数据的配置:

input{
    kafka{
        bootstrap_servers => "172.20.13.181:9092,172.20.13.182:9092,172.20.13.182:9092"
        topics => ["test"]
        # 这个要配置成true
        decorate_events => true
    }
}
# 这些元数据需要在filter中我们通过mutute显示的加上
filter{
    mutate{
        # 这里我们的需求是拿到key
        add_field => {'key' => '%{[@metadata][kafka][key]}'}
    }
}

consumer_threads这个配置项设置消费的线程数,每个线程是一个消费者。一般消费者数量等于kafka-topic分区数。

jdbc

logstash-input-jdbc官方文档

官方公众号,logstash同步db到es这个写的比较详细了。

根据我的经验,查询有两个场景,一个是每次都全量查询,这种查询没有状态可言,配置项比较少。

input {
  jdbc {
    jdbc_default_timezone =>"Asia/Shanghai"
    jdbc_connection_string => "jdbc:oracle:thin:@//172.20.13.188:1521/xxxx"
    jdbc_user => "xxxuser"
    jdbc_password => "xxxpass"
    # 得准备好对应的jar包。
    jdbc_driver_library => "/god/data/elk/lib/ojdbc6-12.1.0.2.jar"
    # jdbc对应厂商的驱动类
    jdbc_driver_class => "Java::oracle.jdbc.driver.OracleDriver"
    # cron表达式,每分钟执行一次查询
    schedule => "* * * * *"
    # 每次全量扫描表
    statement => "select * from t_user where rownum < 5000"
  }
}

另外一种是增量查询,这种时候就需要保存状态信息了。也就是上一次查询截止到哪里,这次得从上次结束的地方继续查。结合上面提到的官方公众号文章我列一下重要的配置项:

  • record_last_run => true是否记录上次执行的结果,这个默认是true
  • use_column_value tracking_column tracking_column_type这三个是要一起用的。例如我db表中有一个字段是snap_time,我需要以这个字段来增量同步,那么这三个字段一定要配置好。
  • last_run_metadata_path => "mysql/last_id.txt"状态存在哪里

这个是官方的一个demo,追踪列是id。看他的statementsql语句中有一个:sql_last_value这实际上就是维护的那个状态值。

input {
  jdbc {
    statement => "SELECT id, mycolumn1, mycolumn2 FROM my_table WHERE id > :sql_last_value"
    use_column_value => true
    tracking_column => "id"
    # ... other configuration bits
  }
}

filter

时间添加时区以及类型的转换

dissect

将原始的消息按\t解析,并给每个字段起好了名字。

dissect {
    mapping => {
        "message" => "%{time}\t%{id}\t%{terminalNo}\t%{logId}\t%{IP}\t%{errCode}\t%{resp_time}\t%{attr}\t%{sequence}\t%{tid}\t%{checksum}\t%{version}"
    }
}

ruby

ruby这块建议单独写成独立的脚本文件

ruby {
    path => "./ruby-script/makeDate.rb"
}

grok

这个场景是从terminalNo中截取出前两个数字作为province

grok {
    match => {
        "terminalNo" => "(?<province>\d{2})"
    }
}

translate

码值转换,场景为将省份编码翻译成中文

translate {
    field => "province"
    destination => "prov_zh"
    dictionary => {
        "11"=>"北京"
        "12"=>"天津"
        "13"=>"河北"
        "14"=>"山西"
        "15"=>"内蒙古"
        "21"=>"辽宁"
        "22"=>"吉林"
        "23"=>"黑龙江"
        "31"=>"上海"
        "32"=>"江苏"
        "33"=>"浙江"
        "34"=>"安徽"
        "35"=>"福建"
        "36"=>"江西"
        "37"=>"山东"
        "41"=>"河南"
        "42"=>"湖北"
        "43"=>"湖南"
        "44"=>"广东"
        "45"=>"广西"
        "46"=>"海南"
        "51"=>"四川"
        "52"=>"贵州"
        "53"=>"云南"
        "54"=>"西藏"
        "50"=>"重庆"
        "61"=>"陕西"
        "62"=>"甘肃"
        "63"=>"青海"
        "64"=>"宁夏"
        "65"=>"新疆"
    }
    fallback => "prov not match"
}

mutate

可以添加删除字段,例如:

mutate{
    add_field => {'snap_time' => '%{index_date} %{time}+0800'}
    add_field => {'key' => '%{[@metadata][kafka][key]}'}
    remove_field => ['@timestamp','time','message']
}

output

通常output端就是ES。对于索引,有的数据流是每天产生一个新索引,有的则是每个月,有的则是每年。根据数据量来定。

output{
    elasticsearch{
        hosts => ["http://node1:9200"]
        # 这里算是一个重点,%{index_date}这是filter中的字段,这里的场景是每天一个索引
        index => "caslog-%{index_date}"
        # 由于es配置了认证,所以需要相应权限的用户名和密码
        user => "xxoo"
        password => "123456"
    }
}