第十三章 Logstash进阶

本文详细介绍了Logstash的体系结构,包括输入、过滤和输出插件,以及队列机制,强调了持久化队列和死信队列在应对数据处理中的重要性。Logstash的管道配置包括主管道和单管道配置,提供了配置示例。文章还探讨了多种数据库(如MySQL)到Elasticsearch的数据同步方案,比较了不同方案的优缺点,并给出了Oracle数据同步的具体实现,涉及Logstash配置文件、环境准备、配置参数以及增量同步的实现方法。
摘要由CSDN通过智能技术生成

一、Logstash体系结构

1、概念介绍

Logstash数据传输管道所具备的流水线特征,体现在数据传输过程分为三个阶段:输入、过滤和输出。这三个阶段按顺序依次相连,像一个加工数据的流水线。 在实现上,它们分别由三种类型的插件实现,即输入插件、过滤器插件 和输出插件,并可通过修改配置文件实现快速插拔。除了这三种类型的插件以外, 还有种称为编解码器(Codec)的插件。编解码器插件用于在数据进人和离开管道时对数据做解码和编码,所以它一般都是与具体的输入插件或输出插件结合起来使用

事件(Event)是Logstash中另一个比较重要的概念,它是对Logstash处理数据的一种面向对象的抽象。如果将Logstash比喻为管道,那么事件就是流淌在管道中的水流。事件由输入插件在读入数据时产生,不同输入插件产生事件的属性并不完全相同,但其中一定会包含有读入的原始数据。过滤器插件会对这些事件做进步处理,处理的方式主要体现在对事件属性的访问、添加和修改。最后,输出插件会将事件转换为目标数据源的数据格式并将它们存储到目标数据源中。

在Logstash管道中,每个输入插件都是在独立的线程中读取数据并产生事件。但输入插件并不会将事件直接传递给过滤器插件或输出插件,而是将事件写人到一个队列中。队列可以是基于内存的,也可以是基于硬盘的可持久化队列。Logstash管道会预先生成一组工作线程,这些线程会从队列中取出事件并在过滤器中处理,然后再通过输出插件将事件输出。事件队列可以协调输入插件与过滤器插件、输出插件的处理能力,使得Logstash具备了一定的消峰填谷能力。

除了输入插件使用事件队列与过滤器插件、输出插件交互以外,Logstash还在输出插件与目标数据源之间提供了一个死信队列(Dead Letter Queue)。死信队列会在目标数据源没有成功接收到数据时,将失败的事件写人到队列中以供后续做进步处理。

输入插件、过滤器插件、输出插件、编解码器插件以及事件、队列等组件, 共同协作形成了整个Logstash 的管道功能,它们构成Logstash的体系结构, 如图所示:

 

2、插件

插件(Plugin)通过简单的配置就可接入系统并增强系统功能。Logstash提供了丰富的输入、过滤器、输出和编解码器插件,它们也都是通过配置接入系统并增强Logstash在某一方面的功能。 输入插件的作用是使数据进入管道并生成数据传输事件,过滤器插件则对进入管道的事件进行修改、清洗等预处理,而输出插件则将过滤器处理好的事件发送到目标数据源。输入插件和输出插件都可以使用编解码器在数据进入或退出管道时对数据进行编码或解码,使数据以用户期望的格式进入或退出管道。

插件开发都很便捷,一般都会提供统一接口作为开发框架,开发人员只要遵从接口规范并编写逻辑就可以开发出新的插件。Logstash插件使用Ruby语言开发

2.1、logstash-plugin命令

 Logstash官方提供的插件并非全部绑定在Logstash中,有一部分插件需要用户在使用时手工安装,比如log4j输入插件在默认情况下就没有安装。Logstash提供了一条插件管理的命令logstash-plugin,可以用于查看、安装、更新或删除插件。除了Logstash官方提供的插件以外,用户还可以根据需要自定义插件, 这些插件也需要使用logstash- plugin命令管理。有关logstash-plugin的具体使用方法,可通过执行logstash-plugin-help查看帮助。

2.2、插件配置

Logstash插件的可插拔性,除了体现在可通过logstash-plugin命令方便地安装与删除以外,还体现在已安装插件的使用和关闭仅需要通过简单的配置即可实现。Logstash配置文件分为两类,一类是用于设置Logstash自身的配置文件,主要是设置Logstash启动、执行等基本信息的配置文件。这类配置文件位于Logstash安装路径的config目录中,包括logstash. yml、pipelines. yml、jvm. options、log4j. properties等文件。在这些配置文件中,logstash. yml文件是核心配置文件,采用的语法格式为YAML;其余文件分别用于配置JVM和log4j:

jvm.optionslog4j2.propertieslogstash-sample.conflogstash.ymlpipelines.yml startup.options

另一类配置就是设置Logstash管道的配置文件了,它们用于设置Logstash如何应用这几种插件组成管道,是应用Logstash最核心的内容之一。

最简单的配置Logstash管道的方式就是我们前面展示的方式,通过logstash命令参数-e 配置管道再有一种方式就是通过-f 参数指定管道配置文件的路径以配置文件的形式设置Logstash管道Logstash在启动时会自动扫描目录,并加载所有以. conf 为扩展名的配置文件,所以用户可以将Logstash的管道配置文件放置在这个目录中。

 Logstash管道配置的语法格式不是YAML,它的语法格式是更接近Ruby哈希类型的初始化格式。

3、事件

Logstash事件由一组属性组成,包括数据本身、事件产生时间、版本等。不同输入插件产生事件的属性各不相同,这些事件属性可以在过滤器插件和输出插件中访问、添加、修改或是删除。

由于事件本身由输入插件产生,所以在输入插件中不能访问事件及其属性。这里所说的对事件及其属性的访问是指在Logstash管道配置中的访问,比如过滤器插件配置中根据事件属性执行不同的过滤等。

在事件属性中有一个比较特殊的属性tags,它的类型为数组,包含了插件在处理事件过程中为事件打上所有标签。比如插件在处理事件中发生了异常,一般都会为事件添加一个异常标签。标签本身就是一个字符串,没有特别的限制。事件的标签在一开始的时候都是空的,只有插件为事件打上了新标签,这个属性才会出现在输出中。所以,总体来说可以认为事件是一组属性和标签的集合。

3.1访问事件属性

 在管道配置中访问事件属性的最基本语法形式是使用方括号和属性名,例如[ name ]代表访问事件的name属性;如果要访问的属性为嵌套属性,则需要使用多层次的路径,如[ parent][ child]相当于访问parent. child属性;如果要访问的属性为事件的最顶层属性则可以省略方括号。

事件中有哪些属性取决于输入插件的类型,但有一些事件属性几乎在所有事件中都有,比如@version、@timestamp和@metadata等。这类属性大多以@开头,可以认为是事件的元属性。其中@version代表了事件的版本,@timestamp是事件的时间戳。

@metadata与前述两个属性不同,它在默认情况下是一个空的散列表( Hash table)。@metadata最重要的特征一般不会在最终的输出中出现,即使在插件中向这个散列表中添加了内容也是如此。(只有当输出插件使用rubydebug解码器,并且将它的metadata参数设置为true,@metadata属性才会在输出中显示): code => "event. set ('[ @ metadata][hello]','world' ) " @ metadata

属性设计的目的并不是为了给输出添加数据,而是为了方便插件做数据运算。它类似于一个共享的存储空间,在过滤器插件和输出插件中都可以访问。因为在实际业务有一些运算结果是需要在插件间共享而又不需要在最终结果中输出,这时就可以将前一插件处理的中间结果存储在@metadata中。

除了使用方括号访问事件属性以外,在插件参数中还可以通过“%{ field_name}"的形式访向事件属性,其中field. name就是前述的方括号加属性名的形式。例如,在文件输出插件中将日志根据级别写人到不同的文件中:

output {

file{

path => "/var/log/%{loglevel}. log"

}

}

在上面的例子中,loglevel是当前事件的一个属性名称,file输出插件的path参数通过%{ loglevel}的形式读取这个属性的值,并以它的值为日志文件名称。

3.2、事件API

事件API主要是在一些支持Ruby脚本的插件中使用等。这些插件一般都有一个code参数接收并运行Ruby脚本,在这些脚本中就可以使用event内置对象访问、修改或者删除事件属性。

4、队列

在互联网时代,许多活动或突发事件会导致应用访问量在某一时间点瞬间呈几何式增长。在这种情况下,应用产生的数据也会在瞬间爆发,而类似Logstash这样的数据管道要搬运的数据也会突然增加。如果没有应对这种瞬间数据爆炸的机制,轻则导致应用数据丢失,重则直接导致系统崩溃,甚至引发雪崩效应将其 他应用一并带垮。

应对瞬间流量爆炸的通用机制是使用队列,将瞬间流量先缓存起来再交由后台系统处理。后台系统能处理多少就从队列中取出多少,从而避免了因流量爆炸导致的系统崩溃。

Logstash输入插件对接的事件队列实际上就是应对瞬间流量爆炸、提高系统可用性的机制,它利用队列先进先出的机制平滑事件流量的峰谷,起到了削峰填谷的重要作用。除了输入插件使用的事件队列,输出插件还有一个死信队列。这个队列将会保存输出插件没有成功发送出去的事件,它的作用不是削峰填谷而是容错。

4.1、持久化队列

Logstash输入插件默认使用基于内存的事件队列,这就意味中如果Logstash因为意外崩溃,队列中未处理的事件将全部丢失。不仅如此,基于内存的队列容量小且不可以通过配置扩大容量,所以它能起到的缓冲作用也就非常有限。为了应对内存队列的这些问题,可以将事件队列配置为基于硬盘存储的持久化队列( Persistent Queue)。

持久化队列将输入插件发送过来的事件存储在硬盘中,只有过滤器插件或输出插件确认已经处理了事件,持久化队列才会将事件从队列中删除。当Logstash因意外崩溃后重启,它会从持久化队列中将未处理的事件取出处理,所以使用了持久化队列的Logstash可以保证事件至少被处理一次。

如果想要开启Logstash持久化队列,只要在logstash. yml文件中将queue. type参数设置为persisted即可,它的默认值是memory。当开启了持久化队列后,队列数据默认存储在Logstash数据文件路径的queue目录中。数据文件路径默认是在Logstash安装路径的data目录,这个路径可以通过path. data参数修改。持久化队列的存储路径则可以通过参数path. queue修改,它的默认值是${ path. data |/ queue。

尽管持久化队列将事件存储在硬盘上,但由于硬盘空间也不是无限的,所以需要根据应用实际需求配置持久化队列的容量大小。Logstash持久化队列容量 可通过事件数量和存储空间大小两种方式来控制,在默认情况下Logstash持久化队列容量为1024MB即1CB而事件数量则没有设置上限。当持久化队列达到了容量上限,Logstash会通过控制输入插件产生事件的频率来防止队列溢出,或者拒绝再接收输入事件直到队列有空闲空间。持久化队列事件数量容量可通过queue. max events修改,而存储空间容量则可通过 queue. max bytes来修改。

事实上在许多高访问量的应用中,单纯使用Logstash内部队列的机制还是远远不够的。许多应用会在Logstash接收数据前部署专业的消息队列,以避免瞬间流量对后台系统造成冲击。这就是人们常说的MQ( Message Queue),比如Kafka、RocketMQ等。这些专业的消息队列具有千万级别的数据缓存能力,从而可以保护后续应用避免被流量压跨。所以在Logstash的输入插件中也提供了一些对接MQ的输入插件,比如kafka、rabbitmq等。

4.2、死信队列

Logstash输入插件的事件队列位于输入插件与其他插件之间,而死信队列则位于输出插件与目标数据源之间。如果Logstash处理某一事件失败,事件将被写入到死信队列中。

Logstash死信队列以文件的形式存储在硬盘中,为失败事件提供了采取补救措施的可能。死信队列并不是Logstash中特有的概念,在许多分布式组件中都采用了死信队列的设计思想。由于死信队列的英文名称为Dead Letter Queue,所以在很多文献中经常将它简写为DLQ。

Logstash在目标数据源返回400或404响应状态码时认为事件失败,而支持这种逻辑的目标数据源只有Elasticsearch。所以Logstash死信队列目前只支持目标数据源为Elasticsearch的输出插件,并且在默认情况下死信队列是关闭的。开启死信队列的方式与持久化队列类似,也是在logstash. yml文件中配置,参数名为dead_ letter_queue.enable。死信队列默认存储在Logstash数据路径下的dead_letter_queue目录中,可通过path. dead_ letter. _queue参数修改。

死信队列同样也有容量上限,默认值为1024MB,可通过dead_letter_queue.max_bytes参数修改。虽然死信队列可以缓存一定数量的错误事件, 但当容量超过上限时它们还是会被删除,所以依然需要通过某种机制处理这些事件。Logstash为此专门提供了一种死信队列输入插件,它可以将死信队列中的事件读取出来并传输至另一个管道中处理。

二、管道配置

Logstash安装路径下的bin目录包含了一些重要的命令,启动Logstash管道就是通过其中的logstash命令实现。logstash命令在执行时会读取管道配置,然后根据管道配置初始化管道。管道配置可通过命令行-e 参数以字符串的形式提供也可以通过命令行-f 参数以文件形式提供。前者定义管道配置的方式称为配置字符串(Config String)后者则是配置文件,这两种方式只能任选其一而不能同时使用

在logstash. yml配置文件中,也包含了config. string和path. config两个参数,分别用于以上述两种方式配置默认管道。logstash命令与logstash. yml配置文件之间还有许多这样用于配置管道的共享参数,下面就先来看看这些配置参数。

1、主管道配置

Logstash启动后会优先加载在logstash. yml文件中配置的管道,并且为这个管道指定惟一标识main,我们可以称这个管道为主管道。无论是通过-e参数以配置字符串的形式创建管道,还是通过-f参数以配置文件的形式创建管道,它们在默认情况下都是在修改主管道的配置。既然有主管道存在,那么是不是还可以配置其他管道呢?

答案是肯定的。事实上Logstash可以在一个进程中配置多个管道,不同的是主管道是在logstash. yml文件中配置,而其他管道则是在pipeline. yml文件中配置。但是pipelie. yml中配置的管道与主管道互斥,如果在logstash. yml文件中或是使用logstash命令行的-e、-f 等参数配置了主管道,那么pipeline. yml文件中配置的其他管道就会被忽略。只有主管道缺失,logstash命令才会尝试通过pipeline. yml文件初始化其他管道。无论是主管道还是其他管道,它们的配置参数都是相同的。

在logstash. yml文件中主管道配置参数范例如下:

pipeline.id: main

pipeline.workers: 2

pipeline.batch.size: 125

pipeline.batch.delay: 50

pipeline.unsafe_shutdown: false

其中:

pipeline. id用于设置管道的ID。

而pipelne. workers则用于设置并发处理事件的线程数量,默认情况下与所在主机CPU核数相同

pipeline.batch.size: 每个工作线程每批处理事件的数量

pipeline.batch.delay: 工作线程处理事件不足时的超时时间

pipeline.unsafe_shutdown: 如果还有未处理完的事件,是否立即退出

Logstash处理事件并不是来一个处理一个, 而是先缓存125个事件或超过50ms后再统一处理。由于使用了缓存机制,所以当Logstash管道因意外崩溃时会丢失已缓存事件。pipeline. unsafe_ shutdown参数用于设置在Logstash正常退出时,如果还有未处理事件是否强制退出。在默认情况下,Logstash会将未处理完的事件全部处理完再退出。如果将这个参数设置为true,会因为强制退出而导致事件丢失。同时,这个参数也解决不了因意外崩溃而导致缓存事件丢失的问题。

2、单管道配置

一个logstash实例可以运行一个管道也可以运行多个管道,它们相互之间可以不受任何影响。一般来说,如果只需要运行一个管道可以使用logstash. yml配置的主管道;而在需要运行多个管道时才会使用pipeline. yml配置。无论是单管道还是多管道,对于其中某一具体管道的配置格式完全一致。这种格式不仅在配置文件中有效,在使用字符串配置管道时也是有效的。

2.1、语法格式

管道配置由3个配置项组成,分别用于配置输入插件、过滤器插件和输出插件,如下面的示例所示。尽管输入插件、过滤器插件和输出插件在处理数据时按顺序进行,但在配置文件中它们的顺序并不重要,而且它们也都不是必要配置。 所有插件配置都必须要在这三个配置项中,插件是哪种类型就应该放置在哪一个配置项中。每一个配置项可以放多个插件配置,并可以通过一些参数设置这些插件的特性。

input {

……

}

filter {

……

}

output {

…..

}

一个插件的具体配置由插件名称和一个紧跟其后的大括号组成,大括号中包含了这个插件的具体配置信息。虽然编解码器也是一种插件类型,但由于只能与输入或输出插件结合使用,所以编解码器只能在输入或输出插件的codec参数中出现。例如,在示例中就配置了一个stdin输入插件、一个aggregate过滤器插件和一个stdou输出插件。其中stdin使用的编解码器mutiline也是一个插件, 它的配置也遵从插件配置格式,由编解码器插件名称和大括号组成。在编解码器mutiline的大括号中配置了pattern和what两个参数,而在过滤器插件aggregate的大括号中则配置了task. _id和code两个参数。

input {stdin{

codec => multiline {

pattern => "^\s"

what => "previous"

}

}

}

filter {

aggregate {

task id => "%{message}"

code => "event.set ('val', event.get('val')==nil?11:22)"

}

}

output {stdout { }}

插件参数的格式与插件本身的配置在格式上有些相同,但又有着比较明显的 区别。插件配置在插件名称与大括号之间是空格,而插件参数则是使用“=>” 关联起来的键值对。其中,键是插件参数名称,而值就是参数值。这种格式与Ruby哈希类型的初始化一样,所以本质上来说每个插件的配置都是对Ruby哈希类型的初始化。

管道配置的这种格式中还可以添加注释,注释以“#”开头,可以位于文件的任意位置。在Logstash安装路径的config目录中,有一个logstash- sample. conf的样例配置文件,可以在配置管道时参考。

3、多管道配置

Logstash应用多管道通常是由于数据传输或处理的流程不同,使得不同输入事件不能共享同一管道。假如Logstash进程运行的宿主机处理能力超出一个管道的需求,如果想充分利用宿主机的处理能力也可以配置多管道。当然在这种情况下,也可以通过在宿主机上启动多个Logstash实例的方式,充分挖掘宿主机的处理能力。但多进程单管道的Logstash比单进程多管道的Logstash占用资源会更多,单进程多管道的Logstash在这种情况下就显现出它的意义了。

多管道虽然共享同Logstash进程,但它们的事件队列和死信队列是分离的。在使用多管道的情况下,要分析清楚每个管道的实际负载,并以此为依据为每个管道分配合理的计算资源,起码每个管道的工作线程数量pipeline.workers应该与工作负载成正比。

配置单进程多管道的Logstash,是通过pipeline. yml配置文件实现的。在启动Logstash时,如果没有设置主管道信息,Logstash读取pipeline. yml文件以加载管道配置信息。pipeline. yml通过config. string或path. config参数以配置字符串或配置文件的形式为每个管道设置独立的配置信息,还可以设置包括事件队列、管道工作线程等多种配置信息。

pipeline. yml文件也采用YAML语法格式,它使用YAML中的列表语法来定义多个管道,在每个列表项中通过键值对的形式来定义管道的具体配置。默认情况下,logstash. yml没有配置主管道,而pipeline. yml文件的内容也全部注释了,所以直接无参运行logstash时会报pipeline. yml文件为空的错误。配置范例如下:

- pipeline.id: test

pipeline.workers: 1

pipeline.batch.size: 1

config.string: "input { generator {} } filter { sleep { time => 1 } } output

{stdout { .. } }"

- pipeline.id: another_test

queue.type: persisted

path.config: "/tmp/logstash/*.config"

配置了两组管道,它们的惟一标识分别是test和another_test。test管道使用confg sting直接设置了管道的插件信息,而another_ test则通过path. config指定了管道配置文件的路径。可见,pipeline. yml配置多管道时使用的参数与logstash. yml中完全一致,它还可以使用配置队列和死信队列的参数,只是这些参数都只对当前管道有效。

三、编解码器插件

在Logstash管道中传递是事件,所以输入和输出插件都存在数据与事件的编码或解码工作。为了能够复用编码和解码功能,Logstash将它们提取出来抽象到一个统一的组件中,这就是Logstash的编解码器插件。由于编解码器在输入和输出时处理数据,所以编解码器可以在任意输入插件或输出插件中通过codec参数指定。

1、plain插件

Logstash版本7中一共提供了20多种官方编解码器插件,在这些编解码器插件中最为常用的是plain插件,有超过八成的输入和输出插件默认使用的编解码器插件就是plain。

plain编解码器用于处理纯文本数据,在编码或解码过程中没有定义明确的分隔符用于区分事件。与之相较,line编解码器则明确定义了以换行符作为区分事件的分隔符,每读入行文本就会生成一个独立的事件,而每输出一个事件也会在输出文本的结尾添加换行符。line编解码器符合人们处理文本内容的习惯,所以也是种比较常用的编解码器。除了plain和line编解码器以外,json编解码器是另一种较为常用的编解码器,主要用于处理JSON格式的文本数据。

尽管plain编解码器非常重要,但它在使用上却非常简单,只有charset和format两个参数。其中charset 用于设置文本内容采用的字符集,默认值为UTF-8,而format则用于定义输出格式,只能用于输出插件中。format参数定义输出格式时,采用的是“%{}”形式,例如:

input {

stdin {codec => line }

}

output{

stdout{

codec => plain {

format => "The message is % {[message]}\nIt's

from %{[host]}\n"

}

}

}

在示例中,Logstash在标准输入(即命令行)等待输入,并将输入内容按format参数定义的格式以两行打印在标准输出中。

2、line编解码器

以行为单位对文本数据进行解码时,每行都有可能成为一个独立事件;而在编码时,每个事件输出结尾都会添加换行符。以行为单位的编解码器有line和mutiline两种,前者以单行为输入事件,而后者则根据一定条件将多行组装成一个事件。

mutiline编解码器也是以行为编解码的单元,但在它编解码的事件中可能会包含一行也有可能会包含多行文本。mutiline编解码器对于处理日志中出现异常信息时非常有用,它能将异常合并到同一个日志事件中。例如,在下面中所示的一段日志文本,从是一个Java异常打印出来的堆栈信息,在逻辑上它们都应该归属于ERROR日志:

 

一般来说,正常日志都以方括号“[”开头,而当有异常写人到日志中时,异常信息不会按日志统一格式打印。mutiline提供了几个可以设置如何合并多行的参数,比如说:

codec = > mutiline{

pattern = > "^\[ "

negate = > truewhat = > "previous"

}

pattern参数设置了一个正则表达式,代表的含义是以方括号“[”开头的文 本。pattern参数与negate参数共同决定什么的文本需要被合并,negate参数用于设置对pattern定义的正则表达式取反。所以上面定义这两个参数的含义就是所有不满足“^\[”正则表达式的行都需要被合并,而合并到哪里则由参数what来决定。what参数有previous和next两个可选值,前者将需要合并行与前一个事件合并,而后者则会将需要合并行与后一个事件合并。

3、json编解码器

json文本的编解码器有两种,一种叫json编解码器,另一种叫json_lines编解码器。在输入时从JSON格式的数据中将消息解析出来生成事件,而在输出时将事件编码为JSON格式的数据。如果json编解码器在解析JSON时出错,Logstash会使用plain编解码器将JSON视为纯文件编入事件,同时会在事件中添加_jsonparsefailure标签。

json编解码器只有一个参数charset,用于设置JSON数据字符集,默认为UTF-8。

json_ lines编解码器也用于处理基于JSON的文本格式,但它处理的是一种被称为JSONLines的特殊 JSON格式。这种格式由多个独立的JSON行组成,每一行都是有效的JSON值。

 json_lines可用于存储每行一个JSON的结构化数据,适用于unix风格的文本处理工具和shell管道。有关JSON Lines的相关说明请参考JSON Lines官方网站http://jsonlines. org/。

json_ lines编解码器包括charset和delimiter两个参数,前者用于设置JSON数据字符集,默认为UTF-8;而后者则用于设置JSON Lines格式中的行分隔符。

4、序列化编解码器

在分布式应用中,为了将对象在网络中传输通常需要将对象转换为可传输的文本或二进制字节流。这种将对象转换为文本或字节的过程就称为序列化,而将文本或字节还原为对象的过程则称为反序列化。由于存在读取序列化数据格式的需求,Logstash也提供了两种针对不同序列化协议的编解码器,它们就是avro和protobuf。

 Avro最初是Hadoop的一个子项目,负责Hadoop中数据序列化与反序列化,现在已经发展为Apache顶级开源项目。Avro需要通过Schema文件定义对象格式,然后再依照Schema文件定义做序列化和反序列化。Logstash的avro编解码器也是基于Schema文件,所以在定义avro编解码器时必须要指定的参数是 schema_uri,用于定义Schema文件的存储路径。

Protobuf是Google定义的另一种序列化协议,与其他序列化协议相比Protobuf更小更快也更简单,所以在很多领域都得到了广泛应用。Logstash的protobuf编解码器默认并没有安装,需要使用“logstash-plugin installlgstash-codec-protobuf"安装。protobuf编解码器有两个参数,一个是class_ name用于指定.proto文件中定义类的名称,另一个是include_ path用于定义.proto文件的存储路径。

四、输入输出插件

1、概念介绍

Logstash管道可以配置多个输入插件,这可以将不同源头的数据整合起来做统 一处理。在分布式系统中,数据都分散在不同的容器或不同的物理机上,每份数 据往往又不完整,需要类似Logstash这样的工具将数据收集起来。比如在微服务 环境下,日志文件就分散在不同机器上,即使是单个请求的日志也有可能分散在 多台机器上。如果不将日志收集起来,就无法查看一个业务处理的完整日志。

Logstash管道也可以配置多个输出插件,每个输出插件代表一种对数据处理 的业务需求。比如对日志数据存档就可以使用s3输出插件,将日志数据归档到S3存储服务上;还可以使用elasticsearch输出插件,将数据索引到Elasticsearch中以便快速检索等。在业务系统创建之初,人们对于数据究竟会产生什么样的价 值并不清楚。但随着人们对于业务系统理解的深人,对数据处理的新需求就会进 发出来。面对新需求,只要为Logstash管道添加新的输出插件就能立即与新的 数据处理需求对接起来,而对已有数据处理业务又不会产生任何影响。到目前为 止,Logstash对于常见的数据处理需求都可以很好的对接,这包括数据归档存 储、数据分析处理、数据监控报警等。

Logstash官方提供的输入插件与输出插件都有50多种,而在这共计100多种 的插件中每一种插件又有不同的配置参数,全部都了解意义不大。这些插件基本 上都会成对出现,大多数输入插件也会在输出插件中出现。

2、stdin输入插件和stdout插件

两个最简单的插件,即stdin输入插件和stdout插件。这两个插件分别代表标 准输入和标准输出,也就是命令行控制台。由于它们比较简单,所以一般不需要做任何配置就可以直接使用。

stdin只有一组通用参数,这些参数不仅对stdin有效,对其他输入插件也有效。

 

3、elasticsearch插件

Elasticsearch既可以作为Logstash的输入也可以作为输出,但在多数情况下Elasticsearch都是作为 Logstash 的输出使用。

elasticsearch输出插件使用hosts参数设置连接Elasticsearch实例的地址,hosts参数可以接收多个Elasticsearch实例地址,Logstash在发送事件时会在这些实例上做负载均衡。如果连接的Elasticsearch与Logstash在同一台主机且端口为9200,那么这个参数可以省略。

elasticsearch插件连接相关部分参数列表

名称   参数类型    默认值    用处

  • Hosts uri [//127.0.0. 1] ES 所在主机地址,可包括端口号。
  • Path  string 无 访问 Elasticsearch 的 HTTP 路径。
  • bulk_ path string 无 执行_ bulk 请求的路径。
  • proxy uri 无 HTTP 代理。
  • parameters hash 无 URL 参数。
  • pipeline string nil Elasticsearch ingest 管道。
  • user string 无 Elasticsearch 用户名。
  • password password 无 Elasticsearch 密码。
  • timeout number 60 连接超时时间。
  • pool_max number 1000 连接最大数。
  • retry_max_interval number 64 重连时间间隔的上限,单位为秒(s)。
  • retry_initial_interval number 2 请求失败后重连的初始时间间隔, 之后每次重连后加倍时间间隔,单位为秒(s)。
  • retry_on_conflict number 1 冲突时重试次数。
  • resurrect_delay number 5 连接端点死机后重连的时间延迟, 单位为秒。
  • sniffing boolean false 感知 Elasticsearch 集群节点变化。
  • smiffing_delay number 5 感知 Elasticsearch 集群节点变化的时 间间隔,单位为秒。
  • smiffing_path string 无 感知 Easticsearch 集群节点变化的路径。

elasticsearch输出插件将Logstash事件转换为对Elasticsearch索引的操作,默认情况下插件会将事件编人名称格式为“ logstash- %{+ YYYY. MM. dd}”的索引中,可使用index参数修改索引名称格式。

除了编入索引以外,elasticsearch输出插件还支持根据ID删除文档、更新文档等操作,具体可以通过action参数配置。action可选参数包括index、delete、create和update几种,其中index和create都用于向索引中编入文档,区别在于create在文档存在时会报错,而index则会使用新文档替换原文档。Delete和update则用于删除和更新文档,所以在使用delete、update和create时需要通过document_ id参数指定文档ID。此外,update在更新时如果文档ID不存在,那么可以使用upsert参数设置添加新文档的内容。

例如:

output{

elasticsearch {

document_id = > "% {message}"

action = > "update"upsert = > "{\"message\":\"id didn't exist\"}"

}

}

message属性的内容作为文档ID值,并根据这个ID值使用当前文档更新原文档。如果文档不存在则使用upsert参数设置的内容添加文档,upsert参数要求为字符串类型,其中属性要使用双引号括起来。除了upsert以外,还可以将doc_as_upsert参数设置为true,则在文档不存在时会将当前文档添加到索引。elasticsearch插件还有很多与Elasticsearch索引、模板、路由等相关的参数。

elasticsearch输出插件部分参数列表

名称 参数类型 默认值 用处

  • Index string 无 事件输出的索引,默认值为 logstash- %{+ YYYY. MM. dd}
  • action string index Elasticsearch 操作行为,可选值index、delete 、create、updateupsert
  • upsert string 无 upsert 内容
  • doc_as_upsert boolean false 执行 upsert 操作,即在文档 ID 不存在 时创建文档
  • document_id string 无 文档 ID
  • document_type string 无 文档映射类型
  • parent string nil 父文档 ID
  • routing string 无 分片路由

Elasticsearch作为Logstash输入源时,Logstash默认会通过DSL从索引"logstash-*"中读取所有文档,一日读取完毕Logstash就会自动退出。输入插件使用的DSL通过query参数设置,参数值与Elasticsearch的_search接口参数相同。

4、文件插件

面向文件的输入插件从文件中读取数据并转换为事件传入Logstash管道,而面向文件的输出插件则将Logstash管道事件转换为文本写入到文件中。面向文件的输入输出插件名称都是file,它们都需要通过path参数设置文件路径。

不同的是,文件输入插件的path参数类型是数组,所以可以指定组文件作为输入源头。而文件输出插件的path参数则为字符串,只能设置一个文件名称。此外,文件输入插件的path参数只能是绝对路径而不能使用相对路径,并且在路径中可使用“*”和“**”这样的通配符。

而文件输出插件则可以使用相对路径,并且还可以在路径中使用“%{}”的形 式动态设置文件路径或名称。这也是文件输出插件形成滚动文件的方式,例如使用“path=>/logs/log-%{+YYYY-MM-dd}.log”配置输出插件就可以每日生成一个文件。

4.1、事件属性

文件输入插件产生的输入事件中包含一个名为path的属性,它记录了事件实际来自哪个文件。在配置文件输入插件时,如果在读取路径配置中使用了通配符或使用数组指定了多个读取路径时,path属性可以明确事件实际来源的文件。换句话说,path属性记录的是某个文件的具体路径和名称而不会包含通配符。 默认情况下,文件输入插件以一行结束标识一个输入事件,message属性中会包含换行符前面的所有内容。当然也可以通过将插件的编解码器设置为mutiline,将多行数据归为一个输入事件。文件输入插件默认也以换行符“\n”作为行结束标识。

4.2、读取模式

文件输入插件默认会从文件结尾处读取文件的新行,这种模式被称为尾部读取模式(Tail Mode)。如果 Logstash启动后被读取文件没有新行产生,则不会有新的事件输出。

除了尾部读取模式以外,文件输入插件也可配置为从文件开头读取,这种模式称为全部读取模式(Read Mode)。也就是说,文件输入插件支持两种读取模式,尾部读取模式和全部读取模式,可以通过设置文件输入插件的参数mode以修改读取模式。例如可以将示例中读取模式设置为read,这样只要被读取文件不为空,Logstash启动后就会有输出:

input {

file {

path = > "xxxx/xxxxx/logstash-plain. log"

mode = > "read"

}

}

在尾部读取模式下,文件被看作是一个永不终止的内容流,所以EOF EndofFile)标识对于文件输入插件来说没有特别意义。如果使用过Linux中的tail命令,文件输入插件的尾部读取模式与tail -0F极为相似。

尾部读取模式非常适合实时搬运日志文件数据,只要有新日志产生它就会将变化内容读取出来。而在全部读取模式下文件输入插件将每个文件视为具有有限行的完整文件,如果读取到EOF就意味着文件已经结束,文件输入插件会关闭文件并将其置于“未监视”状态以释放文件。

参数file_ completed action可以设置文件读取结束后的释放行为,可选值为delete、log和log_and_delete,默认行为是删除文件即delete。如果选择log或log_and_delete,还需要使用file_completed log_ path参数指定一个日志文件及其路径,文件输入插件会将已经读取完的文件及其路径写入到这个日志文件中。对于log_and_delete来说,则会先写日志再将文件删除。需要特别注意的是,文件输入插件在写这个日志文件时,并不负责对文件的滚动,所以该日志文件可能会变得非常大

4.3、多文件

当path参数配置的文件是多个时,文件输入插件会同时监控这些文件。当文件大小发生了变化,文件输入插件会将这些文件激活并打开。

文件输入插件感知文件变化的方法是每隔1秒钟轮询所有被监控文件,这个时间间隔由参数stat_interval控制。显然增加stat_ interval的值会减少访问文件的次数,但会增加用户感知文件变化的时间延迟。

除了感知已知文件的变化以外,文件输入插件也会感知到新文件的加入。新加入文件的感知时间间隔由discover_interval参数来控制,它设置的值是stat_interval的倍数,所以实际发现新文件的时间间隔是 discover. interval * stat interval。默认情况下discover_interval为15s,而slat. inteval为1s,所以发现新文件的最大时间间隔为15s。如果被监控文件变化非常频繁,可以将stat_interval值缩小以减少数据延迟。

在轮询被监控文件时,轮询文件的顺序也是有讲究的。在Logstash的早期版本中,轮询的顺序由操作系统返回文件的顺序决定。新版本中这个顺序由两个参数共同决定,一个是file_sort_by参数,定义根据文件哪一个属性排序;另一个是file_sort direction参数,定义文件轮询是按升序还是按降序。

file_sort_by有两个可选值last_modified和path,前者按文件最后修改时间排序,是默认值;后者是按文件路径和名称排序。file_sort_direction也有两个可选值,asc代表升序,desc代表降序,默认值为asc。所以在默认情况下,轮询是按照文件最后修改时间的升序依次进行的,也就是最先修改的数据将会被优先 感知到并处理。

文件输入插件同时打开文件的数量也有上限,默认情况下文件输入插件可同时打开文件的数量是4095个,这个值可以通过max_open_files参数来修改。设置了同时打开文件的最大值,可以防止打开文件过多而影响系统性能或者将系统拖垮。如果需要打开的文件数量很多,又不能将max_open_files设置过大,可以使用close_older将长时间无变化的文件关闭以释放打开文件的数量空间。close_ older设置了文件未被读取的时间间隔,达到这个标准后文件即被关闭,默认值为1小时。文件在关闭后如果又发生了变化,它将重新进入队列中等待打开。由于在全部读取模式下文件读取完毕后就会被释放,所以close_older参数仅对尾部读取模式有效。

文件输入插件在打开文件后,会按前面讲的顺序依次读取所有文件,直到读取完所有文件。在默认情况下,它会先将一个文件需要处理的内容全部读取完再读下一个文件。但这个行为可以通过更改file_chunk_size和file _chunk_count来修改,它可以使插件每次只读取文件一部分内容后就读取下一个文件,这对于快速将全部文件的变化反馈给用户非常有用。

其中,file_hunk_size用于设置每次读取数据块的大小,默认值为32768即32kB,file_chunk_count则用于设置处理每个文件时读取多少个数据块,默认值为4611686018427387903,所以相当于读取没有上限。一般只需要将file_chunk_ count值缩小,而无需修改file_ _chunk_ size值。

除此之外,还有一个start_position参数,设定文件开始读取位置,仅对tail模式有效。

在配置项中如果遇到了与时长有关的,有一类类特殊的数据类型叫string_duration,它用于以字符串的形式定义时间范围。string_duration的基本格式为[number][string],即由数字、空格和字符串组成,中间的空格可加也可以不加,而字符串代表时间单位。例如45s和45s都代表45秒,s代表的时间单位为秒。事件单位支持周(w week weeks)、天(d day days)、小时(h hour hours)、分钟(m min minute minutes)、秒(s sec second seconds)、毫秒(ms msec msecs)、微秒(us usec usecs)。

4.4、文件输出插件

相对于文件输入插件来说,文件输出插件就简单多了。默认情况下,它会根据path参数指定的路径创建文件,并将事件转换为JSON格式写人文件的一行。如果文件中已经有数据,则新事件会附加到文件结尾。当然也可以通过修改write_ behavior参数为overwrite,覆盖文件中已存在的数据。

5、更多插件

5.1、面向关系型数据库的插件

面向关系型数据库的插件用于从关系型数据库中读取或写入数据,它们的名称是jdbc。

Logstash官方只提供了jdbc输入插件,而输出插件则属于社区开发插件。jdbc输入插件可以定期执行SQL查询数据库,也可以只执行一次SQL。每读入数据库表中一行会生成一个输入事件,行中每一列会成为事件的一个属性。jdbc输入插件可用于结构化数据的大数据分析和处理,可以将关系型数据库中的数据传输到类似Elasticsearch这样的NoSQL数据库以方便海量数据检索或存档,或传输给Spark、Storm这样的大数据分析框架做实时分析处理。

JDBC连接数据库需要指定JDBC驱动程序、JDBC URL以及数据库用户名和密码,jdbc输入插件也不例外。除了连接数据库,还要为jdbc输入插件定义要执行的SQL语句。

5.2、面向消息中间件的插件

Logstash虽然内置了队列对输入事件做缓冲,但面对瞬间突发流量时仍然存在出错甚至崩溃的可能。同样地,如果Logstash给目标数据源发送数据量过大,也存在着压垮目标数据源的可能。所以在一些流量比较大的互联网应用中,基本上都会视情况在Logstash之前或之后部署分布式消息中间件,以达到削峰填谷、保护应用的作用。除了削峰填谷,消息中间件还可以作为一种适配器来使用。有些应用可以向消息中间件发送消息,但却没有Logstash的相关输入插件。这时就可以让Logstash从消息中间件中订阅该应用的消息,以间接的方式从应用中获取数据。

Logstash面向消息中间件的插件主要是kafka、redis和rabbitmq三个插件,stomp和jms插件虽然也是基于消息的协议,但它们并未在Logstash的默认绑定中,需要自行安装。

5.3、面向通信协议的插件

Logstash面向通信协议的插件通过标准通信协议与外部数据源交互,支持包括TCP、UDP、HTTP、IMAP 等广泛应用的协议。由于这些插件从通信协议层面支持数据传输,可以支持基于这些协议对外提供服务的应用,因此极大地扩展了Logstash的应用范围。以Elaticsearch为例,它对外提供的REST接口基于HTTP,所以尽管Logstash提供了elasticsearch输入和输出插件,但依然可以通过HTTP相应的插件实现数据输入和输出的功能。

5.4、执行命令插件

据采集问题,但依然有可能存在一些特殊情况需要使用特定命令或脚本处理数据。Logstash提供了两个可执行命令或脚本的插件exec和pipe,它们都同时具有输入和输出插件。

exec输入插件将执行命令的结果转换为事件传人Logstash管道,而exec输出插件则针对每一个事件执行一条命令,所以它们都必须要通过command参数指定要执行的命令。

pipe插件基于操作系统管道机制实现数据的输入和输出,但正因为使用了管道而不能在Windows系统中使用pipe插件。pipe同时具备输入插件和输出插件。

5.5、开发测试插件

Logstash提供了两种可以自动生成事件的输入插件,即generator和heartbeat输入插件,可以使用它们测试配置是否正常或测试组件的性能指标。

generator插件默认会不间断地生成输入事件,事件包含的文本内容为“ Hello world!"。事件的生成次数可由参数count设置,默认值0代表不限次数。事件的文本内容可以通过message或lines参数定义。

heartbeat插件与generator插件类似,也是自动生成包含特定文本内容的输入事件。不同的是,heartbeat插件的输入事件会按定的时间间隔产生。事件生成的事件间隔默认值为60s,可使用interval参数修改。

五、过滤器插件

一个Logstash管道可以配置多个过滤器插件,每个过滤器插件的职责各不相同。它们可以对原始数据做添加、删除、修改等基本的转换操作,也可以对原始文本数据做结构化处理,还可以对数据做一致性校验、清除错误数据等。这相当于数据处理与分析领域中的数据清洗,是数据处理与分析中极为重要的一环。 多个过滤器会组装成过滤器的职责链,事件经过一个过滤器处理后会传递给下一个过滤器,过滤器处理事件的顺序与它们在配置文件中的配置次序一致。

1、全文数据结构化过滤器

全文数据是典型的非结构化数据。但有些全文数据具有一定的格式规范,它们比较接近半结构化数据,但相比于半结构化数据又粗糙一些。在这种类型的文本中,比较典型的例子就是系统或应用输出的文本日志。这些文本日志往往都是按一定的次序包含时间、线程、日志级别、日志信息等内容,所以在处理上非常适合使用正则表达式做解析,并从中提取出结构化数据以方便对数据做进一步分析。

Logstash过滤器插件中,grok过滤器和dissect过滤器都可以通过定义模式文本中提取数据。不同的是grok过滤器使用的是正则表达式定义模式,而dissect过滤器则是使用“%{}”的格式定义了一套独立的模式规则。从使用上来说,grok过滤器由于使用了正则表达式而功能强大,但也使得它在处理文本时的性能比较低;而dissect虽然功能简单,但性能更为出众,适用于文本格式规则比较简单的文本。

1.1、grok过滤器

grok过滤器在Logstash过滤器插件中应该是最为常用的文本结构化过滤器, 对于一些中间件日志文本的处理来说,grok过滤器几乎是必须要选择的过滤器插件。grok过滤器根据一组预定义或自定义的模式去匹配文本,然后再将匹配的结果提取出来存储在事件属性上,后续过滤器插件就可以在此基础上做进一步处理。

1)预定义模式

grok过滤器预定义了大约120种模式,它们使用一组由大写字母组成的模式名称来惟一标识。常用的模式名称包括NUMBER (数字)、WORD (单词)、SPACE (空格)、DATA(任意字符)等:

  • NUMBER (?:% I BASE10NUM| ) 十进制数,BASE10NUM 的简写
  • BASEIONUM (?<! [0-9. +-])(?>[ +-]?(?:(?:[0-9] +(?:\. [0-9] +)?) |(?:\.[0-9]+))) 十进制数,整型或浮点型数值
  • WORD 单词
  • SPACE 空格
  • NOTSPACE 非空格
  • DATA 任意字符,非贪婪模式匹配
  • GREEDYDATA 任意字符,贪婪模式匹配
  • TIMESTAMP_IS08601 IS08601 定义的日期格式,即yyyy- MM-dd'T'HH:mm:ss:SSSZZ
  • LOGLEVEL 日志级别

由于这些预定义的模式很多,大家可通过如下地址查看这些模式的列表和详细定义:

https ://github.com/logstash-plugins/logstash-patterns-core/tree/master/patterns

如果使用grok过滤器预定义模式定义文本提取规则,需要遵从一定的语法规则。具体来说,它们的基本语法格式为“% { SYNTAX: SEMANTIC}”,其中SYNTAX为模式名称,SEMANTIC为存储匹配内容的事件属性名称。如果想匹配一段数字并希望将匹配出来的数字存储到事件的age属性上,则按上述语法规则应该写为"% { NUMBER:age}"。在这段提取规则中就使用到了前面的预定义模式NUMBER。

2)自定义模式

如果grok过滤器预定义的模式不能满足要求,用户也可以根据grok过滤器 指定的语法直接使用正则表达式定义模式。自定义模式的语法格式为“(?<属性 名>模式定义)”,其中“<属性名>”指定了模式匹配后存储属性的名称,注意这里的尖括号是语法的一部分故而不能省略。“<属性名>”后面的“模式定义”代表使用正则表达式定义的模式。例如,义邮政编码的模式为6位数字,并希望提取后存到postcode属性上,则使用grok自定义模式表示的提取规则为”(?<postcode >\d{6})”。

3)match参数

grok过滤器最重要的一个参数是match,它接收一个由事件属性名称及其文本提取规则为键值对的哈希结构。grok过滤器会根据match中的键读取相应的事件属性,然后再根据键对应的提取规则从事件属性中提取数据。下面通过一个简单的例子来说明match参数的用法。

以Logstash默认日志的格式为例,Logstash每条日志开头都包括三个方括号,方括号中的内容依次为时间、日志级别、Logger名称等,在这三个方括号后面跟的是日志具体信息。具体格式可以到Logstash安装路径下的logs目录中查看logstash- plain. log文件,这里摘取其中几行为例:

 

由于Logstash日志遵从一定的格式规范,所以如果使用grok过滤器的match参数定义提取规则,就可以将时间、日志级别、Logger名称和日志内容提取出来。 提取规则如示例所示:

filter{

grok {

match = >{

message = > "\[%{TIMESTAMP_ISO8601:time}\]\[%{LOGLEVEL:level}%{SPACE}\]\[%{NOTSPACE:lo

gger}%{SPACE}\]%{SPACE}%{GREEDYDATA:msg}"

}

}

}

在示例中,match参数的类型为散列,其中只包含有一个条目。这个条目以message属性为键,而以对该属性的提取规则为值。在message属性的提取规则中,使用了多个预定义模式。比如其中的“% {LOGLEVEL:level}"就是使用LOGLEVEL预定义模式匹配文本,并将匹配结果添加到事件的level属性。

在默认情况下,grok插件在模式匹配失败时会给事件打上_grokparsefailure标签。除了match参数以外,grok还有很多参数,大家可以自行查询相关网址。

1.2、dissect过滤器

dissect过滤器是另一种可以实现文本结构化的过滤器,它使用用户自定义的分隔符切分文本,然后再将切分后的文本存储到事件属性上。

dissect过滤器使用多个切分单元(Dissection)定义切分规则,切分单元之间可以有一个或多个字符,这些字符就是切分文本的分隔符。定义一个切分单元的格式为“%{ field}",其中的field在一般情况下是事件的属性名称。

例如在“%{ field1}/% {field2}"中共定义了两个切分单元,由于符号“/”位于两个切分单元之间,所以将被认为是切分文本的分隔符。如果切分规则中包含有三个以上的切分单元,则在切分规则中就可以指定多个分隔符了。

mapping参数

dissect过滤器使用mapping参数定义切分文本的方式,mapping参数也是哈希类型。与match参数类似,mapping参数的键为需要切分的属性名称,而值则为切分规则。下面以Logstash日志存档文件名称为例,说明如何使用mapping参数定义切分规则。Logstash日志存档文件名称有着非常固定的格式,比如:

 

假设输人事件的message属性保存的文本就是Logstash存档日志文件名称,使用dissect过滤器做切分就可以定义如下:

filter {

dissect{

mapping => {

message =>  "%{component}-%{log_type}-%{year}-%{month}-%{date}-%{index}.%{extension}.%{co

mpress}"

}

}

}

在示例中,message属性定义的切分规则中就包含有两种分隔符“-”和“.”,dissect过滤器会按分隔符的顺序依次切分文本。切分后的文本会存储到事件属性上,在后续过滤器中,这些属性就可以参与其他处理了。 总的来看,dissect过滤器虽然不及grok过滤器强大,但针对简单文本的处理却也足够了。

2、处理半结构化文本

半结构化文本一般都有固定的格式要求,它们往往都有通用的格式规范,比如JSON、XML、CSV等。同样通过可以解析半结构化文本的过滤器插件,它们同样会将解析的结果存储在事件属性上。

2.1、json 过滤器

json过滤器从属性中读取JSON字符串,然后按JSON语法解析后将JSON属性添加到事件属性上。所以使用json插件有一个必须要设置的参数source,它是用来指定插件从哪一个事件属性中读取JSON字符串,例如:

filter{

json {source =>message”}

}

2.2、xml过滤器

类似地,xml过滤器用于解析XML格式的文本,过滤器读取XML原始文本的事件属性由source参数配置,XML解析结果存储的事件属性由target参数配置。例如:

filter(

xml {

source => message

target => doc

}

}

在示例中,配置的xml过滤器会从事件的message属性读取XML文本,然后将文本解析后存储到doc属性上。xml过滤器在解析XML时会使用XML标签 名称作为事件属性名称,而将标签体的文本作为事件属性的值。但如果标签体中嵌套了子标签或者标签本身还带有属性,那么事件的属性就会以散列类型代表这个标签的解析结果。对于标签属性,它们将会以属性名称和值为键值对成为散列结构的一个条目,而标签体中的非标签文本将会以content为键形成条目。比如:

<root>

<student id="1">tom</student>

<grade>3</grade >

</root >

那么解析后就成为:

"doc" => {

"student" =>{

"content" => " tom",

" id"=>"1"

},

"grade" => "3 "

}

默认情况下,xml过滤器为了防止同名标签覆盖问题,会将每个标签都解析为数组类型。

2.3、csv过滤器

CSV是Comma Separated Values的简写,即逗号分隔值。它是一种数据格式,以这种格式保存的文件通常扩展名为CSV。这种格式以纯文本形式表示表格数据,数据的每一行都是一条记录,每个记录又由一个或多个字段组成,用逗号分隔。一些关系型数据库的客户端就提供了导出CSV格式数据的功能csv过滤器就是根据这种格式将它们解析出来,并将解析结果存储到不同的属性名上。

默认情况下,属性名由csv过滤器自动生成,名称格式类似于column1、column2; 属性名也可以通过columns参数以数组的形式定义。此外,在CSV格式中第一行也可以用于表示列名,如示例中所示。如果希望使用CSV文本中定义的列名,则需要通过autodetect_column_names参数显示地设置首行为列名。

2.4、kv过滤器

kv过滤器插件用于解析格式为"key = value"的键值对,多个键值对可以使用空格连接起来,使用field_split或field_split_pattern可以定制连接键值对的分隔符。kv过滤器默认会从message属性中取值并解析,解析后的结果会存储在事件的根元素上。

例如"usermname =root&password = root"的分隔符为“&",比如:

message = >"username = root&password = root " 则:

filter {

kv {field_split => "&"}

}

3、事件聚集过滤器

Logstash提供的aggregate过滤器可以将多个输人事件整合到一起,形成一个新的、带有总结性质的输人事件。例如,假设某个业务请求由多个服务处理,每个服务都会生成一些与请求相关的数据。如果需要从这些数据中提取部分信息做整合就可以使用aggregate过滤器,比如从每个服务的日志中提取单个服务处 理时间以计算整个请求处理了多少时间。

为了将相关事件整合到一起,aggregate过滤器需要解决两个问题。首先,由于需要将一组输人事件整合起来做处理,所以aggregate过滤器需要有办法区分输人事件的相关性;其次,由于需要跨事件做数据处理和运算,所以aggregate过滤器需要一个能在多个输人事件之间共享数据的存储空间。 aggregate过滤器通过任务的概念组合相关事件,并通过map对象在事件之间共享数据。

六、ES实现方案调研与实现

1、数据库同步数据到ES的方案比较

方案1

利用logstash定时向数据库读取数据然后写入到elastic search中。

架构:数据库+logstash+elastic search

缺点:

1)因为是定时读取数据库,存在一定的时延。

2)若同步时间间隔调的比较短,比如每秒定时同步一次数据,此时会增大业务数据库的压力。

3)若一次同步的数据量比较大的话,网络传输成本会增大。

相关链接:https://www.cnblogs.com/csts/p/6120644.html

为了解决时延的问题,采用同步双写方案,实时同步数据,于是引入方案2:

方案2:

同步双写,即在业务应用系统写数据到数据库时,同时插入一条数据到elastic search中。

架构:业务应用+elastic search。

缺点:硬编码,强业务耦合,性能差等。

为了解决同步双写性能差的问题,那么引入mq来实现异步双写,于是引入方案3。

方案3:

异步双写,即引入mq并开发一个数据同步系统,在业务系统应用(生产者)中每做一笔交易,然后将数据发送到mq,数据同步系统(消费者)订阅mq消息,完成数据同步到elastic search中。

架构:业务应用+mq+同步系统+elastic search。

缺点:数据同步逻辑与业务应用系统强耦合在一起。

相关链接:https://blog.csdn.net/lp2388163/article/details/80633190

为了解决数据同步逻辑与业务应用系统强耦合的问题,为了解耦且解决logstash定时同步时延的问题,于是引入阿里的canal来同步数据,引入方案3:

方案4

利用canal订阅数据库的binlog日志文件,然后实时发送数据到elastic search中

架构:mysql+canal+elastic search

缺点:

1)若在高并发场景下,对canal服务器及客户端可能会造成性能压力

2)若canal同步binlog日志过程中,canal客户端宕机可能导致数据丢失

相关链接:https://www.jianshu.com/p/9677ca6ca34e

为了解决方案3中高并发的性能压力及canal客户端宕机数据丢失的问题,此时也引入mq,既做到限流削峰又起到数据缓存存储的作用

方案5:

利用canal订阅数据库的binlog日志文件,然后实时发送数据到mq中,然后利用数据同步系统订阅mq的消息完成数据同步到elastic search中

架构:mysql+canal+mq+数据同步系统+elastic search

缺点:增加了系统架构复杂性

相关链接:https://www.cnblogs.com/sanduzxcvbnm/p/11558858.html

PS:

1)由于canal的开源方案中只支持mysql,若要同步oracle数据,可以考虑oracle同步工具比如ogg,databus等。

2)文中提及的mq有很多,比如active mq、rabbit mq、rocket mq、kafka等,根据不同的业务场景选择合适的mq。

参考链接:https://juejin.im/post/5e16e5ba5188254c281d21d3

2、Oracle数据同步工功能实现

首先要知道的是,logstash抽取Oracle的数据是通过追踪某一个递增列实现增量导入的,所以就要求在查询结果有一个递增列,这并不是说需要一个自动增长列,因为在Oracle中,rownum可以作为查询结果中的一个递增列,如

SELECT * FROM(SELECT NP.*, ROWNUM RN FROM (SELECT * FROM NPMIS_ZW_YSDFMX) NP ) WHERE RN BETWEEN 5000001 AND 6000000;

所以logstash可以追踪rownum来增量导入。

2.1、环境准备

在logstash目录中新建文件夹driver;

把数据库驱动包复制到driver文件夹下(ojdbc6.jar)

把数据库驱动包ojdbc6.jar复制到logstash-7.7.0\logstash-core\lib\jars文件夹下

2.2、配置文件

新建配置文件oracle.conf,该配置文件的内容如下:

input{

  jdbc{

     jdbc_driver_library => "/usr/share/logstash/lib/ojdbc6-11.2.0.4.jar"

     jdbc_driver_class => "Java::oracle.jdbc.driver.OracleDriver"

     jdbc_connection_string => "jdbc:oracle:thin:@192.168.0.181:1521/UNIMETA"

     jdbc_user => "uni_meta"

     jdbc_password => "szadmin123"

     connection_retry_attempts => "3"

     jdbc_validate_connection => "true"

     jdbc_validation_timeout => "3600"

     jdbc_paging_enabled => "true"

     jdbc_page_size => "10000"

     # statement_filepath => "/etc/logstash/script/SYS_USER.sql"

     statement => "SELECT * FROM(SELECT SU.*, ROWNUM RN FROM (SELECT * FROM SYS_USER) SU) WHERE RN > :sql_last_value"

     lowercase_column_names => false

     record_last_run => true

     tracking_column_type => numeric

     use_column_value => "true"

tracking_column => "RN"

     # 记录上次执行结果数据的存放位置

     last_run_metadata_path => "/etc/logstash/script/last_id_sys_user"

     clean_run => false

     schedule => "* * * * *"

     type => "test_type01"

  }

 jdbc{

     jdbc_driver_library => "/usr/share/logstash/lib/ojdbc6-11.2.0.4.jar"

     jdbc_driver_class => "Java::oracle.jdbc.driver.OracleDriver"

     jdbc_connection_string => "jdbc:oracle:thin:@192.168.0.181:1521/UNIMETA"

     jdbc_user => "uni_meta"

     jdbc_password => "szadmin123"

     connection_retry_attempts => "3"

     jdbc_validate_connection => "true"

     jdbc_validation_timeout => "3600"

     jdbc_paging_enabled => "true"

     jdbc_page_size => "10000"

    # statement_filepath => "/etc/logstash/script/SYS_OPERATE_LOG.sql"

     statement => "SELECT * FROM(SELECT SOL.*, ROWNUM RN FROM (SELECT * FROM SYS_OPERATE_LOG) SOL) WHERE RN > :sql_last_value"

     lowercase_column_names => false

     record_last_run => true

     use_column_value => true

     tracking_column_type => numeric

     tracking_column => "RN"

     last_run_metadata_path => "/etc/logstash/script/last_id_operate_log"

     clean_run => false

     schedule => "* * * * *"

     type => "test_type02"

  }

}

filter {

  date {

    match => [ "timestamp" , "yyyy/MM/dd HH:mm:ss Z" ]

  }

  mutate {

    convert => { "zdl" => "float_eu"}

  }

}

output{

 if [type] == "test_type01" {

   elasticsearch {

      hosts => ["http://192.168.0.101:9200"]

      index => "sys-user"

      document_type => "_doc"

      document_id => "%{RN}"

    }

  }

  if [type] == "test_type02" {

    elasticsearch{

      hosts => ["http://192.168.0.101:9200"]

      index => "sys-operate-log"

      document_type => "_doc"

      document_id => "%{RN}"

    }

  }

}

备注:

如果同步的表中有自动增长的列例如orderid,则可以选择该列为sql_last_valueWHERE orderid > :sql_last_value参数tracking_column => "orderid"

2.3脚本配置文件:

SYS_USER.sql文件配置内容:

SELECT * FROM(SELECT SU.*, ROWNUM RN FROM (SELECT * FROM SYS_USER) SU) WHERE RN > :sql_last_value

SYS_OPERATE_LOG.sql文件配置内容:

SELECT * FROM(SELECT SOL.*, ROWNUM RN FROM (SELECT * FROM SYS_OPERATE_LOG) SOL) WHERE RN > :sql_last_value

创建存储last_id的文件:

touch /etc/logstash/script/last_id_sys_user

touch /etc/logstash/script/last_id_operate_log

2.4pipeline.yml配置可选

作用是将配置文件加载进来,可写多个

- pipeline.id: oracle-log

path.config: "/data/elasticsearch/logstash/conf/oracle-log.conf"

2.5、启动测试

cd到logstash的bin目录下,logstash.bat -f #配置文件jdbc.conf所在的路径#

nohup /usr/share/logstash/bin/logstash -f ./etc/logstash/conf.d/oracle-log.conf &

启动成功后,我们会从日志中看到每分钟数据都会进行一次同步:

[INFO ] 2020-07-23 14:06:00.238 [Ruby-0-Thread-27:

/usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/rufus-scheduler-3.0.9/lib/rufus/scheduler/jobs.rb:452] jdbc - (0.000882s) SELECT count(*) "COUNT" FROM (SELECT * FROM(SELECT SU.*, ROWNUM RN FROM (SELECT * FROM SYS_USER) SU) WHERE RN > 19.0) "T1" FETCH NEXT 1 ROWS ONLY

[INFO ] 2020-07-23 14:06:00.405 [Ruby-0-Thread-26:

/usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/rufus-scheduler-3.0.9/lib/rufus/scheduler/jobs.rb:331] jdbc - (0.002419s) SELECT count(*) "COUNT" FROM (SELECT * FROM(SELECT SOL.*, ROWNUM RN FROM (SELECT * FROM SYS_OPERATE_LOG) SOL) WHERE RN > 357.0) "T1" FETCH NEXT 1 ROWS ONLY

从日志信息中可以看到查询的SQL中where条件后面的数据就是rownum,如果数据有增加就会从上次同步的基础上进行增量同步。

2.6、创建索引并查看同步数据

首先到kibana的索引模式中创建索引,这里我们就选择我们配置文件中定义好的名称:sys-usersys-operate-log然后在索引配置汇总选择时间筛选字段名称分别为:update_time与operate_time。

创建完成在Discover窗口选择需要查看的索引进行查看:

 

注意:第一次同步的时候因为参数记录上次执行结果数据位置的值为0,索引会进行全量查询同步,数据比较多的情况下会比较慢。

3、注意事项

3.1、数据重复问题和Oracle增量数据ES同步慢问题

在默认配置下,tracking_column这个值是@timestamp,存在elasticsearch就是_id值,是logstash存入elasticsearch的时间,这个值的主要作用类似mysql的主键,是唯一的,但是我们的时间戳其实是一直在变的,所以我们每次使用select语句查询的数据都会存入elasticsearch中,导致数据重复。

解决方法:在要查询的表中,找主键或者自增值的字段,将它设置为_id的值,因为_id值是唯一的,所以,当有重复的_id的时候,数据就不会重复

3.2、配置参数说明:

input

// 是否记录上次执行结果, 如果为真,将会把上次执行到的 tracking_column 字段的值记录下来,保存到 last_run_metadata_path 指定的文件中

record_last_run => "true"

// 是否需要记录某个column的值,如果record_last_run为真,可以自定义我们需要track的column名称,此时该参数就要为true.。否则默认track的是timestamp的值。

use_column_value => "true"

// 如果use_column_value为true,需配置此参数。track的数据库column名,该column必须是递增的。一般是mysql主键,由于我们是用rownum也是递增的,可以在sql语句中查询出rownum作为这一列,orderid是rownum别名。

tracking_column => "orderid "

last_run_metadata_path => "/etc/logstash/script/last_id"

//如果不设置,会从头开始再查询一次,因为数据量巨大所以导致同步的时间会很长,可能导致Oracle数据没有同步到ES

clean_run => "false"

//sql语句最后添加WHERE orderid > :sql_last_value,如果不适用where,那么之前的所有配置同步的配置都没有生效,每次都执行查询全部的语句,然后在大量的结果集中比较orderid,再将符合要求的数据插入ES,所以非常耗时间,:sql_last_value会读取last_run文件的值,如果没有这个文件就赋值为0开始

statement => "SELECT np.*,ROWNUM orderid FROM xxx np WHERE orderid > :sql_last_value"

output

//index必须固定,不能随时间增长

index => "xxx"

//把查询出来的rownum赋值给document_id,document_id在文档中具有唯一性,所以不会重复,也就不会插入重复数据

document_id => "%{orderid }"

实际上并不是不同步,而是没有记录上次Oracle查询的位置,所以保存上次的查询记录就行了

3.3、数据丢失问题

logstash读取数据类型是由第一行或前几行决定的,所以如果某一列第一行值是整数,那么logstash就认定这一列是整数,第二行是小数,可能会把小数强转成整数,然后因为出现error丢失第二行或多行,丢失行可以在日志文件中看到,所以我们需要设置filter对列类型进行转换。

通过查看日志文件,查看出问题的是第几列,然后强转该列的类型。

float_eu是因为oracle查询出来的数据带有,号分隔符,比如12,340.99,所以需要写成float_eu,写成float会报错。如果是其他数据库可以试试float。

filter {

mutate {

convert => { "zdl" => "float_eu"}

}

}

Oracle同步原文链接:https://blog.csdn.net/chroje/article/details/80412967

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值