1 背景与说明
日志收集我们主要采用 ELK/EFK 方案,具体为什么采用这种方案本篇不进行更多阐述;本篇主要针对把业务日志进行解析并相关处理后推送到 Elasticsearch
中更方便进行检索与数据统计查看,本篇日志推送源来自于 Docker
的 Fluentd Log Driver
,非 Docker
环境操作的仅作为参考。
2 效果图
3 主要三样插件
3.1 内置 Parser
插件
这个插件在最新的 Fluentd
中已经内置了,直接使用即可。参考地址。
3.2 Elasticsearch
插件
把 Fluentd
收集到的日志信息推到 ES
进行存储,参考地址。
3.3 地理位置插件
根据客户端 IP 地址获取到更多地理相关的信息,这样可以在 kibana
上使用地图与位置的统计图方式更直观查看信息,参考地址。
4 开始
4.1 解析日志数据到对应属性
4.1.1 日志原始数据
以下是 Docker
的输出标准日志数据格式内容。
1
2017-03-03T10:46:21+08:00 1806d6c91569 {"log":"2017-03-03 10:46:21 HKT INFO AccessInterceptor:80 - {time=1488509181152, method=POST, action=/nakedhub/adminx/auth/welcome, locale=zh_CN, cost=4, userId=185713, user-agent=null, header-security-token=null, clientIp=139.196.12.22, clientType=null, params={password=111111, username=maomao}, status=200, paramsOfJson=null}","container_id":"1806d6c91569612cdd746651eeb808591e0176befe565820a339294fc2a9ea0d","container_name":"/nhbackend-st.1.d7r1qsa35zwfxtsmyh5np61ft","source":"stdout"}
4.1.2 核心解析格式配置
1
2
3
4
5
6
7
8
9
<filter **>
@type parser
time_format %Y-%m-%d %H:%M:%S %Z
key_name log
suppress_parse_error_log true
reserve_data true
format /.*(?<time>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} \w+?).+AccessInterceptor(.+time=(?<requesttime>.+?),)(.+method=(?<method>.+?),)(.+action=(?<action>.+?),)(.*locale=(?<locale>.+?),)(.+cost=(?<cost>.+?),)(.*userId=(?<userId>.*?),)(.*agent=(?<user-agent>.*?),)(.+token=(?<token>.*?),)(.+clientIp=(?<clientIp>.+?),)(.+clientType=(?<clientType>.+?),)(.*params={(?<params>.*?)})(.+status=(?<status>.*?),)(.*Json=(?<paramsOfJson>.*?)})/
types cost:integer,status:integer
</filter>
这里我采用 Fluentd
中的 filter
对数据进行过滤处理,主要的格式化配置都在 format
这段配置中,主要采用正则表达式拆分数据到自定义的以 <>
包围起来的属性中,测试表达式格式可以点开在线格式解析表达式,测试完毕后再把表达式配置就好了;另外值得注意的是 time
这个属性值,这个值解析器会自动识别为时间类型,会按照 time_format
配置定义的格式转换至时间类型;key_name
配置在官方文档中并没有提及(但这个配置是必须的),我得出来的结论是这个配置会把外层 JSON
的 log
内容拿出来后再去做解析;types
则是把自定义的属性进行类型指定,推送到 Elasticsearch
中也是相应类型(否则通过 kibana
做统计计算的时候由于不是 Number
类型会有问题);reserve_data
配置则表示原始 log
的内容是否保留到输出项中。
4.1.3 最后日志输出项
1
2017-03-03T10:46:21+08:00 1806d6c91569 {"log":"2017-03-03 10:46:21 HKT INFO AccessInterceptor:80 - {time=1488509181152, method=POST, action=/nakedhub/adminx/auth/welcome, locale=zh_CN, cost=4, userId=185713, user-agent=null, header-security-token=null, clientIp=139.196.12.22, clientType=null, params={password=111111, username=maomao}, status=200, paramsOfJson=null}","container_id":"1806d6c91569612cdd746651eeb808591e0176befe565820a339294fc2a9ea0d","container_name":"/nhbackend-st.1.d7r1qsa35zwfxtsmyh5np61ft","source":"stdout","requesttime":"1488509181152","method":"POST","action":"/nakedhub/adminx/auth/welcome","locale":"zh_CN","cost":4,"userId":"185713","user-agent":"null","token":"null","clientIp":"139.196.12.22","clientType":"null","params":"password=111111, username=maomao","status":200,"paramsOfJson":"null"}
4.2 推送到 ElasticSearch
核心配置
1
2
3
4
5
6
7
8
9
10
11
12
13
<match>
@type elasticsearch
host 10.47.121.12
port 9200
user elastic
password 123456
scheme http
index_name fluentd
type_name fluentd
logstash_format true
logstash_prefix docker
reload_connections false
</match>
由于我的 ES
安装了 XPACK
所以我使用了账号与密码项配置内容,另外注意 password
这里不用使用特殊字符,否则会出现 Fluentd
连接 ElasticSearch
的时候字符编码不对导致认为账户与密码不对的错误问题。
4.3 客户端 IP
地理位置处理
4.3.1 注意
这个 filter
的配置项必须要在以上日志解析的配置项下面,要先解析到了 clientIp
属性,我们才能方便配置从而让插件获取客户端IP地址处理地理位置属性。
4.3.2 核心配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<filter **>
type geoip
geoip_lookup_key clientIp
geoip_database /home/fluent/GeoLiteCity.dat
<record>
latitude ${latitude["clientIp"]}
longitude ${longitude["clientIp"]}
country_code3 ${country_code3["clientIp"]}
country ${country_code["clientIp"]}
country_name ${country_name["clientIp"]}
dma ${dma_code["clientIp"]}
area ${area_code["clientIp"]}
region ${region["clientIp"]}
city ${city["clientIp"]}
location '[${longitude["clientIp"]},${latitude["clientIp"]}]'
</record>
skip_adding_null_record true
log_level info
flush_interval 1s
</filter>
GeoLiteCity.dat
参考GITHUB项目地址去下载,location
会自动转换为 geo_point
类型。
4.3.3 Kibana
类型 geo_point
问题
1
No Compatible Fields: The "xxx" index pattern does not contain any of the following field types: geo_point
当 location
属性值已经存在ES中了就算配置后依然无法自动转成 geo_point
类型,我们需要手动进行处理了,先删除相关的所以索引,然后手动创建类型模板定义 location
的类型,然后创建测试索引查看。
4.3.3.1 创建模板类型
打开 Shell
控制台创建文件 json
,文件内容为以下。
1
2
3
4
5
6
7
8
9
10
{
"template": "docker-*",
"mappings": {
"_default_": {
"properties" : {
"location": { "type": "geo_point"}
}
}
}
}
1
curl -u elastic:123456 -XPUT 'http://10.47.121.12:9200/_template/docker' -d @json
4.3.3.2 创建测试索引
1
curl -u elastic:123456 -XPUT 'http://10.47.121.12:9200/docker-test/fluentd/2' -d '{"host":"8.8.8.8","location":[1.23,4.56]}'
4.3.3.3 查看索引属性类型
1
curl -u elastic:123456 -XGET http://10.47.121.12:9200/docker-test/fluentd/_mapping?pretty
4.3.3.4 原始问题参考地址
https://github.com/y-ken/fluent-plugin-geoip/issues/16
4.3.4 最后日志输出内容
1
2017-03-03T10:46:21+08:00 1806d6c91569 {"log":"2017-03-03 10:46:21 HKT INFO AccessInterceptor:80 - {time=1488509181152, method=POST, action=/nakedhub/adminx/auth/welcome, locale=zh_CN, cost=4, userId=185713, user-agent=null, header-security-token=null, clientIp=139.196.12.22, clientType=null, params={password=111111, username=maomao}, status=200, paramsOfJson=null}","container_id":"1806d6c91569612cdd746651eeb808591e0176befe565820a339294fc2a9ea0d","container_name":"/nhbackend-st.1.d7r1qsa35zwfxtsmyh5np61ft","source":"stdout","requesttime":"1488509181152","method":"POST","action":"/nakedhub/adminx/auth/welcome","locale":"zh_CN","cost":4,"userId":"185713","user-agent":"null","token":"null","clientIp":"139.196.12.22","clientType":"null","params":"password=111111, username=maomao","status":200,"paramsOfJson":"null","latitude":30.29360008239746,"longitude":120.1613998413086,"country_code3":"CHN","country":"CN","country_name":"China","dma":null,"area":null,"region":"02","city":"Hangzhou","location":[120.1613998413086,30.29360008239746]}
5 最后
本篇相关的内容到这里就结束了,有什么问题的话给我留言,我会尽量回复,如果有不正确的地方还望不吝赐教,我会作出相应的改进,避免误导更多人,谢谢。