背景: nginx 日志写入了ELK中,现需要基于es中的nginx日志,进行业务监控。
要求: 统计5分钟内,某个接口的访问数量,以及5分钟内接口的平均响应时间。
脚本详解:
1、nginx日志里面有timestamp时间的字段,生成符合日志中的时间格式(end_time、start_time)。
2、封装es的连接
3、编写api_search_body
这里面都是es语法
- 查询返回所有结果
- 过滤出5分钟内产生的日志
- 对
api_url
进行分组聚合 - 统计接口的平均响应时间
4、进行参数化
from elasticsearch import Elasticsearch
import time
import sys
#生产es查询需要的时间戳。end_time是当前时间,start_time是5分钟前的时间点。
end_time = time.strftime(
"%Y-%m-%dT%H:%M:%S+08:00",time.localtime(int(time.time()))
)
start_time = time.strftime(
"%Y-%m-%dT%H:%M:%S+08:00",time.localtime(int(time.time()) - 300 )
)
# print("end", end_time)
# print("start", start_time)
# ES 连接
_es = Elasticsearch(["ip:port"],
http_auth=('用户名', '密码'),
# sniff before doing anything
sniff_on_start=True,
# refresh nodes after a node fails to respond
sniff_on_connection_fail=True,
# and also every 60 seconds
sniffer_timeout=60)
# print(_es.info) ping
# print(_es.cat.indices())
#
api_search_body = {
#"_source": ["timestamp","@timestamp","api_url","upstream_time","request_time"] , #显示文档特定的字段
"size": 0, #不显示查询到的文档
"query": {
"bool": {
"must": {"match_all": {} } , # 返回所有结果,不针对特定查询
"filter": { #过滤针对timestamp字段进行过滤,只返回特定时间的结果
"range": {
"timestamp" : {
"gte": start_time,
"lte": end_time
}
}
}
}
},
"aggs": { #以上面条件查询到的数据,进行api_url字段的分组聚合
"group_by_api_url": {
"terms": {
"field": "api_url.keyword" ,
'size': 100
},
"aggs": { #分组后,针对每个分组,统计request_time字段的平均值
"average_request_time": {
"avg": {
"field": "request_time"
}
}
}
}
}
}
#执行es查询
query = _es.search(index='otosaas_qlllog-*',body=api_search_body)
query_data = query['aggregations']['group_by_api_url']['buckets']
#统计接口调用次数
def api_count(url,es_data):
for i in es_data:
if i['key'] == url:
print(int(i['doc_count']))
#统计接口的request_time时间
def api_request_time(url,es_data):
for i in es_data:
if i['key'] == url:
print("%4.3f" % (i['average_request_time']['value']))
# print(i['average_request_time']['value'])
#命令行传参
api_name = sys.argv[1]
func_name = sys.argv[2]
# print(query['hits']['total'])
# print(query['hits']['hits'])
# print(query['aggregations'])
# print(query)
if __name__ == "__main__":
eval(func_name)(api_name,query_data) #命令行传的参数,是字符串。要想当作函数调用,需要加eval