5.1 Logstash简介
Logstash is a tool for managing events and logs. You can use it to collect logs, parse them, and store them for later use (like, for searching).
logstash是一个数据分析软件,主要目的是分析log日志。整一套软件可以当作一个MVC模型,logstash是controller层,Elasticsearch是一个model层,kibana是view层。
首先将数据传给logstash,它将数据进行过滤和格式化(转成JSON格式),然后传给Elasticsearch进行存储、建搜索的索引,kibana提供前端的页面再进行搜索和图表可视化,它是调用Elasticsearch的接口
返回的数据进行可视化。logstash和Elasticsearch是用Java写的,kibana使用node.js框架。
这个软件官网有很详细的使用说明,https://www.elastic.co/,除了docs之外,还有视频教程。这篇博
客集合了docs和视频里面一些比较重要的设置和使用。
5.2 Logstash 安装
直接下载官方发布的二进制包的,可以访问 https://www.elastic.co/downloads/logstash 页面找对应
操作系统和版本,点击下载即可。
在终端中,像下面这样运行命令来启动 Logstash 进程:
# bin/logstash -e 'input{stdin{}}output{stdout{codec=>rubydebug}}'
注意:如果出现如下报错,请调高虚拟机内存容量。
Java HotSpot(TM) 64-Bit Server VM warning: INFO:
os::commit_memory(0x00000000c5330000, 986513408, 0) failed; error='Cannot
allocate memory' (errno=12)
#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (mmap) failed to map 986513408 bytes for committing
reserved memory.
# An error report file with more information is saved as:
# /usr/local/logstash-6.6.2/confs_test/hs_err_pid3910.log
然后你会发现终端在等待你的输入。没问题,敲入 Hello World,回车,{
"@version" => "1",
"host" => "***",
"message" => "hello world",
"@timestamp" => 2019-03-18T02:51:18.578Z
}
每位系统管理员都肯定写过很多类似这样的命令:
cat randdata | awk '{print $2}' | sort | uniq -c | tee sortdata。
Logstash 就像管道符一样!
你输入(就像命令行的 cat )数据,然后处理过滤(就像 awk 或者 uniq 之类)数据,最后输出(就像 tee )到
其他地方。
5.3 Logstash 配置
5.3.1 input配置
读取文件(File)
input {
file {
path => ["/var/log/*.log", "/var/log/message"]
type => "system"
start_position => "beginning"
}
}
output{stdout{codec=>rubydebug}}
有一些比较有用的配置项,可以用来指定 FileWatch 库的行为:
discover_interval
logstash 每隔多久去检查一次被监听的 path 下是否有新文件。默认值是 15 秒。
exclude
不想被监听的文件可以排除出去,这里跟 path 一样支持 glob 展开。
close_older
一个已经监听中的文件,如果超过这个值的时间内没有更新内容,就关闭监听它的文件句柄。默认是
3600 秒,即一小时。
ignore_older
在每次检查文件列表的时候,如果一个文件的最后修改时间超过这个值,就忽略这个文件。默认是
86400 秒,即一天。
sincedb_path
如果你不想用默认的 $HOME/.sincedb(Windows 平台上在
C:\Windows\System32\confifig\systemprofifile.sincedb),可以通过这个配置定义 sincedb 文件到其他
位置。
sincedb_write_interval
logstash 每隔多久写一次 sincedb 文件,默认是 15 秒。
stat_interval
logstash 每隔多久检查一次被监听文件状态(是否有更新),默认是 1 秒。
start_position
logstash 从什么位置开始读取文件数据,默认是结束位置,也就是说 logstash 进程会以类似 tail -F 的
形式运行。如果你是要导入原有数据,把这个设定改成 "beginning",logstash 进程就从头开始读取,
类似 less +F 的形式运行。
启动命令:../bin/logstash -f ./input_file.conf
测试命令:echo 'hehe' >> test.log echo 'hehe2' >> message
标准输入(Stdin)
我们已经见过好几个示例使用 stdin 了。这也应该是 logstash 里最简单和基础的插件了。
input {
stdin {
add_field => {"key" => "value"}
codec => "plain"
tags => ["add"]
type => "std"
}
}
output{stdout{codec=>rubydebug}}
用上面的新 stdin 设置重新运行一次最开始的 hello world 示例。我建议大家把整段配置都写入一个文本文件,然后运行命令:../bin/logstash -f ./input_stdin.conf。输入 "hello world" 并回车后,你会在终端看到如下输出:
{
"message" => "hello world",
"@version" => "1",
"@timestamp" => "2014-08-08T06:48:47.789Z",
"type" => "std",
"tags" => [
[0] "add"
],
"key" => "value",
"host" => "raochenlindeMacBook-Air.local"
}
解释
type 和 tags 是 logstash 事件中两个特殊的字段。通常来说我们会在输入区段中通过 type 来标记事件类型。而 tags 则是在数据处理过程中,由具体的插件来添加或者删除的。
最常见的用法是像下面这样:
input {
stdin {
type => "web"
}
}
filter {
if [type] == "web" {
grok {
match => ["message", %{COMBINEDAPACHELOG}]
}
}
}
output {
if "_grokparsefailure" in [tags] {
nagios_nsca {
nagios_status => "1"
}
} else {
elasticsearch {
}
}
}
5.3.2 codec配置
Codec 是 logstash 从 1.3.0 版开始新引入的概念(Codec 来自 Coder/decoder 两个单词的首字母缩写)。在此之前,logstash 只支持纯文本形式输入,然后以过滤器处理它。但现在,我们可以在输入期处理不同类型的数据,这全是因为有了 codec 设置。所以,这里需要纠正之前的一个概念。Logstash 不只是一个input | fifilter | output 的数据流,而是一个 input | decode | fifilter | encode | output 的数据流!codec 就是用来 decode、encode 事件的。codec 的引入,使得 logstash 可以更好更方便的与其他有自定义数据格式的运维产品共存,比如graphite、flfluent、netflflow、collectd,以及使用 msgpack、json、edn 等通用数据格式的其他产品等。事实上,我们在第一个 "hello world" 用例中就已经用过 codec 了 —— rubydebug 就是一种 codec!
虽然它一般只会用在 stdout 插件中,作为配置测试或者调试的工具。
采用 JSON 编码
在早期的版本中,有一种降低 logstash 过滤器的 CPU 负载消耗的做法盛行于社区(在当时的 cookbook上有专门的一节介绍):直接输入预定义好的 JSON 数据,这样就可以省略掉 fifilter/grok 配置!这个建议依然有效,不过在当前版本中需要稍微做一点配置变动 —— 因为现在有专门的 codec 设置。
配置示例
input {
stdin {
add_field => {"key" => "value"}
codec => "json"
type => "std"
}
}
output{stdout{codec=>rubydebug}}
输入:
{"simCar":18074045598,"validityPeriod":"1996-12-
06","unitPrice":9,"quantity":19,"amount":35,"imei":887540376467915,"user":"test"
}
运行结果:
{
"imei" => 887540376467915,
"unitPrice" => 9,
"user" => "test",
"@timestamp" => 2019-03-19T05:01:53.451Z,
"simCar" => 18074045598,
"host" => "zzc-203",
"amount" => 35,
"@version" => "1",
"key" => "value",
"type" => "std",
"validityPeriod" => "1996-12-06",
"quantity" => 19
}
5.3.3 fifilter配置
Grok插件
logstash拥有丰富的fifilter插件,它们扩展了进入过滤器的原始数据,进行复杂的逻辑处理,甚至可以无中生有的添加新的 logstash 事件到后续的流程中去!Grok 是 Logstash 最重要的插件之一。也是迄今为止使蹩脚的、无结构的日志结构化和可查询的最好方式。Grok在解析 syslog logs、apache and other webserver logs、mysql logs等任意格式的文件上表现完美。这个工具非常适用于系统日志,Apache和其他网络服务器日志,MySQL日志等。
配置:
input {
stdin {
type => "std"
}
}
filter {
grok {
match=>{"message"=> "%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %
{NUMBER:bytes} %{NUMBER:duration}" }
}
}
output{stdout{codec=>rubydebug}}
输入:55.3.244.1 GET /index.html 15824 0.043
输出:
{
"@version" => "1",
"host" => "zzc-203",
"request" => "/index.html",
"bytes" => "15824",
"duration" => "0.043",
"method" => "GET",
"@timestamp" => 2019-03-19T05:09:55.777Z,
"message" => "55.3.244.1 GET /index.html 15824 0.043",
"type" => "std",
"client" => "55.3.244.1"
}
grok模式的语法如下:
%{SYNTAX:SEMANTIC}
SYNTAX:代表匹配值的类型,例如3.44可以用NUMBER类型所匹配,127.0.0.1可以使用IP类型匹配。
SEMANTIC:代表存储该值的一个变量名称,例如 3.44 可能是一个事件的持续时间,127.0.0.1可能是请
求的client地址。所以这两个值可以用 %{NUMBER:duration} %{IP:client} 来匹配。
你也可以选择将数据类型转换添加到Grok模式。默认情况下,所有语义都保存为字符串。如果您希望转换语义的数据类型,例如将字符串更改为整数,则将其后缀为目标数据类型。例如%{NUMBER:num:int}将num语义从一个字符串转换为一个整数。目前唯一支持的转换是int和flfloat。
Logstash附带约120个模式。你可以在这里找到它们https://github.com/logstash-plugins/logstash-patterns-core/tree/master/patterns
自定义类型
更多时候logstash grok没办法提供你所需要的匹配类型,这个时候我们可以使用自定义。创建自定义 patterns 文件。
①创建一个名为patterns其中创建一个文件postfifix (文件名无关紧要,随便起),在该文件中,将需要的
模式写为模式名称,空格,然后是该模式的正则表达式。例如:
POSTFIX_QUEUEID [0-9A-F]{10,11}
②然后使用这个插件中的patterns_dir设置告诉logstash目录是你的自定义模式。
配置:
input {
stdin {
type => "std"
}
}
filter {
grok {
patterns_dir => ["./patterns"]
match => { "message" => "%{SYSLOGBASE} %{POSTFIX_QUEUEID:queue_id}: %
{GREEDYDATA:syslog_message}" }
}
}
output{stdout{codec=>rubydebug}}
输入:
Jan 1 06:25:43 mailserver14 postfix/cleanup[21403]: BEF25A72965: message-id=
<20130101142543.5828399CCAF@mailserver1
输出:
{
"queue_id" => "BEF25A72965",
"message" => "Jan 1 06:25:43 mailserver14 postfix/cleanup[21403]:
BEF25A72965: message-id=<20130101142543.5828399CCAF@mailserver1",
"pid" => "21403",
"program" => "postfix/cleanup",
"@version" => "1",
"type" => "std",
"logsource" => "mailserver14",
"host" => "zzc-203",
"timestamp" => "Jan 1 06:25:43",
"syslog_message" => "message-id=<20130101142543.5828399CCAF@mailserver1",
"@timestamp" => 2019-03-19T05:31:37.405Z
}
GeoIP 地址查询归类
GeoIP 是最常见的免费 IP 地址归类查询库,同时也有收费版可以采购。GeoIP 库可以根据 IP 地址提供对应的地域信息,包括国别,省市,经纬度等,对于可视化地图和区域统计非常有用。
配置:
input {
stdin {
type => "std"
}
}
filter {
geoip {
source => "message"
}
}
output{stdout{codec=>rubydebug}}
输入:183.60.92.253
输出:
{
"type" => "std",
"@version" => "1",
"@timestamp" => 2019-03-19T05:39:26.714Z,
"host" => "zzc-203",
"message" => "183.60.92.253",
"geoip" => {
"country_code3" => "CN",
"latitude" => 23.1167,
"region_code" => "44",
"region_name" => "Guangdong",
"location" => {
"lon" => 113.25,
"lat" => 23.1167
},
"city_name" => "Guangzhou",
"country_name" => "China",
"continent_code" => "AS",
"country_code2" => "CN",
"timezone" => "Asia/Shanghai",
"ip" => "183.60.92.253",
"longitude" => 113.25
}
}
5.3.4 output配置
标准输出(Stdout)
保存成文件(File)
通过日志收集系统将分散在数百台服务器上的数据集中存储在某中心服务器上,这是运维最原始的需求。Logstash 当然也能做到这点。和 LogStash::Inputs::File 不同, LogStash::Outputs::File 里可以使用 sprintf format 格式来自动定义输出到带日期命名的路径。
配置:
input {
stdin {
type => "std"
}
}
output {
file {
path => "../data_test/%{+yyyy}/%{+MM}/%{+dd}/%{host}.log"
codec => line { format => "custom format: %{message}"}
}
}
启动后输入,可看到文件
服务器间传输文件(File)
配置:
接收日志服务器配置:
input {
tcp {
mode => "server"
port => 9600
ssl_enable => false
}
}
filter {
json {
source => "message"
}
}
output {
file {
path => "/home/hduser/app/logstash-6.6.2/data_test/%{+YYYY-MM-dd}/%{servip}-
%{filename}"
codec => line { format => "%{message}"}
}
}
发送日志服务器配置:
input{
file {
path => ["/home/hduser/app/logstash-6.6.2/data_test/send.log"]
type => "ecolog"
start_position => "beginning"
}
}
filter {
if [type] =~ /^ecolog/ {
ruby {
code => "file_name = event.get('path').split('/')[-1]
event.set('file_name',file_name)
event.set('servip','接收方ip')"
}
mutate {
rename => {"file_name" => "filename"}
}
}
}
output {
tcp {
host => "接收方ip"
port => 9600
codec => json_lines
}
}
从发送方发送message,接收方可以看到写出文件。
写入到ES
配置:
input {
stdin {
type => "log2es"
}
}
output {
elasticsearch {
hosts => ["192.168.109.133:9200"]
index => "logstash-%{type}-%{+YYYY.MM.dd}"
document_type => "%{type}"
sniffing => true
template_overwrite => true
}
}
在head插件中可以看到数据。
sniffiffiffing : 寻找其他es节点
实战举例:将错误日志写入es。
配置:
input {
file {
path => ["/usr/local/logstash-6.6.2/data_test/run_error.log"]
type => "error"
start_position => "beginning"
}
}
output {
elasticsearch {
hosts => ["192.168.109.133:9200"]
index => "logstash-%{type}-%{+YYYY.MM.dd}"
document_type => "%{type}"
sniffing => true
template_overwrite => true
}
}
问题:一个错误被分成了多个document。如何解决?