elk处理基础数据
应用程序日志通常包含有价值的数据。 我们如何及时,经济地提取这些数据? 作为一个示例应用程序,我们将讨论一个多租户系统,在该系统中我们通过子域托管多个站点。 日志文件中的URL包含路径(/api, /search, etc)
和参数(?foo=bar)
。
如果我们不想使用ELK ,则可以使用API构建不同的数据处理管道来接收消息,将消息放入队列中,然后由工作人员处理数据。 我在本博文中概述了这种方法,并将其与ELK进行了比较。
在我们的ELK解决方案中,我们将按客户和日期将数据划分为单独的Elasticsearch索引,并生成报告以显示访问了哪些URL路径。 在处理时间序列数据时,这是一种常见的模式。
为简单起见,我们将使用负载平衡器日志,该日志包含与Web服务器日志相同的信息,但它们是集中式的。 我们将配置我们的AWS负载平衡器,每隔五分钟将日志发布到S3存储桶。 从那里,Logstash将提取日志并将其处理到Elasticsearch中。
这是ELB日志文件中的示例行:
2018-05-10T18:26:13.276Z ELB_NAME 73.157.179.139:60708 10.0.1.42:80 0.000021
0.000303 0.000014 200 200 0 68 "GET https://site1.mysystem.com/api?foo=bar...
HTTP/1.1" "Mozilla/5.0 (Linux; Android 7.0; SM-T580 Build/NRD90M; wv)
AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/65.0.3325.109 Safari/537.36 [Pinterest/Android]"
ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2
Logstash配置
我们将从Logstash S3输入插件开始:
# /etc/logstash/conf.d/s3_elastic.conf
input {
s3 {
aws_credentials_file => "./aws_credentials_file.yml"
bucket => "my-elb-logs"
prefix => "subfolder/path/here/"
sincedb_path => "./data/plugins/inputs/s3/sincedb_s3_elastic"
}
}
Logstash使用sincedb
文件来跟踪它在日志文件处理中的位置。 如果我们停止Logstash并在以后启动它,它将处理该停机期间累积的日志。
然后,我们配置Elasticsearch输出插件。 stdout
可用于调试。 我们将在本文后面讨论[@metadata][index]
。
# /etc/logstash/conf.d/s3_elastic.conf
output {
stdout { codec => rubydebug { metadata => true } }
elasticsearch {
hosts => [127.0.0.1]
user => "elastic"
password => "password-here"
index => "%{[@metadata][index]}"
}
}
为了进行过滤,我们将从Grok开始,然后删除不必要的字段:
# /etc/logstash/conf.d/s3_elastic.conf
filter {
grok {
match => { "message" => "%{ELB_ACCESS_LOG}"}
remove_field => [ "elb", "backendip", "backendport", ...]
}
}
Logstash为我们提供了可靠的Grok模式,可将每个日志文件行解析为一个Event对象。 现在我们的数据如下所示:
{
"request" => "http://site1.mysystem.com/api?foo=bar",
"path" => "/api",
"@timestamp" => 2018-05-10T18:26:13.276Z,
"response" => 200,
"clientip" => "73.157.179.139",
"params" => "?foo=bar",
"message" => "...",
...
}
Ruby代码
我们需要实施业务逻辑来验证和转换我们的数据。 鉴于此用例的简单要求,我们可以不用Ruby来完成它,但是它为我们提供了更多的灵活性和控制力。 我们需要提取URL主机,它将用作索引名称的一部分。 我们还想从URL中获取foo
参数。 我们可以从内联Ruby代码开始:
# /etc/logstash/conf.d/s3_elastic.conf
filter
{
ruby
{
code
=>
"
require 'uri'
uri = URI(event.get('request'))
event.set('host', uri.host)
foo_value = CGI::parse(event.get('params'))['foo'].first
event.set('foo', foo_value)
"
}
现在,我们的Event对象包含单独的host
和foo
字段:
{
"request" => "http://site1.mysystem.com/api?foo=bar",
"path" => "/api",
...
"host" => "site1.mysystem.com",
"foo" => "bar",
}
将代码放置在配置文件中不是可扩展的方法,并且将很难测试。 幸运的是,最新版本的Ruby过滤器插件支持从.conf文件中引用单独的Ruby脚本,并且我们可以使用自动化测试来测试代码。 我们通过指定Ruby脚本的路径来修改.conf文件:
# /etc/logstash/conf.d/s3_elastic.conf file
filter
{
ruby
{
path
=>
"/etc/logstash/ruby/s3_elastic.rb"
# script_params => { }
}
}
一个区别是,现在Ruby必须从外部脚本文件返回Event对象的数组。
# /etc/logstash/ruby/s3_elastic.rb
require
'uri'
# the value of `params` is the value of the hash passed to `script_params`
# in the logstash configuration
def register
( params
)
end
# the filter method receives an event and must return a list of events.
# Dropping an event means not including it in the return array,
# while creating new ones only requires you to add a new instance of
# LogStash::Event to the returned array
def filter
( event
)
uri =
URI
( event.
get
(
'request'
)
)
event.
set
(
'host' , uri.
host
)
foo_value =
CGI ::parse
( event.
get
(
'params'
)
)
[
'foo'
] .
first
event.
set
(
'foo' , foo_value
)
return
[ event
]
end
test
'valid test'
do
parameters
{
{
}
}
in_event
do
{
'request'
=>
'http://site1.mysystem.com/api?foo=bar'
}
end
expect
(
'params'
)
do
| events
|
events.
first .
get
(
'host'
) ==
'site1.mysystem.com'
events.
first .
get
(
'foo'
) ==
'bar'
end
end
我们可以通过指定-t标志来运行自动化测试,如下所示: logstash -f /etc/logstash/conf/s3_elastic.conf -t
。
[ logstash.
filters .
ruby .
script
]
Test run complete
{ :script_path
=>
"/etc/logstash/ruby/s3_elastic.rb" ,
:results
=>
{ :passed
=>
1 ,
:failed
=>
0 ,
:errored
=>
0
}
}
Configuration OK
[ logstash.
runner
] Using config.
test_and_exit mode.
Config Validation Result: OK.
Exiting Logstash
我们无法确定当前日期,因此需要确定在索引名称中使用哪个日期。 为此,我们将使用timestamp
字段( 2018-05-10T18:26:13.276Z
)。 我们还可以将用于确定索引的业务逻辑提取到单独的方法中。 万一有错误,我们将默认为今天的日期:
# /etc/logstash/ruby/s3_elastic.rb
def filter
( event
)
...
event .
set
(
"[@metadata][index]" , get_index
( event
)
)
return
[ event
]
end
def get_index event
host = event.
get
(
'host'
)
date = event.
get
(
'timestamp'
) .
split
(
'T'
) .
first
"#{host}-#{date}"
rescue
"#{host}-#{Time.now.strftime("
% Y.
% m.
% d
")}"
end
...
我们正在使用event.set
创建[@metadata][index]
字段。 它不会与文档一起保存,但可以在我们的.conf文件中用于指定索引。 这种方法使我们能够在同一Ruby方法中保持将主机和日期结合在一起的逻辑。
集合体
现在,我们可以使用Kibana(甚至curl )来运行聚合。 我们可以查询所有索引,以了解访问了哪些URL路径以及访问频率。
POST /*/_search?size=0
{
"aggs" : {
"path_count" : {
"terms" : {
"field" : "path.keyword"
}
}
}
}
数据将像这样返回:
{
"took": 709,
"timed_out": false,
"_shards": {
...
},
"hits": {
...
},
"aggregations": {
"path_count": {
...
"buckets": [
{
"key": "/api",
"doc_count": 913281
},
{
"key": "/search",
"doc_count": 742813
},
...
]
}
}
}
如果要查询特定客户或日期的数据,则需要在POST /*2018.05.10/_search?size=0
其指定为索引模式。 Kibana还允许我们基于这些聚合来构建可视化和仪表板。
链接
- https://www.elastic.co/blog/do-you-grok-grok
- https://www.elastic.co/guide/zh-CN/logstash/current/event-api.html
- https://www.elastic.co/guide/zh-CN/logstash/current/plugins-filters-ruby.html
- https://www.elastic.co/blog/moving-ruby-code-out-of-logstash-pipeline
- https://github.com/logstash-plugins/logstash-patterns-core/blob/master/p ...
翻译自: https://opensource.com/article/18/5/building-data-pipeline-elk-stack-ruby
elk处理基础数据