网络上有很例子给出一示例是采集一种类型的日志输出到logstash,但一个系统上日志种类很多,同一个采集端能区分不同日志类型吗?
下面的结构是nxlog做客户端采集,通过tcp协议发送到logstash,然后logstash传输到elasticsearch。
前提条件必需:
nxlog配置nxlog.conf:
Module im_file
File "D:\\jar\\dongli\\logs\\spring-boot.log"
SavePos TRUE
Path in_donglilog => out_donglitcp
采集D:\jar\dongli\logs\spring-boot.log日志,输出到192.168.1.238:514上
logstash配置:
input {
tcp {
port => 514
type=>"plm"
}
}
output{
if [type] == "plm"{
elasticsearch {
hosts => ["127.0.0.1:9200"]
index => "kelian-%{+YYYY.MM.dd}"
}
}
}
通过tcp协议监控514端口,这时候logstash工作mode是server(另一个工作mode是client,用于采集并发送数据),是监控514端口数据的。
网上例子多数止步于些,监听一个端口,然后接收数据,发送到elasticsearch。
假设,我们不仅仅监控dongli的日志D:\jar\dongli\logs\spring-boot.log日志,我还监听另一个系统日志,假设应用为kelian。这2个日志格式不一样。nxlog配置相对简单,主要是logstash怎么能区分接收的不同日志,在elasticsearch创建不同的index。总不能把2个应用日志输出到同一个index吧。
这个方法最简单,不同应用开启不同端口监控
nxlog配置
Module im_file
File "D:\\jar\\dongli\\logs\\spring-boot.log"
SavePos TRUE
Path in_donglilog => out_donglitcp
Module im_file
File "D:\\jar\\kelaien\\logs\\spring-boot.log"
SavePos TRUE
Path in_kelianlog => out_keliantcp
logstash配置:
input {
tcp {
port => 514
type=>"dongli"
}
tcp {
port => 515
type=>"kelian"
}
}
output{
if [type] == "dongli"{
elasticsearch {
hosts => ["127.0.0.1:9200"]
index => "dongli-%{+YYYY.MM.dd}"
}
}
if [type] == "kelian"{
elasticsearch {
hosts => ["127.0.0.1:9200"]
index => "kelian-%{+YYYY.MM.dd}"
}
}
}
最简单,但我不希望这么做,因为每增加一个应用就会增加一个端口,而增加一个端口就要增加这个端口对外开放,如果是阿里云ECS,还要修改安全组规则。个人觉得麻烦,但是这也不失为一种可选方式
要是能携带一个数据区分发送日志类型就好了。遗憾的是,nxlog并没有提供这种选项,怎么办?
修改传输的数据。
nxlog每读一行发送到logstash,在每行日志前加个特殊的字符串,然后logstash截取这个字符串,根据这个特殊的字符串创建不同的index。
原理是logstash字符引用,只要是input输入的值,都可以引用
nxlog配置:
Module im_file
File "D:\\jar\\dongli\\logs\\spring-boot.log"
SavePos TRUE
Module im_file
File "D:\\jar\\kelaien\\logs\\spring-boot.log"
SavePos TRUE
Module pm_transformer
Exec $raw_event = "dongli " + $raw_event;
Module pm_transformer
Exec $raw_event = "kelian " + $raw_event;
Path in_donglilog => proc_donglilog => out_donglitcp
Path in_kelianlog => proc_kelianlog => out_keliantcp
通过Processor模块,在每行日志行都添加了应用名。
logstash配置:
input {
tcp {
port => 514
type=>"plm"
}
}
filter{
if [type] == "plm" {
grok{
match=>{
"message" => "%{WORD:key} %{WORD}"
}
}
mutate{
gsub=>["message","%{key}",""]
}
}
}
output{
if [type] == "plm"{
if [key] == "dongli" {
elasticsearch {
hosts => ["127.0.0.1:9200"]
index => "dongli-%{+YYYY.MM.dd}"
}
}
if [key] == "kelian" {
elasticsearch {
hosts => ["127.0.0.1:9200"]
index => "kelian-%{+YYYY.MM.dd}"
}
}
}
}
处理的关键在过滤器代码中
grok{
match=>{
#拿到应用名
"message" => "%{WORD:key} %{WORD}"
}
}
mutate{
#将message里应用名替换为空
gsub=>["message","%{key}",""]
}
在output里就可以使用字段引用功能做判断了
if [type] == "plm"{
if [key] == "dongli" {
}
if [key] == "kelian" {
}
}
缺点
有个缺点,只对单行日志有作用,如果有多行合并为一行的异常日志就不适合,因为将关键字添加到行头,破坏了数据完。
当使用multiline插件过滤输入数据时,行头没办法区分,当使用multiline插件,合并多行时会出问题。
codec => multiline{
#以[开头视为一行
pattern => "^["
negate => true
what => "previous"
}
如果把关键字放在每行的结尾呢
nxlog配置:
Module im_file
File "D:\\jar\\dongli\\logs\\spring-boot.log"
SavePos TRUE
Module im_file
File "D:\\jar\\kelaien\\logs\\spring-boot.log"
SavePos TRUE
Module pm_transformer
Exec $raw_event = $raw_event + "(dongli)";
Module pm_transformer
Exec $raw_event = $raw_event + "(kelian)";
Path in_donglilog => proc_donglilog => out_donglitcp
Path in_kelianlog => proc_kelianlog => out_keliantcp
注意代码
Module pm_transformer
Exec $raw_event = $raw_event + "(dongli)";
Module pm_transformer
Exec $raw_event = $raw_event + "(kelian)";
关键放在行尾并用小括号括起来。
logstash配置:
input {
tcp {
port => 514
codec => multiline{
pattern => "^\d{4}(\-|\/|.)\d{1,2}\1\d{1,2}"
negate => true
what => "previous"
}
type=>"plm"
}
}
filter{
if [type] == "plm" {
grok{
match=>{
"message" => "(?[(]\w+[)\\r])"
}
}
mutate{
gsub=>["message","[(]%{ckey}[)]",""]
#gsub=>["ckey","\r",""]
}
}
}
output{
if [type] == "plm"{
if [ckey] == "(dongli)" {
elasticsearch {
hosts => ["127.0.0.1:9200"]
index => "dongli-%{+YYYY.MM.dd}"
}
}
if [ckey] == "(kelian)" {
elasticsearch {
hosts => ["127.0.0.1:9200"]
index => "kelian-%{+YYYY.MM.dd}"
}
}
}
}
主要的处理在过滤器里
if [type] == "plm" {
grok{
match=>{
"message" => "(?[(]\w+[)\\r])"
}
}
mutate{
gsub=>["message","[(]%{ckey}[)]",""]
}
}
拿到关键字,然后把message字段里关键删除。
这种方式也不失为一种解决方案,但不绝不是优雅的方案
它天生可多携带关键字,而且在windows运行也很稳定,所以我推荐使用filebeat替代nxlog。
filebeat.yml配置:
filebeat.inputs:
- type: log
enabled: true
paths:
- D:\jar\dongli\logs\spring-boot.logg
fields:
appname: dongli
- type: log
enabled: true
paths:
- D:\jar\kelaien\logs\spring-boot.log
fields:
appname: kelaien
logstash配置
input{
beats {
port => 515
type=>"beatss"
}
}
output{
if [fields][appname] == "dongli"{
elasticsearch {
hosts => ["127.0.0.1:9200"]
index => "dongli-%{+YYYY.MM.dd}"
}
}
if [fields][appname] == "kelaien"{
elasticsearch {
hosts => ["127.0.0.1:9200"]
index => "kelaien-%{+YYYY.MM.dd}"
}
}
}
上面对应的是单行日志,如果是多行日志,它的配置放在filebeats而不是logstash
filebeat.yml配置:
filebeat.inputs:
- type: log
enabled: true
paths:
- D:\jar\dongli\logs\spring-boot.logg
multiline:
pattern: '^\d{4}-\d{1,2}-\d{1,2}'
negate: true
match: after
fields:
appname: dongli
- type: log
enabled: true
paths:
- D:\jar\kelaien\logs\spring-boot.log
multiline:
pattern: '^\d{4}-\d{1,2}-\d{1,2}'
negate: true
match: after
fields:
appname: kelaien
多行的关键代码是
multiline:
pattern: '^\d{4}-\d{1,2}-\d{1,2}'
negate: true
match: after