目录
四、Logstash
1、简介
2、功能
简单来说就是输入-过滤-输出。
3、部署安装
#检查jdk环境,要求jdk1.8+
java -version
#解压安装包
tar -xvf logstash-6.2.2.tar.gz
#第一个logstash示例
bin/logstash -e 'input { stdin { } } output { stdout {} }'
4、接收filebeat输入的日志
接下来,我们将Filebeat和Logstash整合起来,读取nginx的日志。
通过filebeat采集日志输送到logstash经过过滤存储到Elasticsearch
a.安装Nginx
apt install nginx -y
#/usr/sbin/nginx:主程序
#/etc/nginx:存放配置文件
#/usr/share/nginx:存放静态文件
#/var/log/nginx:存放日志
#nginx服务命令
service nginx {start|stop|restart|reload|force-reload|status|configtest|rotate|upgrade}
#通过浏览器访问页面并且查看日志
#访问地址:http://192.168.1.7/
tail -f /var/log/nginx/access.log
b.配置Filebeat
#vim test-nginx.yml
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/nginx/access.log
tags: ["log"]
fields:
from: nginx
fields_under_root: false
output.logstash:
hosts: ["192.168.1.7:5044"]
#启动
./filebeat -e -c test-nginx.yml
#说明:现在启动会报错,因为Logstash还没有启动
c.配置Logstash
vim test-pipeline.conf
#输入如下内容:
input {
beats {
port => "5044"
}
}
# 这里先不启用filter
# filter {
#
# }
output {
stdout { codec => rubydebug }
}
#启动 --config.test_and_exit 用于测试配置文件是否正确
bin/logstash -f test-pipeline.conf --config.test_and_exit
#正式启动 --config.reload.automatic 热加载配置文件,修改配置文件后无需重新启动
bin/logstash -f test-pipeline.conf --config.reload.automatic
测试分别启动Filebeat和Logstash,刷新nginx页面查看输出。
{
"@timestamp" => 2019-01-14T12:23:37.604Z,
"fields" => {
"from" => "nginx"
},
"source" => "/var/log/nginx/access.log",
"tags" => [
[0] "log",
[1] "beats_input_codec_plain_applied"
],
"host" => {
"name" => "itcast01"
},
"beat" => {
"name" => "itcast01",
"version" => "6.2.2",
"hostname" => "itcast01"
},
"@version" => "1",
"offset" => 600,
"message" => "192.168.1.20 - - [14/Jan/2019:20:23:35 +0800] \"GET / HTTP/1.1\"
304 0 \"-\" \"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML,
like Gecko) Chrome/70.0.3538.67 Safari/537.36\"",
"input" => {
"type" => "log"
},
"prospector" => {
"type" => "log"
}
}
#可以看到,已经在控制台输出了nginx的访问日志。
d.配置filter
在前面的输出中,可以看出,虽然可以拿到日志信息,但是信息格式并不友好,比如说,不能直接拿到日志中的ip地址。
# 1、自定义nginx日志格式
vim /etc/nginx/nginx.conf
log_format main '$remote_addr|$time_local|$request|'
'$status|$body_bytes_sent|$http_referer|'
'$http_user_agent|$http_x_forwarded_for|'
'$upstream_addr|$upstream_status|$request_time|$upstream_response_time';
access_log /usr/local/nginx/logs/access.log main;
nginx -s reload
# 2、写nginx-patterns文件
REMOTE_ADDR ^([\d\.]+)
TIME_LOCAL [^\s]+\s\+\d{4}
REQUEST [A-Z]+\s.*\sHTTP/\d\.\d
STATUS \d{3}
BODY_BYTES_SENT \d+
HTTP_REFERER [^\|]*
HTTP_USER_AGENT [^\|]*
HTTP_X_FORWARDED_FOR [\d.]+|-
UPSTREAM_ADDR [^\|]*
UPSTREAM_STATUS [\d]{3}|-
REQUEST_TIME [0-9]*\.[0-9]+
UPSTREAM_RESPONSE_TIME ([0-9]*\.[0-9]+|-)$
NGINX_LOGS %{REMOTE_ADDR:remote_addr}\|%{TIME_LOCAL:time_local}\|%{REQUEST:request}\|%{STATUS:status}\|%{BODY_BYTES_SENT:body_bytes_sent}\|%{HTTP_REFERER:http_referer}\|%{HTTP_USER_AGENT:http_user_agent}\|%{HTTP_X_FORWARDED_FOR:http_x_forwarded_for}\|%{UPSTREAM_ADDR:upstream_addr}\|%{UPSTREAM_STATUS:upstream_status}\|%{REQUEST_TIME:request_time}\|%{UPSTREAM_RESPONSE_TIME:upstream_response_time}
USERHOST [\d\.]+
USERNAME [a-zA-Z]+
#3、修改test-pipeline.conf文件
input {
beats {
port => "5044"
}
}
filter{
grok{
patterns_dir => "/opt/software/elk6.2.2/logstash-6.2.2/nginx-patterns_02"
match => {
"message" => "%{NGINX_LOGS}"
}
add_tag =>["nginx_logs"]
}
}
output {
stdout { codec => rubydebug }
}
#4、测试
{
"input" => {
"type" => "log"
},
"http_referer" => "-",
"remote_addr" => "192.168.1.20",
"fields" => {
"from" => "nginx"
},
"host" => {
"name" => "itcast01"
},
"offset" => 664,
"request" => "GET / HTTP/1.1",
"message" => "192.168.1.20 - - [14/Jan/2019:22:52:49 +0800] \"GET /
HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36\"",
"bytes_sent" => "0",
"status" => "304",
"time_local" => "14/Jan/2019:22:52:49 +0800",
"http_user_agent" => "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
"prospector" => {
"type" => "log"
},
"@version" => "1",
"remote_user" => "-",
"@timestamp" => 2019-01-14T14:52:58.948Z,
"source" => "/var/log/nginx/access.log",
"beat" => {
"name" => "itcast01",
"hostname" => "itcast01",
"version" => "6.2.2"
},
"tags" => [
[0] "log",
[1] "beats_input_codec_plain_applied",
[2] "nginx_access"
]
}
e.logstash grok内置正则过滤规则
USERNAME [a-zA-Z0-9._-]+
USER %{USERNAME}
INT (?:[+-]?(?:[0-9]+))
BASE10NUM (?<![0-9.+-])(?>[+-]?(?:(?:[0-9]+(?:\.[0-9]+)?)|(?:\.[0-9]+)))
NUMBER (?:%{BASE10NUM})
BASE16NUM (?<![0-9A-Fa-f])(?:[+-]?(?:0x)?(?:[0-9A-Fa-f]+))
BASE16FLOAT \b(?<![0-9A-Fa-f.])(?:[+-]?(?:0x)?(?:(?:[0-9A-Fa-f]+(?:\.[0-9A-Fa-f]*)?)|(?:\.[0-9A-Fa-f]+)))\b
POSINT \b(?:[1-9][0-9]*)\b
NONNEGINT \b(?:[0-9]+)\b
WORD \b\w+\b
NOTSPACE \S+
SPACE \s*
DATA .*?
GREEDYDATA .*
QUOTEDSTRING (?>(?<!\\)(?>"(?>\\.|[^\\"]+)+"|""|(?>'(?>\\.|[^\\']+)+')|''|(?>`(?>\\.|[^\\`]+)+`)|``))
UUID [A-Fa-f0-9]{8}-(?:[A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12}
# Networking
MAC (?:%{CISCOMAC}|%{WINDOWSMAC}|%{COMMONMAC})
CISCOMAC (?:(?:[A-Fa-f0-9]{4}\.){2}[A-Fa-f0-9]{4})
WINDOWSMAC (?:(?:[A-Fa-f0-9]{2}-){5}[A-Fa-f0-9]{2})
COMMONMAC (?:(?:[A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2})
IPV6 ((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?
IPV4 (?<![0-9])(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))(?![0-9])
IP (?:%{IPV6}|%{IPV4})
HOSTNAME \b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(\.?|\b)
HOST %{HOSTNAME}
IPORHOST (?:%{HOSTNAME}|%{IP})
HOSTPORT %{IPORHOST}:%{POSINT}
# paths
PATH (?:%{UNIXPATH}|%{WINPATH})
UNIXPATH (?>/(?>[\w_%!$@:.,-]+|\\.)*)+
TTY (?:/dev/(pts|tty([pq])?)(\w+)?/?(?:[0-9]+))
WINPATH (?>[A-Za-z]+:|\\)(?:\\[^\\?*]*)+
URIPROTO [A-Za-z]+(\+[A-Za-z+]+)?
URIHOST %{IPORHOST}(?::%{POSINT:port})?
# uripath comes loosely from RFC1738, but mostly from what Firefox
# doesn't turn into %XX
URIPATH (?:/[A-Za-z0-9$.+!*'(){},~:;=@#%_\-]*)+
#URIPARAM \?(?:[A-Za-z0-9]+(?:=(?:[^&]*))?(?:&(?:[A-Za-z0-9]+(?:=(?:[^&]*))?)?)*)?
URIPARAM \?[A-Za-z0-9$.+!*'|(){},~@#%&/=:;_?\-\[\]]*
URIPATHPARAM %{URIPATH}(?:%{URIPARAM})?
URI %{URIPROTO}://(?:%{USER}(?::[^@]*)?@)?(?:%{URIHOST})?(?:%{URIPATHPARAM})?
# Months: January, Feb, 3, 03, 12, December
MONTH \b(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\b
MONTHNUM (?:0?[1-9]|1[0-2])
MONTHNUM2 (?:0[1-9]|1[0-2])
MONTHDAY (?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9])
# Days: Monday, Tue, Thu, etc...
DAY (?:Mon(?:day)?|Tue(?:sday)?|Wed(?:nesday)?|Thu(?:rsday)?|Fri(?:day)?|Sat(?:urday)?|Sun(?:day)?)
# Years?
YEAR (?>\d\d){1,2}
HOUR (?:2[0123]|[01]?[0-9])
MINUTE (?:[0-5][0-9])
# '60' is a leap second in most time standards and thus is valid.
SECOND (?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?)
TIME (?!<[0-9])%{HOUR}:%{MINUTE}(?::%{SECOND})(?![0-9])
# datestamp is YYYY/MM/DD-HH:MM:SS.UUUU (or something like it)
DATE_US %{MONTHNUM}[/-]%{MONTHDAY}[/-]%{YEAR}
DATE_EU %{MONTHDAY}[./-]%{MONTHNUM}[./-]%{YEAR}
ISO8601_TIMEZONE (?:Z|[+-]%{HOUR}(?::?%{MINUTE}))
ISO8601_SECOND (?:%{SECOND}|60)
TIMESTAMP_ISO8601 %{YEAR}-%{MONTHNUM}-%{MONTHDAY}[T ]%{HOUR}:?%{MINUTE}(?::?%{SECOND})?%{ISO8601_TIMEZONE}?
DATE %{DATE_US}|%{DATE_EU}
DATESTAMP %{DATE}[- ]%{TIME}
TZ (?:[PMCE][SD]T|UTC)
DATESTAMP_RFC822 %{DAY} %{MONTH} %{MONTHDAY} %{YEAR} %{TIME} %{TZ}
DATESTAMP_RFC2822 %{DAY}, %{MONTHDAY} %{MONTH} %{YEAR} %{TIME} %{ISO8601_TIMEZONE}
DATESTAMP_OTHER %{DAY} %{MONTH} %{MONTHDAY} %{TIME} %{TZ} %{YEAR}
DATESTAMP_EVENTLOG %{YEAR}%{MONTHNUM2}%{MONTHDAY}%{HOUR}%{MINUTE}%{SECOND}
# Syslog Dates: Month Day HH:MM:SS
SYSLOGTIMESTAMP %{MONTH} +%{MONTHDAY} %{TIME}
PROG (?:[\w._/%-]+)
SYSLOGPROG %{PROG:program}(?:\[%{POSINT:pid}\])?
SYSLOGHOST %{IPORHOST}
SYSLOGFACILITY <%{NONNEGINT:facility}.%{NONNEGINT:priority}>
HTTPDATE %{MONTHDAY}/%{MONTH}/%{YEAR}:%{TIME} %{INT}
# Shortcuts
QS %{QUOTEDSTRING}
# Log formats
SYSLOGBASE %{SYSLOGTIMESTAMP:timestamp} (?:%{SYSLOGFACILITY} )?%{SYSLOGHOST:logsource} %{SYSLOGPROG}:
COMMONAPACHELOG %{IPORHOST:clientip} %{USER:ident} %{USER:auth} \[%{HTTPDATE:timestamp}\] "(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})" %{NUMBER:response} (?:%{NUMBER:bytes}|-)
COMBINEDAPACHELOG %{COMMONAPACHELOG} %{QS:referrer} %{QS:agent}
# Log Levels
LOGLEVEL ([Aa]lert|ALERT|[Tt]race|TRACE|[Dd]ebug|DEBUG|[Nn]otice|NOTICE|[Ii]nfo|INFO|[Ww]arn?(?:ing)?|WARN?(?:ING)?|[Ee]rr?(?:or)?|ERR?(?:OR)?|[Cc]rit?(?:ical)?|CRIT?(?:ICAL)?|[Ff]atal|FATAL|[Ss]evere|SEVERE|EMERG(?:ENCY)?|[Ee]merg(?:ency)?)
f.发送到Elasticsearch
#vim test-pipeline.conf
input {
beats {
port => "5044"
}
}
filter {
grok {
patterns_dir => "/opt/software/logstash-6.2.2/nginx-patterns"
match => { "message" => "%{NGINX_ACCESS}"}
remove_tag => [ "_grokparsefailure" ]
add_tag => [ "nginx_access" ]
}
}
#output {
# stdout { codec => rubydebug }
#}
output {
elasticsearch {
hosts => [ "192.168.1.7:9200","192.168.1.7:9201","192.168.1.7:9202" ]
}
}
5、接收JSON格式的日志输出
a、修改nginx的日志格式
log_format json '{"@timestamp":"$time_iso8601",'
'"@version":"1",'
'"client":"$remote_addr",'
'"url":"$uri",'
'"status":"$status",'
'"domain":"$host",'
'"host":"$server_addr",'
'"size":"$body_bytes_sent",'
'"responsentime":"$request_time",'
'"referer":"$http_referer",'
'"useragent":"$http_user_agent"'
'}';
access_log /usr/local/nginx/logs/access_json.log json;
b、修改filtbeat配置
filebeat.prospectors:
- type: log
enabled: true
paths:
- /usr/local/nginx/logs/access_json.log
fields:
filetype: log_nginx
fields_under_root: true
setup.template.settings:
index.number_of_shards: 3
output.logstash:
hosts: ["192.168.179.128:5044"]
c、修改logstash的配置
input {
beats {
port => "5044"
}
}
filter {
json {
source => "message" # 将message中的键值对进行拆分
remove_field => ["message"]
}
}
output {
stdout { codec => rubydebug }
}
d、启动logstash、filebeat测试访问nginx
#输出如下
{
"filetype" => "log_nginx",
"beat" => {
"version" => "6.2.2",
"name" => "localhost.localdomain",
"hostname" => "localhost.localdomain"
},
"tags" => [
[0] "beats_input_codec_plain_applied"
],
"url" => "/es",
"domain" => "192.168.179.128",
"useragent" => "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
"prospector" => {
"type" => "log"
},
"client" => "192.168.179.1",
"size" => "435",
"source" => "/usr/local/nginx/logs/access_json.log",
"status" => "200",
"offset" => 3380,
"host" => "192.168.179.128",
"referer" => "-",
"@version" => "1",
"responsentime" => "0.006",
"@timestamp" => 2020-11-06T07:24:32.000Z
}
6、自定义Template模板配置
{
"template": "nginx-json*",
"version": 50001,
"settings": {
"index.refresh_interval": "5s"
},
"mappings": {
"_default_": {
"_all": {
"enabled": true,
"norms": false
},
"dynamic_templates": [
{
"message_field": {
"path_match": "message",
"match_mapping_type": "string",
"mapping": {
"type": "text",
"norms": false
}
}
},
{
"string_fields": {
"match": "*",
"match_mapping_type": "string",
"mapping": {
"type": "text",
"norms": false,
"analyzer": "ik_max_word",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
],
"properties": {
"@timestamp": {
"type": "date",
"include_in_all": false
},
"@version": {
"type": "keyword",
"include_in_all": false
}
}
}
}
}
在logstash的配置文件中加入template配置
input {
beats {
port => "5044"
}
}
filter {
json {
source => "message" # 将message中的键值对进行拆分
remove_field => ["message"]
}
}
output {
if [filetype] == "log_nginx"{
elasticsearch{
hosts=>["192.168.179.128:9200"]
index => "nginx_json_%{+YYYY-MM-dd}.log"
template_overwrite => true
template => "/opt/software/elk6.2.2/logstash-6.2.2/test-nginx-dynamic-template.json"
template_name => "ngins-json*"
}
}
}