全方位立体监控之日志解决方案ELK(2)

来源:互联网 发布:数控画图软件 编辑:程序博客网 时间:2024/05/19 16:03

Logstash详细介绍

下面我们将对ELK框架进行深入详细的了解,了解了其中的原理,才能选择更加高效可靠的配置方案。ElasticSearch的配置比较简单,主要性能瓶颈在于内存以及节点设置方面,Kibana的配置也较为简单,web应用无很大的优化改进空间,主要在于第三方插件的使用。下面我们主要介绍Logstash的方案设计。

Logstash使用Ruby语言编写,它编写了自己的一套DSL。Logstash在处理数据的时候,就像管道一样,每一个配置文件中都必须存在一个input插件和一个output插件,数据以流的形式在管道中进行传输。下面我通用一个图例来表示,日志数据就像是大小形状不同的图案,通过nput进入到管道内部,然后通过一个圆形的filter插件,这样从output出来的数据就都成为了圆形。所以一个标准的Logstash配置,应该同时存在一个nput,filter,output配置。
这里写图片描述

通过官方文档我们可以看到,由于开放的社区环境,logstash中存在大量的插件,功能丰富且强大。可以接受多种形式的数据,进行多样性的处理,最后传输到各种各样的环境中。下面我们结合实际情况,介绍一些常用的插件。

  • 情景:需要监控Apache等生成的日志,可以动态监控文件内容。
    解决方案:
    监控文件内容,使用input:file插件。Logstash中使用一个名叫FileWatch的Ruby Gem库来监听文件变化,并且会生成一个${Home}/.sincedb的数据库文件来记录被监听的日志文件的当前读取位置。具体实现如下:
input {    file {            type => “apache”            path => "apache_access.log"            start_position => "beginning"            sincedb_path => "/dev/null"    }}

type:通过type字段来表示这个日志的类型或者归属等特性,用来进行区分。
path:指定需要监控的文件,可以使用*号来表示某个路径下的所有满足条件的文件。
start_position:默认为”beginning”,表示每次Logstash重启后文件都从头开始加载,直到当前监控的位置。”end”选项表示,每次读取文件都从最后开始,接收当前传输的最新的文件。
sincedb_path:指定.sincedb文件的位置。每次logstash读取数据时会从since_db中记录的pos开始读取数据。如果在重复测试的时候,为了方便,可以将sincedb_path定义为/dev/null,这样logstash每次重启时都会从头开始读取文件。

  • 情景:获取到日志文件后,需要对数据进行提取,最终以key-value字段的形式显示,便于Elasticsearch对数据进行索引,方便后期Kibana生成图表等。
    解决方案:
    使用filter:grok插件对数据进行提取。因为logstah会根据事件传输的当前时间自动给事件加上@timestamp字段,后续elasticsearch会根据该字段对事件进行排序。如果需要将日志中的事件抽取为@timestamp字段,还需用到filter:date插件。
    假设当前日志为log4j打印日志的格式为:

    %d [%p] [%c{1}] [%t] %30.30C.%M():%L - %m%n

    具体实现如下:

   filter {    if [type] == "log4j" {        grok {            match=>["message","(?m)%{TIMESTAMP_ISO8601:timestamp}\[%{LOGLEVEL:logLevel}\] \[%{WORD:method}\] \[%{GREEDYDATA:thread}\] %{GREEDYDATA:javaClass}\(\)\:%{NUMBER:line} - {GREEDYDATA:message}"]            remove_field => ["@version"]            overwrite => [ "message"]        }        date {            match=>[ "timestamp" , "yyyy-MM-dd HH:mm:ss,SSS"]        }       }}

==:Logstash可以判断值的正确性,所以这里可以对不同类型的事件进行不同的处理而互不影响。
match:用来处理数据,前面一个字段为原始字段名称。logstash支持正则表达式,现有的库中已经可以满足大部分事件。推荐在编写grok时,使用http://grokdebug.herokuapp.com/ 网站来帮助自己检验grok语法。
remove_field:Logstash会根据自己的模板为所有事件加上一些字段,可以用remove_field移除多余的属性。
overwrite:用正则表达式中提取出的字段来替代原始的字段。
date:用日志中抽取出的时间,取代logstash自动生成的时间字段。保证数据的时序性。默认的@timestamp字段为标准时间,不是本地时间。
效果:
输出的原始日志数据为:(数据有删减)

2016-04-15 16:31:13,758 [INFO] [DubboProtocol] [DubboSharedHandler-thread-1] protocol.dubbo.DubboProtocol$1.disconnected():135 - [DUBBO] pid=2535&revision=1.0.1&side=consumer&timestamp=1460709070016, dubbo version: sf.2.0.2, current host: 192.168.2.1

经过filter输出的数据为:

{
“message” => “pid=2535&revision=1.0.1&side=consumer&timestamp=1460709070016, dubbo version: sf.2.0.2, current host: 192.168.2.1”,
“@timestamp” => “2016-04-15T08:31:13.758Z”,
“host” => “localhost”,
“sequence” => 13112,
“timestamp” => ” 2016-04-15 16:31:13,758”,
“logLevel” => “INFO”,
“method” => “DubboProtocol”
“thread” => “DubboSharedHandler-thread-1”,
“javaClass” => “protocol.dubbo.DubboProtocol$1.disconnected”,
“line” => “135”
}

  • 情景:收集Java错误日志时,由于堆栈消息会产生多行数据,每一行消息都被当做单独的事件收集,并且经常grok出错。
    解决方案:
    Logstash有专门的codec:multiline插件用来处理多行事件。只需要事先约定好特殊的开始符号或者结束符号即可。
    具体实现如下:
input {     file {        type => "java_stack"        path =>"apache.log"        codec => multiline {            pattern => "^%{TIMESTAMP_ISO8601}"            negate => true            what => "previous"        }    }}

pattern:约定好的特殊的开始符或者结束符,一般使用正则符号。
negate:是否使用pattern中的正则匹配。
what:表示匹配的位置,previous表示指定行匹配pattern的内容是上一行的内容(开始部分),next指定行匹配pattern选项是下一行的内容(结束部分)。

  • 情景:系统使用Log4j打印日志,生成日志文件,但是Grok占用较多系统资源,寻求更高效可靠的解决方法。
    解决方案:
    1) 使用之前的input:file插件,所有的数据进入message字段。
    2) 配置log4j文件,使用Socket通信+input:log4j插件接收事件。
    log4j.properties文件配置如下:
 log4j.rootLogger=DEBUG, logstash###SocketAppender###log4j.appender.logstash=org.apache.log4j.net.SocketAppenderlog4j.appender.logstash.Port=4560log4j.appender.logstash.RemoteHost=logstash_hostnamelog4j.appender.logstash.ReconnectionDelay=60000log4j.appender.logstash.LocationInfo=true

log4j.xml文件配置如下:

<appender  name="LOGSTASH" class="org.apache.log4j.net.SocketAppender">    <param name="RemoteHost" value="logstash_hostname" />    <param name="ReconnectionDelay" value="60000" />    <param name="LocationInfo" value="true" />    <param name="Threshold" value="DEBUG" /></appender><!—将logstash Appender加入到root--><root>    <level value="INFO"/>    <appender-ref ref="OTHERPLACE"/>    <appender-ref ref="LOGSTASH"/></root>

logstash配置文件如下:

input {  log4j {    type => "log4j-json"    port => 4560  }}

这样的方法是利用log4j的socket通信,进行数据传输,但是不推荐这样的方法。因为SocketAppender采用最原始的Socket通讯,IO性能较低。存储和发送日志是一个同步过程,有可能会出现打日志的动作堵死应用程序的场景。但是日志手机最基本的要求是不能影响原有系统的运行,所以不推荐这样的方法。

3) 使用jsonevent-layout插件,设置日志格式为Json形式,然后生成到日志文件中,依赖利用input:file插件,同时省略了grok的过程。

首先在pom.xml文件中,加入以下的配置:

<dependency>    <groupId>net.logstash.log4j</groupId>    <artifactId>jsonevent-layout</artifactId>    <version>1.7</version></dependency>

log4j.properties配置如下:

log4j.rootLogger=debug,jsonlog4j.appender.json=org.apache.log4j.DailyRollingFileAppenderlog4j.appender.json.File=app.loglog4j.appender.json.DatePattern=.yyyy-MM-ddlog4j.appender.json.layout=net.logstash.log4j.JSONEventLayoutV1

log4j.xml配置如下:

<appender name="logstah" class="org.apache.log4j.FileAppender">    <param name="Append" value="true" />    <param name="file"  value=" app.log " />    <layout class="net.logstash.log4j.JSONEventLayoutV1" /></appender>

打印出来的日志格式为:

{"thread_name":"ZkClient-EventThread-55-10.118.4.1:2181,10.118.4.2:2181,10.118.4.3:2181","message":" [DUBBO]  dubbo version: sf.2.0.2, current host: 192.168.2.1","@timestamp":"2016-04-15T07:15:47.794Z","level":"INFO","mdc":{},"file":"AbstractRegistry.java","class":"com.alibaba.dubbo.registry.support.AbstractRegistry","line_number":"422","logger_name":"com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry","method":"notify","@version":1,"source_host":"localhost"}

经过处理的事件:

{"thread_name":"ZkClient-EventThread-55-10.118.4.1:2181,10.118.4.2:2181,10.118.4.3:2181","message":" [DUBBO]  dubbo version: sf.2.0.2, current host: 192.168.2.1","@timestamp":"2016-04-15T07:15:47.794Z","level":"INFO","mdc":{},"file":"AbstractRegistry.java","class":"com.alibaba.dubbo.registry.support.AbstractRegistry","line_number":"422","logger_name":"com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry","method":"notify","@version":1,"source_host":"localhost""host":"localhost","sequence":968}

综合性能以及可靠性,推荐使用第三种方案,较为简单方便。

  • 情景:接收服务器系统日志,使用syslog进行数据传输。
    解决方案:
    1) logstash使用input:syslog插件,直接接收数据。
    具体实现如下:
input {      syslog {        port => "514"      }}

2) 使用input:tcp插件,接收数据再进行grok转化。

input {tcp {    port => "8514"}}filter {    grok {       match => ["message", "%{SYSLOGLINE}" ]    }    syslog_pri { }}

其实Logstash的syslog插件也就是使用UDPSocket,TCPServer和filter:grok来实现的,所以我们将syslog进行拆分,走TCP通道,并且使用filter多线程的模式来提高性能,所以推荐第二种方式。

0 0
原创粉丝点击