Logstash:使用 Ruby 过滤器

Logstash 具有丰富的过滤器集,你甚至可以编写自己的过滤器,但是由于没有现成的过滤器,你可以直接将 Ruby 代码 入配置文件中,因此通常不必创建自己的过滤器。

使用 logstash-filter-ruby,你可以使用Ruby字符串操作的所有功能来解析奇异的正则表达式,不完整的日期格式,写入文件,甚至进行 Web 服务调用。

 

Logstash 安装

如果你从来没有安装过自己的 Logstash,你可以参考我之前的文章 “如何安装 Elastic 栈中的 Logstash” 来进行安装。

 

例子事件

让我们在整个日志事件示例中使用一个包含3个字段的示例:

  • 没有日期的时间戳 – 02:36.01
  • 源日志文件的完整路径 – /var/log/Service1/myapp.log
  • 字符串 – “Ruby is great”

该事件如下所示,我们将在接下来的示例中使用它。

02:36.01 /var/log/Service1/myapp.log Ruby is great

我们创建如下的 ruby-logstash.conf 文件:

ruby-logstash.conf

input {
    generator {
       message => "02:36.01 /var/log/Service1/myapp.log Ruby is great"
       count => 1
    }
}

filter {
}

output {
    stdout {
        codec => rubydebug
    }
}

现在运行 Logstash:

./bin/logstash -f ruby-logstash.conf 

几秒钟后,它应该显示 “Pipeline main started”。

由于我们未提供任何过滤器,因此它只是将日志事件作为 “message” 字段回显,并添加了有关主机和事件处理时间的一些元数据。现在,让我们做最少的事情,并将这条消息分解为几个字段:

input {
    generator {
       message => "02:36.01 /var/log/Service1/myapp.log Ruby is great"
       count => 1
    }
}

filter {
   grok {
     match => { "message" => "%{DATA:justtime} %{DATA:logsource} %{GREEDYDATA:msg}" }
   } 
}

output {
    stdout {
        codec => rubydebug
    }
}

这次我们使用了 grok 过滤器来结构化我们的数据。再次运行 Logstash:

显然这次,我们的数据变得结构化一点。但是就像我们之前提到的那样,这里的 justtime 显示是没有日期的。这个在我们的 ES 里是没法使用。我们在接下来的部分介绍如何使用 ruby 过滤器来对它进行加工。

 

使用 ruby 来解析不完整的日期

请注意,时间 '02:36.01' 非常稀疏。

它不提供日期,也不提供时区参考。  假设,我们必须假设日期为当天,只有在检查了服务器设置及其构造日志文件后,我们才知道实时时区。

让我们将以下处理添加到 ruby-logstash.conf 文件中的 filter 部分。 首先,我们允许标准的 logstash 'date' 处理解析(这将产生不正确的结果),然后在下面,我们使用 'ruby' 扩展今天的日期,然后解析结果(得出正确的结果)。

ruby-logstash.conf

input {
    generator {
       message => "02:36.01 /var/log/Service1/myapp.log Ruby is great"
       count => 1
    }
}

filter {
    grok {
        match => { "message" => "%{DATA:justtime} %{DATA:logsource} %{GREEDYDATA:msg}" }
    }

    # incorrectly autopopulates to first day of year
    date {
        match => [ "justtime", "HH:mm.ss" ]
        target => "incorrectfulldatetime"
        timezone => "America/Los_Angeles"
    } # date

    # use ruby to augment with current day
    ruby {
        code => "
        event.set('fulldatetime', Time.now.strftime('%Y-%m-%d') + ' ' + event.get('justtime'))
        "
    }

    date {
        match => [ "fulldatetime", "YYYY-MM-dd HH:mm.ss" ]
        target => "correctfulldatetime"
        # target => "@timestamp"
        timezone => "America/Los_Angeles"
    } # date
}

output {
    stdout {
        codec => rubydebug
    }
}

重新运行  Logstash:

请注意,由于未提供日期部分,因此为一年中的第一天设置的“ invalidfulldatetime” 是2020年1月1日。 并注意“ correctfulldatetime” 如何使用 2020-08-12(当天)。

我们在上面创建了一个名为 “correctfulldatetime” 的新字段,但是,如果你要更新标准 timestamp 字段,则只需把 target 设置为 “@timestamp” 即可。

 

Ruby 解析更为复杂的句子

源日志文件名 “/var/log/Service1/myapp.log” 包含我们要提取的嵌入数据。 父目录 “Service1” 目录指示服务的名称,它可以嵌套在任意深处,并且也需要小写。

虽然这肯定是我们可以使用标准 logstash 正则表达式提取值的情况,但让我们使用 Ruby,因为你可能会遇到这样的情况:标准正则表达式无法覆盖你的需求,或者该表达式过于复杂以至于你需要维护 将问题分解为易于理解的步骤。

因此,我们将以下内容添加到 ruby-logstash.conf 过滤器中:

ruby-logstash.conf

input {
    generator {
       message => "02:36.01 /var/log/Service1/myapp.log Ruby is great"
       count => 1
    }
}

filter {
    grok {
        match => { "message" => "%{DATA:justtime} %{DATA:logsource} %{GREEDYDATA:msg}" }
    }

    # incorrectly autopopulates to first day of year
    date {
        match => [ "justtime", "HH:mm.ss" ]
        target => "incorrectfulldatetime"
        timezone => "America/Los_Angeles"
    } # date

    # use ruby to augment with current day
    ruby {
        code => "
        event.set('fulldatetime', Time.now.strftime('%Y-%m-%d') + ' ' + event.get('justtime'))
        "
    }

    date {
        match => [ "fulldatetime", "YYYY-MM-dd HH:mm.ss" ]
        # target => "correctfulldatetime"
        target => "@timestamp"        
        timezone => "America/Los_Angeles"
    } # date

    # split apart log source to extract service name
    ruby {
        code => "
        fpath = event.get('logsource').split('/')
        event.set('serviceName', fpath[fpath.length-2].downcase)
        "
    }   
}

output {
    stdout {
        codec => rubydebug
    }
}

重新运行 Logstash:

在上面,我们可以看到一个新的字段 servideName 被创建了。

 

Ruby 写入一个本地文件

无论是用于调试,指标还是检测,让 Logstash 作为服务运行将字段值写到本地磁盘都是很有用的。 如果你绝对需要原子性,请使用文件锁。在上面的 ruby-logstash.conf 中的 filer 部分添加:

# append msg field to disk
ruby {
  code => "
    File.open('/tmp/mydebug.log','a') { |f| f.puts event.get('msg') }
  "
}

重新运行 Logstash。我们可以看到如下生产的文件:

$ ls /tmp/mydebug.log 
/tmp/mydebug.log

 

Ruby 调用微服务

如果你正在进行高级集成,则可能需要显示消息 “Ruby is great”,并根据该信息进行某种 REST/Web/network 查找。

为了简单起见,让我们以消息 “Ruby” 的第一个单词为例,并调用一个基于 REST 的 echo 服务,该服务以json格式返回结果。 这是你要添加到过滤器中的代码:

    # call out to REST based echo service
    ruby {
        init => "
        require 'net/http'
        require 'json'
        "
        code => "
        firstWord = event.get('msg').split(' ')[0]
        uri = URI.parse('http://echo.jsontest.com/word/' + firstWord)
        response = Net::HTTP.get_response(uri)
        if response.code == '200'
            result = JSON.parse(response.body)
            returnWord = result['word']
            event.set('echo', firstWord + ' echoed back as: ' + returnWord)
        else
            event.set('echo', 'ERROR reaching web service')
        end
        "
    }

重新运行 Logstash:

在上面,我们可以看到新增加的 echo 字段。

整个最终的 ruby-logstash.conf 的代码如下:

ruby-logstash.conf

input {
    generator {
       message => "02:36.01 /var/log/Service1/myapp.log Ruby is great"
       count => 1
    }
}

filter {
    grok {
        match => { "message" => "%{DATA:justtime} %{DATA:logsource} %{GREEDYDATA:msg}" }
    }

    # incorrectly autopopulates to first day of year
    date {
        match => [ "justtime", "HH:mm.ss" ]
        target => "incorrectfulldatetime"
        timezone => "America/Los_Angeles"
    } # date

    # use ruby to augment with current day
    ruby {
        code => "
        event.set('fulldatetime', Time.now.strftime('%Y-%m-%d') + ' ' + event.get('justtime'))
        "
    }

    date {
        match => [ "fulldatetime", "YYYY-MM-dd HH:mm.ss" ]
        # target => "correctfulldatetime"
        target => "@timestamp"        
        timezone => "America/Los_Angeles"
    } # date

    # split apart log source to extract service name
    ruby {
        code => "
        fpath = event.get('logsource').split('/')
        event.set('serviceName', fpath[fpath.length-2].downcase)
        "
    }

    # append msg field to disk
    ruby {
        code => "
            File.open('/tmp/mydebug.log','a') { |f| f.puts event.get('msg') }
        "
    }

    # call out to REST based echo service
    ruby {
        init => "
        require 'net/http'
        require 'json'
        "
        code => "
        firstWord = event.get('msg').split(' ')[0]
        uri = URI.parse('http://echo.jsontest.com/word/' + firstWord)
        response = Net::HTTP.get_response(uri)
        if response.code == '200'
            result = JSON.parse(response.body)
            returnWord = result['word']
            event.set('echo', firstWord + ' echoed back as: ' + returnWord)
        else
            event.set('echo', 'ERROR reaching web service')
        end
        "
    }
}

output {
    stdout {
        codec => rubydebug
    }
}

 

实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值