Logstash从grok到5.X版本的dissect

grok 作为 Logstash 最广为人知的插件,在性能和资源损耗方面同样也广为诟病。为了应对这个情况,同时也考虑到大多数时候,日志格式并没有那么复杂,Logstash 开发团队在 5.0 版新添加了另一个解析字段的插件:dissect。当日志格式有比较简明的分隔标志位,而且重复性较大的时候,我们可以使用 dissect 插件更快的完成解析工作。

检查所有插件
/your/logstash/path/bin/logstash-plugin list
如果没有,安装logstash-filter-dissect
/your/logstash/path/bin/logstash-plugin install logstash-filter-dissect
例如:

filter {
    dissect {
        mapping => {
            "message" => "%{ts} %{+ts} %{+ts} %{src} %{} %{prog}[%{pid}]: %{msg}"
        }
        convert_datatype => {
            pid => "int"
        }
    }
}

语法解释:
我们看到上面使用了和 Grok 很类似的 %{} 语法来表示字段,这显然是基于习惯延续的考虑。不过示例中 %{+ts} 的加号就不一般了。dissect 除了字段外面的字符串定位功能以外,还通过几个特殊符号来处理字段提取的规则:
● %{+key} 这个 + 表示,前面已经捕获到一个 key 字段了,而这次捕获的内容,自动添补到之前 key 字段内容的后面。
● %{+key/2} 这个 /2 表示,在有多次捕获内容都填到 key 字段里的时候,拼接字符串的顺序谁前谁后。/2 表示排第 2 位。
● %{?string} 这个 ? 表示,这块只是一个占位,并不会实际生成捕获字段存到 Event 里面。
● %{?string} %{&string} 当同样捕获名称都是 string,但是一个 ? 一个 & 的时候,表示这是一个键值对。

比如对 http://rizhiyi.com/index.do?id=123 写这么一段配置:
http://%{domain}/%{?url}?%{?arg1}=%{&arg1}
则最终生成的 Event 内容是这样的:

{
  domain => "rizhiyi.com",
  id => "123"
}

 

实际测试:
解析Nginx日志格式如下:

127.0.0.1 - - [24/May/2017:14:24:34 +0800] \"POST /ws-sale/shopJsonService HTTP/1.1\" 200 1139 \"-\" \"Apache CXF 2.7.0\" 0.002 0.002 \"-\"

发现如果出现了\”,会解析错误,例如:

    dissect {
        mapping => {
            "message" => "%{ipaddress} - - [%{timestamp}] \"%{verb} %{url} HTTP/%{?http_version}\" %{code} %{bytes} \"%{referrer}\" \"%{agent}\" %{} %{} \"%{cookie}\""
        }
    }

在网上也查到了相关文章:https://github.com/logstash-plugins/logstash-filter-dissect/issues/10
需要替换为单引号

    dissect {
        mapping => {
            "message" => '%{ipaddress} - - [%{timestamp}] "%{verb} %{url} HTTP/%{?http_version}" %{code} %{bytes} "%{referrer}" "%{agent}" %{} %{} "%{cookie}"'
        }
    }

另外发现最后一个字段是cookie,但是拿出来的值最后都有\”,例如如果cookie为空,则日志中会记录为-,而logstash打印出来是”cookie” => “-\””
这里用ruby去掉

event.set('cookie',event.get('cookie').chop)

另外使用ruby分割客户端IP和代理IP,uri和参数的时候,发现ruby code的用法在5.3也改了。
之前在2.3的时候,用法如下:

    ruby {
        code => "
                event['uri'] = event['url'].split('?')[0]
                event['parameter'] = event['url'].split('?')[1]
        "
    }

5.3版本报错如下:

[2017-05-24T16:03:14,482][ERROR][logstash.filters.ruby    ] Ruby exception occurred: Direct event field references (i.e. event['field']) have been disabled in favor of using event get and set methods (e.g. event.get('field')). Please consult the Logstash 5.0 breaking changes documentation for more details.

修改为:

    ruby {
        code => "
            event.set('uri',event.get('url').split('?')[0])
            event.set('parameter',event.get('url').split('?')[1])
        "
    }

最终配置:

filter{
    json {
        source => "message"
    }
    dissect {
        mapping => {
            "message" => '%{ipaddress} - - [%{timestamp}] "%{verb} %{url} HTTP/%{?http_version}" %{code} %{bytes} "%{referrer}" "%{agent}" %{} %{} "%{cookie}"'
        }
    }
    ruby {
        code => "
            event.set('uri',event.get('url').split('?')[0])
            event.set('parameter',event.get('url').split('?')[1])
            event.set('client',event.get('ipaddress').split(', ')[0])
            event.set('proxy',event.get('ipaddress').split(', ')[1..5])
            event.set('cookie',event.get('cookie').chop)
        "
    }
    date {
        match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss +0800"]
        target => "@timestamp"
        "locale" => "en"
        timezone => "UTC"
    }  
}