比较

flume文件一般用于将本地日志文件上传到kafka中

flume的原理:

flume的使用:

logstash用于对接日志文件(nginx、windows、java,MySQL等等)、Redis和kafka等等,将数据存储到es中

dataview支持本地文件到es,但优先推荐上面两种方式;支持数据库数据迁移到es中,但性能不是很好。不推荐使用

 

flume官网:http://flume.apache.org/

flume对接HTTPsink:

flume1.8支持的sink类型:

 

logstash

组成

有input、filter和output三个部分组成,所以其脚本也是有这三个部分组成

input {}
filter {}
output {}

其中必须要有input和output两个,如果不指定,就默认使用的是stdin和stdout
最简单的配置语句,输入输出到控制台
input {stdin{}}
output {stdout{}}

常用命令

查看插件列表

logstash-plugin list

启动logstash

.\logstash -f logstash-csv-stdout.conf --config.reload.automatic
.\logstash -f logstash-csv-stdout.conf --config.reload.automatic --config.reload.interval 3.\logstash -f D:\logstash\config\logstash-txt-json-es.conf --config.reload.automatic --path.data=.\data
.\logstash -f D:\logstash\config\logstash-kafka-json-es.conf --config.reload.automatic --path.data=.\data

默认加载logstash脚本统计目录的conf文件,也可以指定conf文件的完整路径

--config.reload.automatic:conf文件修改时可以自动重新加载,类似nginx的reload,都是先起个新的进程或线程,没有问题时再把旧的关掉

--config.reload.interval :重新加载conf文件检测的时间间隔,默认是3s

--path.data:进程文件锁,logstash默认只能单实例启动,因为使用的同一个目录的文件锁,如果给每个实例都指定独立的文件路径,那么就可以多实例运行。

自动加载配置的处理逻辑:


事件与消息

数据在线程之间以事件的形式流传。不要叫行,因为 logstash 可以处理多行事件。
事件就是一个 Ruby 对象,或者更简单的理解为就是一个哈希也行。可以随意给事件添加字段或者从事件里删除字段。
每个 logstash 过滤插件,都会有四个方法叫 add_tag , remove_tag ,add_field 和 remove_field 。它们在插件过滤匹配成功时生效。

事件中的默认字段

1. host 标记事件发生在哪里。
2. type 标记事件的唯一类型。
3. tags 标记事件的某方面属性。这是一个数组,一个事件可以有多个标签。
4. @timestamp,用来标记事件的发生时间。因为这个字段涉及到 Logstash的内部流转,所以必须是一个joda对象,如果你尝试自己给一个字符串字段重命名为 @timestamp 的话,Logstash会直接报错。所以,请使用 filters/date 插件 来管理这个特殊字段。使用这个插件可以将其他事件字段的值替换掉@timestamp的默认值,因为这个值取的是logstash的服务端时间,有时并不满足需求,需要替换成真实日志的时间,并且这个字段用的是UTC的时间,如果是东八区,那么时间就会差8个小时,这个时候如果用它来动态创建日期索引,就会出现问题,所以这个字段的值一般都是要替换掉的,当然也不要通过ruby代码将UTC的时间认为的加8个小时,这是不对的。

UTC时间介绍:


加8小时的错误方法和解决方法


东八区是能够根据正确时间字段动态创建索引的方法时,使用日志里面时间字段的时间,新建一个时间字段,该字段就用来存储index的动态日期名字,比如说:

filter {
  date {
    match => ["LogDateTime","yyyy-MM-dd HH:mm:ss.SSS"]    #message在实际应用中修改为自己的字段
    target => "index_day"
  }

  ruby {
    code => '
        #这个不行,还是0时区的
#        event.set("@timestamp",event.get("@timestamp").getlocal)
        #下面的方案虽然暂时可以得到形式上的东8区的时间,但其实是0时区的,真实时间多了8小时,后面ES或者kibana使用该字段时都会有问题,但是如果不使用也没有问题,这样可以解决创建索引的日期对不上的问题
#        event.set("timestamp", event.get("@timestamp").time.localtime + 8*60*60)
#        event.set("@timestamp",event.get("timestamp"))
        event.set("index_day", event.get("index_day").time.localtime.strftime("%Y%m%d"))
    '
#    remove_field => ["timestamp"]
  }
  mutate {
    remove_field => ["@version", "message", "path", "host", "geoip"]
  }
}

output {
  elasticsearch {
     index => "indexpreifx-%{index_day}" #使用index_day字段里面的时间
     index => "indexpreifx-%{+YYYY.MM.dd}" #使用@timastamp里面的时间
  }
  stdout {
    codec => json_lines
  }

5. @version

事件的作用

合并多行数据(Multiline)
有些时候,应用程序调试日志会包含非常丰富的内容,为一个事件打印出很多行内
容。这种日志通常都很难通过命令行解析的方式做分析。

 

编码插件codec

这个插件不仅可以在filter里使用,在input和output里也都可以使用

codec是coder和decoder的缩写,logstash的整个处理过程就变成了input、decoder、filter、encoder和output这几个过程。
codec就是用来处理decoder和encode过程的。
codec的引入是的输入的日志格式不只是文本的格式,可以通过codec指定任意的日志格式,举例如下

将消息自动转化为json格式

codec => json
codec => json_lines

指定编码格式

codec => json {charset=>"GBK"}

为一个事件输出多行日志,常用于堆栈信息的整理

codec => multiline {
    pattern => "^\["
    negate => true
    what => "previous"
}

字段引用

字段是 Logstash::Event 对象的属性。我们之前提过事件就像一个哈希一样,如果你想在 Logstash 配置中使用字段的值,只需要把字段的名字写在中括号 []里就行了

Logstash 还支持变量内插,在字符串里使用字段引用的方法是这样:
"the longitude is %{[geoip][location][0]}"
如果是单层变量,不用[],直接使用%{变量名}也是可以的

输入类型

基本上碰到的接口协议都支持,包括tcp、http、jmx等等
控制台输入

stdin {
    codec => json {charset=>"GBK"}
  }

本地文件

file {
    path => ["D:\logstash\data\json.txt"]  
    start_position => "beginning"
    sincedb_path => "D:\logstash\data\sincedb_path\txt-json.txt"
    codec => "json"
  }

log4j日志框架的日志

TCP数据流

kafka数据流

kafka {
    bootstrap_servers => "xx:9093,xx:9093"
    client_id => "xxx"
    group_id => "xxx"
    auto_offset_reset => "latest"
    consumer_threads => 1
    decorate_events => true
    topics => ["xxxx"]
    type => "bhy"
    codec => json  
    security_protocol => "SASL_PLAINTEXT"
    sasl_mechanism => "PLAIN"
    sasl_jaas_config => "org.apache.kafka.common.security.plain.PlainLoginModule required username='xxx' password='xxxx';"
  }

filter插件

条件判断

通过条件判断可以处理满足指定条件的消息,不满足条件的就会被提前丢弃

if [Key] != 10007 {
  drop{}
}

日期处理

date {
    match => ["LogDateTime","yyyy-MM-dd HH:mm:ss.SSS"]
    target => "index_day"
  }

date 插件可以用来转换你的日志记录中的时间字符串,变成 LogStash::Timestamp 对象,然后转存到 @timestamp 字段里。

注意:因为在稍后的outputs/elasticsearch中常用的%{+YYYY.MM.dd}这种写法必须读取@timestamp数据,所以一定不要直接删掉这个字段保留自己的字段,而是应该用 filters/date 转换后删除自己的字段!因为使用UTC的时区,所以使用这个字段在东8区创建索引时有问题,因此应该用上面@timestamp处的方案介绍。

grok正则捕获

性能略差,谨慎使用

grok{
    match=>{
        "message"=>"\s+(?<request_time>\d+(?:\.\d+)?)\s+"
    }
}

从中可以解析出新的request_time字段
表达式语法,默认预定义一些正则表达式变量,也可以自定义表达式变量,放到指定路径的文件,可以直接使用这些变量解析出新的字段,并且也可以指定新的字段的数据类型,就可以避免在使用mutate的convert再对字段类型进行转换了。
grok 表达式的打印赋值格式的完整语法是下面这样的:

%{PATTERN_NAME:capture_name:data_type}

:data_type 目前只支持两个值: int 和 float 。
所以我们可以改进我们的配置成下面这样:

filter{
    grok{
        match=>{
            "message"=>"%{WORD} %{NUMBER:request_time:float} %{WORD}"
        }
    }
}

使用存储到配置文件的表达式格式如下:

filter{
    grok{
        patterns_dir=>["/path/to/your/own/patterns"]match=>{
            "message"=>"%{SYSLOGBASE} %{DATA:message}"
        }overwrite=>["message"]
    }
}

匹配多个正则表达式
因为日志格式可能不止一种,需要灵活配置多个,其实可以设置成列表的格式,如下:

grok{
    match=>{
        "message"=>["%{WORD} %{NUMBER:request_time:float} %{WORD}",
        "%{NUMBER:request_time:int} %{WORD}"]
    }
}

dissect

替代grok的性能较好,使用较简单的插件

GeoIP

地址信息查询插件,查询IP对应的省市和经纬度,对于地图的可视化展示。当然只能查询到公网的IP,内部网的IP就不支持了。

mutate

插件是 Logstash 另一个重要插件。它提供了丰富的基础类型数据处理能力。包括类型转换,字符串处理和字段处理等。

convert:类型转换

mutate {
    convert => {
      "id" => "integer"
      "age" => "integer"
      "name" => "string"
    }
  }

支持转换的类型包括:interger,float,boolean和string。注意不支持long和date类型。date转换是需要使用filter里的date插件进行操作。如果使用了不支持的数据类型,就会出现奇怪的错误如下:

LogStash::ConfigurationError: translation missing: en.logstash.agent.configuration.invalid_plugin_register。

字符串处理

常用的操作基本都有。

正则替换

灵活运用,可以实现增删改查、截取的各种效果

mutate {
       gsub => ["urlparams", "[\\?#]", "_"]
       gsub => ["urlparams", "A", "a"]
  }
split

根据指定的分隔符,将字段从字符串类型转成数组类型

join

split的反向操作

merge

合并两个数组或者哈希字段

strip

去除左右两端的空格

lowercase、uppercase

字段处理

rename

重命名某个字段

需要注意的是mutate里面各个操作是有顺序,并不是按照定义的顺序执行的

json

将指定的输入内容为JSON的字段解析为多个json结构中的字段
如果输入字段的内容是一个字符串,那么可以直接将这个json字符串直接拆分成几个字段

json {
  source => "Value_json"
  remove_field => ["Value_json"]
}

但是如果内容是一个json对象,那么就不能直接拆分了,做法是先将json对象的字符串塞进另一个新建的字段中,然后再用上面的方法拆分这个新字段,拆分之后就将这个新字段删除掉,也可以将原来的json对象字段也删除掉,这样留下的就是拆分后的各字段了。下面中的Value字段是一个JSON对象,该JSON对象内又嵌套的有MenuInfo和LogData字段。

mutate {
    add_field => { "Value_json" => "%{[Value]}" }
    add_field => { "MenuInfo_json" => "%{[Value][MenuInfo]}" }
    add_field => { "LogData_json" => "%{[Value][LogData]}" }
    remove_field => ["Value"]
  }
  json {
    source => "Value_json"
    remove_field => ["Value_json"]
    remove_field => ["MenuInfo"]
    remove_field => ["LogData"]
  }
  json {
    source => "MenuInfo_json"
    remove_field => ["MenuInfo_json"]
  }
  json {
    source => "LogData_json"
    remove_field => ["LogData_json"]
  }

metrics

内存里实现实时的计数和采样分析

ruby

通过编写ruby代码实现灵活的控制

输出类型

elasticsearch

output {
    elasticsearch {
        hosts => ["192.168.0.2:9280","192.168.0.3:9280"]
        user => 'xxx'
        password => 'xxxxxxx'
        index => "logstash-%{type}-%{+YYYY.MM.dd}"
        document_type => "%{type}"
        flush_size => 20000         #批量写入ES的最大条数
        idle_flush_time => 10       #批量写入ES的最大延时,所以每次批量写入ES的条数由上面这两个参数确定的,那个先满足都会触发
        sniffing => true            #当指定节点不可用时,自动寻找其他节点
        template_overwrite => true  # 是否重写模板
    }
}

index => "logstash-%{type}-%{+YYYY.MM.dd}"
可以通过变量来灵活指定index的名字,这样可以匹配预先定义好的模板,自动创建新的index。这里面的时间就是取自于之前的@timestamp字段,所以如果在这里想要使用日志格式化,就不能随意的删除时间字段

批量写入ES的参数控制

低版本的是通过flush_size和idle_flush_time参数控制的,5.0之后还会受patch_size这个参数的控制,因为filter-output线程跟input线程分开了,这个参数是filter-output线程批量接收input队列消息数的数目,相当于每个filter-output线程控制批量写入ES,跟ES有一个连接池,而不是只有一个连接。

template:logstash-*
默认是一个上面的名字的模板,符合上面的命名的话都会应用这个模板。不起这样的名字就不会用到这个模板。

{
  "order": 0,
  "version": 60001,
  "index_patterns": [
    "logstash-*"
  ],
  "settings": {
    "index": {
      "refresh_interval": "5s"
    }
  },
  "mappings": {
    "_default_": {
      "dynamic_templates": [
        {
          "message_field": {
            "path_match": "message",
            "match_mapping_type": "string",
            "mapping": {
              "type": "text",
              "norms": false
            }
          }
        },
        {
          "string_fields": {
            "match": "*",
            "match_mapping_type": "string",
            "mapping": {
              "type": "text",
              "norms": false,
              "fields": {
                "keyword": {
                  "type": "keyword",
                  "ignore_above": 256
                }
              }
            }
          }
        }
      ],
      "properties": {
        "@timestamp": {
          "type": "date"
        },
        "@version": {
          "type": "keyword"
        },
        "geoip": {
          "dynamic": true,
          "properties": {
            "ip": {
              "type": "ip"
            },
            "location": {
              "type": "geo_point"
            },
            "latitude": {
              "type": "half_float"
            },
            "longitude": {
              "type": "half_float"
            }
          }
        }
      }
    }
  },
  "aliases": {}
}

 

 

 


input,queue,pipline运行的线程模型

 

flume读取日志文件保存到es flume写入es_字段

 

 

flume读取日志文件保存到es flume写入es_flume读取日志文件保存到es_02

 

 

简述:

(1)每个Input启动一个线程,从对应数据源获取数据

(2)Input会将数据写入一个队列:默认为内存中的有界队列(意外停止会导致数据丢失)。为了防止数丢失Logstash提供了两个特性:

Persistent Queues:通过磁盘上的queue来防止数据丢失。默认使用的是memory,需要手动调整为persisted

Dead Letter Queues:保存无法处理的event(仅支持Elasticsearch作为输出源) 。默认不开启,需要手动开启

上面的配置都是在logstash.yml文件中。

(3)Logstash会有多个pipeline worker, 每一个pipeline worker会从队列中取一批数据,然后执行filter和output(worker数目及每次处理的数据量由配置确定)

Persistent Queue说明

https://www.elastic.co/guide/en/logstash/current/persistent-queues.html

flume读取日志文件保存到es flume写入es_字符串_03

提供at-least-once保证消息不丢失,但不保证不重复

局限性:tcp,udp,ZeroMQ,以及没有ack机制的input类型,无法保证数据不丢失;持久化硬盘损坏,故障情况无法处理