一、了解数据及建模
在生产环境中,nginx日志格式往往使用的是自定义的格式,我们需要把logstash中的message结构化后再存储,方便kibana的搜索和统计,因此需要对message进行解析。
logstash中默认存在一部分正则让我们来使用,可以在$logstash/vendor/bundle/jruby/2.3.0/gems/logstash-patterns-core-4.1.2/patterns
目录里面查看。基本定义在grok-patterns中,我们可以使用其中的正则,当然并不是所有的都适合nginx字段,这时就需要我们自定义正则,然后通过指定patterns_dir
来调用。
logstash自带的grok正则中有Apache的标准日志格式:
# Log formats
HTTPD_COMMONLOG %{IPORHOST:clientip} %{HTTPDUSER:ident} %{HTTPDUSER:auth} \[%{HTTPDATE:timestamp}\] "(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})" %{NUMBER:response} (?:%{NUMBER:bytes}|-)
HTTPD_COMBINEDLOG %{HTTPD_COMMONLOG} %{QS:referrer} %{QS:agent}
# Deprecated
COMMONAPACHELOG %{HTTPD_COMMONLOG}
COMBINEDAPACHELOG %{HTTPD_COMBINEDLOG}
对于nginx标准日志格式,可以发现只是最后多了一个 $http_x_forwarded_for
变量。则nginx标准日志的grok正则定义为:
MAINNGINXLOG %{COMBINEDAPACHELOG} %{QS:x_forwarded_for}
对于自定义格式的日志,只需要在此基础上对对自定义部分进行定义即可。
数据样例:
114.248.161.26 - - [10/Nov/2016:00:01:02 +0800] "POST /api3/getcourseintro HTTP/1.1" 200 2510 "www.imooc.com" "-" cid=283&secrect=86b720f312c2b25da3b20e59e7c89780×tamp=1478707261951&token=4c144b3f4314178b9527d1e91ecc0fac&uid=3372975 "mukewang/5.0.2 (iPhone; iOS 8.4.1;Scale/2.00)" "-" 10.100.136.65:80 200 0.007 0.008
正则匹配:
%{IPORHOST:clientip} %{HTTPDUSER:ident} %{HTTPDUSER:auth} \[%{HTTPDATE:[@metadata][timestamp]}\] "(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})" %{NUMBER:response_status_code} (?:%{NUMBER:bytes}|-) %{QS:hostname} %{QS:referrer} (?:-|%{DATA:params}) %{QS:agent} %{QS:x_forwarded_for} (?:-|%{URIHOST:upstream_host}) (?:-|%{NUMBER:upstream_response_status_code}) (?:-|%{BASE10NUM:upstream_response_time}) %{BASE10NUM:response_time:float}
数据建模
创建nginx访问日志的索引模板
PUT _template/nginx_logs
{
"index_patterns":"nginx_logs*",
"settings": {
"index": {
"number_of_shards": 5,
"number_of_replicas":1,
"refresh_interval":"30s"
}
},
"mappings": {
"doc": {
"dynamic": false,
"properties": {
"@timestamp": {
"type": "date"
},
"@version": {
"type": "keyword",
"ignore_above": 256
},
"agent": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"auth": {
"type": "keyword",
"ignore_above": 256
},
"bytes": {
"type": "long"
},
"clientip": {
"type": "keyword",
"ignore_above": 256
},
"geoip": {
"properties": {
"city_name": {
"type": "keyword",
"ignore_above": 256
},
"continent_code": {
"type": "keyword",
"ignore_above": 256
},
"country_code2": {
"type": "keyword",
"ignore_above": 256
},
"country_code3": {
"type": "keyword",
"ignore_above": 256
},
"country_name": {
"type": "keyword",
"ignore_above": 256
},
"ip": {
"type": "keyword",
"ignore_above": 256
},
"latitude": {
"type": "half_float"
},
"location": {
"type": "geo_point"
},
"longitude": {
"type": "half_float"
},
"region_code": {
"type": "keyword",
"ignore_above": 256
},
"region_name": {
"type": "keyword",
"ignore_above": 256
},
"timezone": {
"type": "keyword",
"ignore_above": 256
}
}
},
"host": {
"type": "keyword",
"ignore_above": 256
},
"hostname": {
"type": "keyword",
"ignore_above": 256
},
"httpversion": {
"type": "keyword",
"ignore_above": 256
},
"ident": {
"type": "keyword",
"ignore_above": 256
},
"params": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"port": {
"type": "keyword",
"ignore_above": 256
},
"referrer": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"referrer_host": {
"type": "keyword",
"ignore_above": 256
},
"request": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"response_status_code": {
"type": "keyword",
"ignore_above": 256
},
"response_time": {
"type": "float"
},
"upstream_host": {
"type": "keyword",
"ignore_above": 256
},
"upstream_response_status_code": {
"type": "keyword",
"ignore_above": 256
},
"upstream_response_time": {
"type": "float"
},
"useragent": {
"properties": {
"build": {
"type": "keyword",
"ignore_above": 256
},
"device": {
"type": "keyword",
"ignore_above": 256
},
"name": {
"type": "keyword",
"ignore_above": 256
},
"os": {
"type": "keyword",
"ignore_above": 256
},
"os_major": {
"type": "keyword",
"ignore_above": 256
},
"os_minor": {
"type": "keyword",
"ignore_above": 256
},
"os_name": {
"type": "keyword",
"ignore_above": 256
}
}
},
"verb": {
"type": "keyword",
"ignore_above": 256
},
"xforwardedfor": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
二、导入数据
input {
file {
path => "/home/wfs/project/imooc_log/access.20161111.log"
#sincedb_path => "/dev/null"
start_position => "beginning"
}
}
filter {
#mutate{add_field => {"[@metadata][debug]"=>true}}
grok {
match => {"message" => '%{IPORHOST:clientip} %{HTTPDUSER:ident} %{HTTPDUSER:auth} \[%{HTTPDATE:[@metadata][timestamp]}\] "(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})" %{NUMBER:response_status_code} (?:%{NUMBER:bytes}|-) %{QS:hostname} %{QS:referrer} (?:-|%{DATA:params}) %{QS:agent} %{QS:x_forwarded_for} (?:-|%{URIHOST:upstream_host}) (?:-|%{NUMBER:upstream_response_status_code}) (?:-|%{BASE10NUM:upstream_response_time}) %{BASE10NUM:response_time:float}'}
#remove_field => ["message"]
}
date {
match => ["[@metadata][timestamp]","dd/MMM/yyyy:HH:mm:ss Z"]
}
geoip {
source => "clientip"
#fields => ["location","country_name","city_name","region_name"]
}
useragent {
source => "agent"
target => "useragent"
}
mutate {
add_field => { "[@metadata][index]" => "nginx_logs-%{+YYYY.MM.dd}"}
}
if [referrer] =~ /^"http/ {
grok{
match => {"referrer" => '%{URIPROTO}://%{URIHOST:referrer_host}'}
}
}
if "_grokparsefailure" in [tags] {
mutate {
replace => { "[@metadata][index]" => "nginx_logs_failure-%{+YYYY.MM.dd}"}
}
}else {
mutate{remove_field=>["message"]}
}
}
output {
if [@metadata][debug]{
stdout{codec=>rubydebug{metadata=>true}}
}else{
stdout{codec=>dots}
elasticsearch {
hosts => [ "192.168.20.101:9200","192.168.20.102:9200" ]
index => "%{[@metadata][index]}"
user => "elastic"
password => "123456"
}
}
}
三、数据实战分析
对日志分析重要的是要知道通过日志我们能能获取到哪些信息要分析什么,这样才能够创建对应的可视化图标进行展示。
以nginx访问日志为例:
nginx访问分析:
- 访问人数?流量?请求QPS?
- 访问来源分布?访问站点分布?访问页面排名?
- 请求响应时间分布?
- 请求响应代码分布?
- 访问地图分布?
业务数据分析:业务数据根据具体业务场景进行分析,例如:
- 访问量最大的是视频还是文章
- 最受欢迎视频、文章有哪些?
- 最努力的用户是谁?兴趣最广泛的用户是谁?
- 用户那个时间段最活跃?
这里只对nginx做访问分析,像访问次数、用户分布啊之类的不做介绍,整理一下在可视化过程中需要注意的一些地方:
1,请求QPS:这里使用的是Timelion
,有个小技巧,这里只需把计算间隔指定到1s即可:
.es(index=nginx_logs*).label(qps).scale_interval(interval=1s)
2,请求响应代码分布情况:使用Visual Builder
,对异常请求做个过滤,然后在分组
!response_status_code:(200 OR 301 OR 304 OR 302 OR 206)
3,响应时间分布:使用热力图
对于响应时间大于1s的请求,只需要添加一个过滤条件即可:response_time:>1
4,流量单位问题:
访问流量时间分布图:在options选项中修改Data Formatter格式为Bytes
即可。
总流量图:使用Metric没有找到单位换算的选项,最后换成了Visual Builder,有个地方需要注意:
5,地图展示:
Coordinate Map
:前提mapping中location字段设置为geoip
类型
Region Map
:需要在kibana的配置文件中配置如下选项:
regionmap:
includeElasticMapsService: true
6,总访问资源数:对request.keyword
的Unique Count
;
总访问用户数 breakdown by city:需要先对clientip
做去重处理;
7,本来想对来源网站做个Tag Cloud,但是第一次导入数据时没有对referrer做进一步的过滤,所以这里只做一个展示,并没有添加到dashboard中。
最后整个nginx日志分析的效果如下所示: