Filebeat7 Kafka Gunicorn Flask Web应用程序日志采集
本文的内容
- 如何用filebeat kafka es做一个好用,好管理的日志收集工具
- 放弃logstash,使用elastic pipeline
- gunicron日志格式与filebeat/es配置
- flask日志格式与异常日志采集与filebeat/es配置
- 以上的配置
概况
我有一个HTTP请求,经过的路径为
Gateway(kong)-->WebContainer(gunicorn)-->WebApp(flask)
我准备以下流向处理我的日志
file --> filebeat --> kafka topic--> filebeat --> elastic pipeline --> elasticsearch
|
| ----------> HBase
为什么这么做
Logstash去哪里了?
- Logstash太重了,不过这不是问题,也就是多个机器加点钱的问题。能把事情处理就行。
- Logstash不美,Logstash虽然是集中管理配置,但是一个logstash好像总是不够,Logstash好像可以分开配置,但是你永远不知道如何划分哪些配置应该放在一个配置文件,哪些应该分开。
- 删除一个配置?不可能的,我怎么知道应该删除什么配置。
- 如果用了Logstash. As a 'poor Ops guys having to understand and keep up with all the crazy input possibilities. _
Filebeat的痛处
- 看看这个Issue吧, 万人血书让filebeat支持
grok, 但是就是不支持,不过给了我们两条路,比如你可以用存JSON的日志啊, 或者用pipeline - Filebeat以前是没有一个好的kafka-input。只能自己写kafka-es的转发工具
简单点
我想要的日志采集就是简简单单,或者说微服务的内聚力。 一条日志采集线就不该和其他业务混合。最好的就是以下这种状态
onefile -> filebeat_config -> kafka_topic -> filebeat_config -> elastic pipepline -> es index
Gunicorn日志
gunicorn日志
gunicorn日志采集如下的信息
- time
- client_ip
- http method
- http scheme
- url
- url query string
- response status code
- client name
- rt
- trace id
- remote ips
日志格式
%(t)s [%(h)s] [%(m)s] [%(H)s] [%(U)s] [%(q)s] [%(s)s] [%(a)s] [%(D)s] [%({Kong-Request-ID}i)s] [%({X-Forwarded-For}i)s]
日志例子
[15/Nov/2019:10:23:37 +0000] [172.31.37.123] [GET] [HTTP/1.1] [/api/v1/_instance/json_schema/Team/list] [a=1] [200] [Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36] [936] [9cbf6a3b-9c3a-4835-a2ef-02e03ee826d7#16] [137.59.103.3, 172.30.17.253, 172.30.18.12]
Es processing解析
es processing是6.0之后的功能,相当于es之前自带了一个logstash.对于复杂日志有多种processing,
可以使用grok或者dissect.某些情况下dissect更加快一些.
经过kafka,再有filebeat打到ES, 需要删除多余的信息
PUT _ingest/pipeline/gunicorn
{
"description" : "devops gunicorn pipeline",
"processors" : [
{
"remove": {"field": ["agent", "ecs", "host", "input", "kafka"]}
},
{
"json": {
"field": "message",
"add_to_root": true
}
},
{
"remove": {"field": ["@metadata", "ecs", "agent", "input"]}
},
{
"dissect" : {
"field": "message",
"pattern": "[%{@timestamp}] [%{client_ip}] [%{method}] [%{scheme}] [%{path}] [%{query_string}] [%{status}] [%{client}] [%{rt_millo}] [%{trace_id}] [%{remote_ips}]"
}
}
],
"on_failure": [
{
"set": {
"field": "_index",
"value": "failed-{{ _index }}"
}
}
]
}
Es mapping
这里比较关键的是ES时间格式文档的定义, 如果某些字段我们觉得有必要分词,就是用text。否则使用keyword。这样可以更加
方便的聚合和查询日志数据, 开启_source方便做一些数据统计
PUT _template/gunicorn
{
"index_patterns": ["*gunicorn*"],
"settings": {
"number_of_shards": 1
},
"version": 1,
"mappings": {
"_source": {
"enabled": true
},
"properties": {
"@timestamp": {
"type": "date",
"format": "dd/LLL/yyyy:HH:mm:ss Z"
},
"client_ip": {
"type": "ip"
},
"method": {
"type": "keyword"
},
"scheme": {
"type": "keyword"
},
"path": {
"type": "text"
},
"query_string": {
"type": "text"
},
"status": {
"type": "integer"
},
"client": {
"type": "text"
},
"rt_millo": {
"type": "long"
},
"trace_id": {
"type": "keyword"
},
"remote_ips": {
"type": "text"
}
}
}
}
filebeat 采集到kafka配置文件
filebeat.inputs:
- type: log
paths:
- /yourpath/gunicorn-access.log
multiline.pattern: '^\['
multiline.negate: true
multiline.match: after
tail_files: true
queue.mem:
events: 4096
flush.min_events: 512
flush.timeout: 5s
output.kafka:
hosts: ["kafka-01","kafka-02","kafka-03"]
topic: 'gunicron_access'
required_acks: 1
compression: gzip
max_message_bytes: 1000000
filebeat 从kafka消费配置文件
filebeat.inputs:
- type: kafka
hosts: ["kafka-01","kafka-02","kafka-03"]
topics: ["gunicron_access"]
group_id: "filebeat_gunicron"
output.elasticsearch:
hosts: ["es-url"]
pipeline: "gunicorn"
index: "gunicorn-%{+yyyy.MM.dd}"
setup.template.name: "gunicorn"
setup.template.pattern: "gunicorn-*"
setup.ilm.enabled: false
setup.template.enabled: false
Flask日志
Flask日志是我们程序打印的,用于查看一些异常和错误的日志。在上线初期,info日志是可以打开debug的日志的。这样方便我们进行调试。
在稳定之后应该将日志接受级别调高。info日志不适合做统计,只是除了问题我们可以快速定位问题所在。 异常应该打到info日志中
INFO日志可以使用我建议的格式。我们关心
- time
- levelname: 日志级别
- host, process, thread: 用于定位到某台机器的某个进程下的某个线程(一些复杂的bug需要,或者开启了异步进程)
- name, funcname, filename, lineno: 用于定位日志发生的代码位置
- message: 日志内容
日志格式
{
"format": "[%(asctime)s.%(msecs)03d] [%(levelname)s] [{}:%(process)d:%(thread)d] [%(name)s:%(funcName)s] [%(filename)s:%(lineno)d] %(message)s".format(HOST),
"datefmt": "%Y-%m-%d %H:%M:%S"
}
日志例子
[2019-11-18 08:47:49.424] [INFO] [cmdb-008069:5990:140482161399552] [cmdb:execute_global_worker] [standalone_scheduler.py:116] RUN_INFO: tiny_collector_ali starting at 2019-11-18 08:47:49, next run will be at approximately 2019-11-18 09:47:49
[2019-11-18 08:11:27.715] [ERROR] [cmdb-008069:5985:140184204932928] [cmdb:common_handler] [error.py:48] 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
Traceback (most recent call last):
File "/home/server/venv3/lib/python3.6/site-packages/flask/app.py", line 1805, in full_dispatch_request
rv = self.dispatch_request()
File "/home/server/venv3/lib/python3.6/site-packages/flask/app.py", line 1783, in dispatch_request
self.raise_routing_exception(req)
File "/home/server/venv3/lib/python3.6/site-packages/flask/app.py", line 1766, in raise_routing_exception
raise request.routing_exception
File "/home/server/venv3/lib/python3.6/site-packages/flask/ctx.py", line 336, in match_request
self.url_adapter.match(return_rule=True)
File "/home/server/venv3/lib/python3.6/site-packages/werkzeug/routing.py", line 1799, in match
raise NotFound()
werkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
Es processing解析
经过kafka,再有filebeat打到ES, 需要删除多余的信息
PUT _ingest/pipeline/info
{
"description" : "devops info pipeline",
"processors" : [
{
"remove": {"field": ["agent", "ecs", "host", "input", "kafka"]}
},
{
"json": {
"field": "message",
"add_to_root": true
}
},
{
"remove": {"field": ["@metadata", "ecs", "agent", "input"]}
},
{
"dissect" : {
"field": "message",
"pattern": "[%{@timestamp}] [%{level}] [%{host}:%{process_id}:%{thread_id}] [%{name}:%{func_name}] [%{file}:%{line_no}] %{content}"
}
}
],
"on_failure": [
{
"set": {
"field": "_index",
"value": "failed-{{ _index }}"
}
}
]
}
Es mapping
thread_id 要给一个long字段, python如果获取不到会给一个超出integer范围的数字
PUT _template/info
{
"index_patterns": ["*info*"],
"settings": {
"number_of_shards": 1
},
"version": 1,
"mappings": {
"_source": {
"enabled": true
},
"properties": {
"@timestamp": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss.SSS"
},
"level": {
"type": "keyword"
},
"host": {
"type": "keyword"
},
"process_id": {
"type": "integer"
},
"thread_id": {
"type": "long"
},
"name": {
"type": "keyword"
},
"func_name": {
"type": "keyword"
},
"file": {
"type": "keyword"
},
"line_no": {
"type": "integer"
},
"content": {
"type": "text"
}
}
}
}
filebeat 采集到Kafka配置文件
这里采用^\[20\d{2}来区分行首
filebeat.inputs:
- type: log
paths:
- /you_path/app.log
multiline.pattern: '^\[20\d{2}'
multiline.negate: true
multiline.match: after
tail_files: true
queue.mem:
events: 4096
flush.min_events: 512
flush.timeout: 5s
output.kafka:
hosts: ["kafka-01", "kafka-02", "kafka-03"]
topic: 'devops_app'
required_acks: 1
compression: gzip
max_message_bytes: 1000000
filebeat 从kafka消费配置文件
filebeat.inputs:
- type: kafka
hosts: ["kafka-01", "kafka-02", "kafka-03"]
topics: ["devops_app"]
group_id: "filebeat_app"
output.elasticsearch:
hosts: ["es_url"]
pipeline: "info"
index: "app-info-%{+yyyy.MM.dd}"
setup.template.name: "info"
setup.template.pattern: "app-info-*"
setup.ilm.enabled: false
setup.template.enabled: false
Filebeat7 Kafka Gunicorn Flask Web应用程序日志采集的更多相关文章
- 基于Kafka的服务端用户行为日志采集
本文来自网易云社区 作者:李勇 背景 随着互联网的不断发展,用户所产生的行为数据被越来越多的网站重视,那么什么是用户行为呢?所谓的用户行为主要由五种元素组成:时间.地点.人物.行为.行为对应的内容.为 ...
- web应用程序 ---- 日志系统的设计
最近在做一个小的项目,是web的应用程序,最近也有点时间,把日志管理来简单的说说. 日志,就是需要记录一些自己感兴趣的信息,把它保存起来,具体保存在哪里?保存多长时间?这些要求都是根据不同的项目需求而 ...
- kafka和flume进行整合的日志采集的confi文件编写
配置flume.conf 为我们的source channel sink起名 a1.sources = r1 a1.channels = c1 a1.sinks = k1 指定我们的source收集到 ...
- 如何使用Nginx和uWSGI或Gunicorn在Ubuntu上部署Flask Web应用
你好!欢迎阅读我的博文,你可以跳转到我的个人博客网站,会有更好的排版效果和功能. 此外,本篇博文为本人Pushy原创,如需转载请注明出处:https://pushy.site/posts/151981 ...
- 转载 Flask中客户端 - 服务器 - web应用程序 是如何处理request生成response的?
文章转载自https://blog.csdn.net/weixin_37923128/article/details/80992645 , 感谢原作者 当客户端向服务器发送一个请求时,服务器会将请求转 ...
- 使用Log4j将程序日志实时写入Kafka(转)
原文链接:使用Log4j将程序日志实时写入Kafka 很多应用程序使用Log4j记录日志,如何使用Kafka实时的收集与存储这些Log4j产生的日志呢?一种方案是使用其他组件(比如Flume,或者自己 ...
- 一寸宕机一寸血,十万容器十万兵|Win10/Mac系统下基于Kubernetes(k8s)搭建Gunicorn+Flask高可用Web集群
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_185 2021年,君不言容器技术则已,欲言容器则必称Docker,毫无疑问,它是当今最流行的容器技术之一,但是当我们面对海量的镜像 ...
- nginx+supervisor+gunicorn+flask
一. 更新系统 #yum -y install epel-release #yum clean all && yum makecache #yum -y update 二.安装pyth ...
- 基于Flume+LOG4J+Kafka的日志采集架构方案
本文将会介绍如何使用 Flume.log4j.Kafka进行规范的日志采集. Flume 基本概念 Flume是一个完善.强大的日志采集工具,关于它的配置,在网上有很多现成的例子和资料,这里仅做简单说 ...
随机推荐
- 优雅的退出asyncio事件循环
import asyncio import functools import os import signal """ 信号值 符号 行为 2 SIGINT 进程终端,C ...
- C++入门经典-例9.4-默认模板参数
1:默认模板参数就是在类模板定义时设置类型形式参数表中的一个类型参数的默认值,该默认值是一个数据类型.有了默认的数据类型参数后,在定义模板的新类型时就可以不进行指定.代码如下: // 9.4.cpp ...
- If表达式 kotlin(8)
If表达式 在 Kotlin 中, if 是一个表达式,即它会返回一个值. 因此就不需要三元运算符(条件 ? 然 后 : 否则) ,因为普通的 if 就能胜任这个角色. // 传统用法 var max ...
- LC 539. Minimum Time Difference
Given a list of 24-hour clock time points in "Hour:Minutes" format, find the minimum minut ...
- 实用的60个CSS代码片段[上]
1.垂直对齐 如果你用CSS,则你会有困惑:我该怎么垂直对齐容器中的元素?现在,利用CSS3的Transform,可以很优雅的解决这个困惑: .verticalcenter{ position: re ...
- Linux特点
开放性 多用户 多任务 丰富的网络功能 可靠的系统安全 良好的可移植性 具有标准兼容性 良好的用户界面(命令界面,图形界面等) 出色的速度性能.
- Linux安全加固之中间件Tomcat
(注:皆为参考操作配置) 这次是tomcat的Linux加固,分为身份鉴别.访问控制.安全审计.资源控制和入侵防范5个方面大部分加固基于xml配置文件进行修改,也应根据实际需求制定方案.寻找配置文件目 ...
- iOS实现渐变颜色
下面是我的两种实现: 1.直接图片展示,注意图片的变形问题; 2.用CAGradientLayer渐变颜色实现; 代码如下: // // ViewController.m // ImageStrenc ...
- ASP.NET(C#)事务的创建、提交以及回滚 (附代码)
1.事务是什么? 事务是应用程序中一系列严密的操作,所有的操作必须全部成功完成,否则每个操作中的所有更改都会被撤销.也就是事务具有原子性,一个事务中的一系列操作要么全部成功,要么 ...
- Spring MVC 根容器和子容器
整合 spring mvc 根容器和子容器 public class TestWebInitializer extends AbstractAnnotationConfigDispatcherServ ...