阿里云的接口监控
原因:了解每个服务的调用量以及该服务下每个接口的调用量和失败量,展示出来
技术栈:
后端:djargo+阿里云的sdk(使用方式详见阿里云对外提供的sdk下载方式https://help.aliyun.com/document_detail/29077.html?spm=a2c4g.11186623.6.1326.643a9951bybOLt)
前端:layui+js+echarts(画图js)
设计思路:
1.拿到业务中台所有项目,以及每个项目所包含的接口(接口数据来源yapi)
2.了解监控服务的阿里云路径
3.通过日志聚类的方式去查询获取每日的调用总量以及每个接口的调用量和失败量(失败量如何定义根据组内开发者敲定,我这只统计500)
4.日志聚类的查询语句,详情见帮助文档中的日志服务-日志查询语句
5.日志聚类涉及的查询语句"* | SELECT _container_name_,path, response,status,method,COUNT(*) as number WHERE status in ( '499' , '599') GROUP BY _container_name_,method,path,response,status ORDER BY number limit 1000” ,查询语句中的_container_name_,path等都是来源查询分析属性(属性是由运维创建,可以通过阿里云的sdk中get_index_config接口获取
)
6.通过查询语句获取到每日调用量以及失败量后,直接存放数据库(数据库一张表包含查询语句中的字段以及调用量和失败调用量)
代码实现:
1.获取阿里云的访问密钥:
from aliyun.log import LogClient
def conn()
endpoint = 'cn-hangzhou.log.aliyuncs.com' #日志服务的域名。更多信息,请参见服务入口。此处以杭州为例,其它地域请根据实际情况填写。
accessKeyId = 'your_access_id' #阿里云访问密钥AccessKey ID。更多信息,请参见访问密钥。阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日 常运维。
accessKey = 'your_access_key' #阿里云访问密钥AccessKey Secret。
client = LogClient(endpoint, accessKeyId, accessKey) #创建LogClient。
return client
2.封装get_logs接口,通过项目名称project,服务名称store,查询语句queryString,开始时间startTime,结束时间endTime去查询
def GetLog(project,store,queryString,startTime,endTime):
client=conn()
api_request = GetLogsRequest(project, store, fromTime=startTime, toTime=endTime, topic='', query=queryString, offset=0,reverse=False)
res = client.get_logs(api_request)
result=[]
for i in res.get_body():
result.append(i)
return result
3.通过每个接口的调用量获取
def GetApiLog(url,ProjectName,LogStoreName,startTime,endTime):
'''
:param url: 服务的所有接口
:param ProjectName:
:param LogStoreName:
:param queryString:
:param startTime:
:param endTime:
:return:
'''
whereSql = "where routePath LIKE"
queryString = "* | select _container_name_,routePath,count(*) as num " + whereSql + "'" + url +"' GROUP BY _container_name_,method,routePath ORDER BY num limit 1000"
res = GetLog(project=ProjectName, store=LogStoreName, startTime=startTime,
endTime=endTime,
queryString=queryString)
failedqueryString= "* | select _container_name_,routePath,response,status,count(*) as num " + whereSql + "'" + url +"' and status in ( '400' , '599') GROUP BY _container_name_,routePath,response,status ORDER BY num limit 1000"
failedres = GetLog(project=ProjectName, store=LogStoreName, startTime=startTime,
endTime=endTime,
queryString=failedqueryString)
dic={}
if res:
if failedres:
dic["_container_name_"] = res[0].get("_container_name_")
dic["routePath"] = res[0].get("routePath")
dic["failedresponse"] = failedres[0].get("response")
dic["num"] = res[0].get("num")
dic["__source__"] = ""
dic["__time__"] = res[0].get("__time__")
dic["failednum"] = failedres[0].get("num")
else:
dic["_container_name_"]=res[0].get("_container_name_")
dic["routePath"] = res[0].get("routePath")
dic["num"] = res[0].get("num")
dic["__source__"] = ""
dic["__time__"] = res[0].get("__time__")
dic["failednum"] = '0'
else:
dic["_container_name_"] = LogStoreName.split('-')[-1]
dic["routePath"]=url
dic["num"] = '0'
dic["__source__"] = ""
dic["__time__"] = str(int(startTime))
dic["failednum"] = '0'
return dic
4.前端直接展示
{% load static %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>线上接口监控信息</title>
<!-- 引入 echarts.js -->
<link rel="stylesheet" href="{% static 'css/layui.css' %}">
<script src="https://cdn.bootcdn.net/ajax/libs/echarts/5.0.1/echarts.min.js"></script>
</head>
<body οnlοad='init()'>
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div>
<h2>线上接口监控信息</h2>
</div>
<form class="layui-form" action="">
<div class="layui-form-item">
<label class="layui-form-label">请输入时间</label>
<div class="layui-input-inline">
<input type="text" name="stdate" lay-verify="title" autocomplete="off" placeholder="YYYY-MM-DD" class="layui-input">
</div>
<button type="submit" class="layui-btn" lay-submit="" lay-filter="input">导入</button>
</div>
</form>
<div class="layui-form-item">
<div class="layui-inline" id="main" style="width: 1000px;height:400px;"></div>
</div>
<div class="layui-form-item">
<div class="layui-inline" id="apidaynum" style="width: 1000px;height:600px;"></div>
<div class="layui-inline" id="dev" style="width: 600px;height:400px;"></div>
</div>
<script src="{% static 'layui.js' %}"></script>
<script src="{% static 'jquery-3.4.1.min.js' %}"></script>
<script>
layui.use(['form', 'table','layedit', 'laydate'], function(){
var form = layui.form
,layer = layui.layer
,layedit = layui.layedit
,table = layui.table
,laydate = layui.laydate;
//日期
laydate.render({
elem: '#date'
});
laydate.render({
elem: '#date1'
})
//$(document).ready(function(){}初始化加载
$(document).ready(function(){
$.get('/api/getApitotalNum/',function (res){
var dataAxis =res.data;
var nums=[]
var dates=[]
for (var i=0;i<dataAxis.length;i++)
{
nums.push(dataAxis[i].num)
dates.push(dataAxis[i].time)
}
var chartDom = document.getElementById('main');
var myChart = echarts.init(chartDom);
var option;
option = {
title: {
text: ''
},
tooltip: {
trigger: 'axis',
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
data: dates.slice(-5),
axisTick: {
alignWithLabel: true
},
axisLabel: {
interval:0,
rotate:40
},
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
name: 'Apitotal',
type: 'line',
data: nums
}
]
};
option && myChart.setOption(option);
//通过click事件触发当天每个接口的调用量以及失败调用量
myChart.on('click',function (params){
var timesjson={"times":params.name};
$.post('/api/getApiError/',timesjson,function (data){
var dataAxis =data.data;
var nums=[]
var failednums=[]
var dates=[]
var apis=[]
for (var i=0;i<dataAxis.length;i++)
{
if(dataAxis[i].number !='0'){
nums.push(dataAxis[i].number)
dates.push(dataAxis[i].time)
failednums.push(dataAxis[i].failednum)
apis.push(dataAxis[i].path)
}
}
var chartappbugs = document.getElementById('apidaynum');
var myChartappbugs = echarts.init(chartappbugs);
var bugsoption;
bugsoption = {
title: {
text: '每个接口的调用量以及失败调用量'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['调用总量','失败调用量']
},
grid: {
left: '3%',
right: '4%',
bottom: '35%',
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'category',
data: apis,
boundaryGap:false,
axisLabel:{
interval:0,
margin: 5,
rotate:30
},
},
yAxis: {
type: 'value'
},
series: [
{
name: '调用总量',
type: 'line',
data: nums
},
{
name: '失败调用总量',
type: 'line',
data: failednums
}
]
};
bugsoption && myChartappbugs.setOption(bugsoption);
})
})
})
return false;
});
form.on('submit(input)', function(data){
var index = layer.load(1);
var stdate=data.field.stdate;
$.get('/api/postApiDaytotalNum/', {stdate:stdate},function (res){
if(res.code==0){
layer.close(index);
alert(res.msg)
}else {
layer.close(index);
alert(res.msg)
}
});
return false;
});
});
</script>
</body>
</html>
5.库的设计
class Apidayurlnum(models.Model):
id = models.AutoField(primary_key=True)
container_name=models.CharField(verbose_name='服务名称',max_length=45)
time = models.DateTimeField(verbose_name='日期',max_length=256)
path= models.CharField(verbose_name='请求路径',max_length=256)
response= models.JSONField(verbose_name='返回内容',max_length=256)
number = models.CharField(verbose_name="调用数量",max_length=45)
failednum = models.CharField(verbose_name="失败调用数量",max_length=45,default="")
class Apidaytotalnum(models.Model):
id = models.AutoField(primary_key=True)
container_name = models.CharField(verbose_name='服务名称', max_length=45)
time = models.DateTimeField(verbose_name='日期', max_length=256)
number = models.CharField(verbose_name="调用数量", max_length=45)
python调用阿里云sdk统计接口调用量
请
登录后发表观点