需求描述

运行监控需求,需要采集Nginx 每个URL请求的相关信息,涉及两个指标:一分钟内平均响应时间,调用次数,并且为每个指标提供3个标签:请求方法,请求状态,请求URL,并向普罗米修斯暴露这些指标相关数据

实践环境

Python 3.6.8

Django 3.0.6

prometheus-client 0.11.0

代码设计与实现

说明:为了更好的表达主题,代码中数据采集部分暂且采用​​data​​变量替代。

1. 基于官方SDK

​Gauge​​ Metric为例

view视图实现
CustomExporters.url_exporter_views.UrlExporterView
#!/usr/bin/env python
# -*- coding:utf-8 -*-

# Create your views here.
from django.http import HttpResponse
from django.views.generic import View
from prometheus_client import CollectorRegistry, Gauge, generate_latest

import logging
import traceback
logger = logging.getLogger('mylogger')

REGISTRY = CollectorRegistry()
LABELS = ['req_status', 'req_method', 'req_url'] # 标签定义

# 指标定义
g_requests_total = Gauge('requests_total', 'url request num each minute', LABELS, registry=REGISTRY)
g_avg_response_time_seconds = Gauge('avg_response_time_seconds', 'url avg response time of one minute', LABELS, registry=REGISTRY)


class UrlExporterView(View):
def get(self, request, *args, **kwargs):
try:
data = {
'count': 34,
'method': 'get',
'status': 200,
'url': 'url',
'avg_rt':50
}
g_requests_total.labels(data.get('status'),data.get('method'),data.get('url')).set(data.get('count')) #set设定值
g_avg_response_time_seconds.labels(data.get('status'),data.get('method'),data.get('url')).set(data.get('avg_rt'))

return HttpResponse(generate_latest(REGISTRY),status=200, content_type="text/plain")
except Exception:
error_msg = '%s' % traceback.format_exc()
logger.error(error_msg)
return HttpResponse('# HELP Error occured', status=500, content_type="text/plain")

注意:通过官方SDK无法向普罗米修斯暴露数据生成时间(非采集时间),以上实现方式无法满足这种需求

项目URL路由配置
CustomPrometheusExporters.CustomPrometheusExporters.urls.py
from django.contrib import admin
from django.urls import path, re_path, include

urlpatterns = [
re_path(r'^exporters/', include('CustomExporters.urls')),
path('admin/', admin.site.urls),
]
应用urls.py url路由配置
CustomExporters.urls.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-

from django.urls import path,re_path

from CustomExporters.url_exporter_views import UrlExporterView


urlpatterns = [
re_path(r'url-exporter/metrics$', UrlExporterView.as_view(), name='url-exporter')
]
查看运行结果

浏览器中访问 ​​http://127.0.0.1:8000/exporters/url-exporter/metrics​​,输出如下:

# HELP requests_total url request num each minute
# TYPE requests_total gauge
requests_total{req_method="get",req_status="200",req_url="url"} 34.0
# HELP avg_response_time_seconds url avg response time of one minute
# TYPE avg_response_time_seconds gauge
avg_response_time_seconds{req_method="get",req_status="200",req_url="url"} 50.0

2. 不基于官方SDK

view视图实现

CustomExporters.url_exporter_views.UrlExporterView
#!/usr/bin/env python
# -*- coding:utf-8 -*-

# Create your views here.
from django.http import HttpResponse
from django.views.generic import View
from prometheus_client.utils import floatToGoString

import logging
import traceback
logger = logging.getLogger('mylogger')


class UrlExporterView(View):

def get(self, request, *args, **kwargs):
try:
data = {
'count': 34,
'method': 'get',
'status': 200,
'url': 'url',
'avg_rt':50,
'timestamp': 1634099490000
}
requests_total_line_list = ['# HELP requests_total The total requests number of url to req_service, req_method, status \n'] # 存放 requests_total指标输出
avg_response_time_line_list = ['# HELP avg_response_time_milseconds average request response time for url correspond to req_service, req_method, status\n'] # 存放 avg_response_time_seconds指标输出
line_template = '%(metric_name)s{req_method="%(req_method)s",req_status="%(req_status)s",req_url="%(req_url)s"} %(label_value)s %(timestamp)s\n'

requests_total_line_list.append(line_template % {
'metric_name':'requests_total',
'req_method':data.get('method'),
'req_status':data.get('status'),
'req_url':data.get('url'),
'label_value':floatToGoString(data.get('count')),
'timestamp':data.get('timestamp')
})

avg_response_time_line_list.append(line_template % {
'metric_name':'avg_response_time_milseconds',
'req_method':data.get('method'),
'req_status':data.get('status'),
'req_url':data.get('url'),
'label_value':floatToGoString(data.get('avg_rt')),
'timestamp':data.get('timestamp')
})

output_list = []
output_list.extend(requests_total_line_list)
output_list.append('\n')
output_list.extend(avg_response_time_line_list)

return HttpResponse(''.join(output_list).encode('utf-8'), status=200, content_type="text/plain")
except Exception:
error_msg = '%s' % traceback.format_exc()
logger.error(error_msg)
return HttpResponse('# HELP Error occured', status=500, content_type="text/plain")

查看运行结果

浏览器中访问 ​​http://127.0.0.1:8000/exporters/url-exporter/metrics​​,输出如下:

# HELP requests_total The total requests number of url to req_service, req_method, status 
requests_total{req_method="get",req_status="200",req_url="url"} 34.0 1634099490000

# HELP avg_response_time_milseconds average request response time for url correspond to req_service, req_method, status
avg_response_time_milseconds{req_method="get",req_status="200",req_url="url"} 50.0 1634099490000

样本数据格式说明

普罗米修斯基于文本的(​​text-based​​)格式是面向行的。行由换行符(\n)分隔。最后一行必须以换行字符结尾。空行将被忽略

在一行中,​​tokens​​​可以由任意数量的空格和​​/​​或制表符分隔(如果它们与前一个令牌合并,则必须至少由一个空格分隔)。忽略行收尾随空格。

以 ​​#​​​ 作为首个非空白字符的行,被当作注释,且除非​​#​​​后面第一个token为​​HELP​​​、​​TYPE​​​,形如 ​​# HELP​​​、​​# TYPE​​,否则罗米修斯会自动忽略该行。

如果token为​​HELP​​​,则至少需要1个token,该token为​​Metric​​​名称,剩余所有token为该属性的文档字符串说明(​​dockstring​​​)。​​HELP​​​行可以是任意UTF-8序列字符,如果包含反斜杠 ​​\​​​、 换行符​​\n​​​字符,需要进行显示转义,形如 ​​\\​​​,  ​​\n​

如果token为​​TYPE​​​,则至少需要2个token,第一个token为​​Metric​​​名称,第二个为​​counter​​​,​​gauge​​​, ​​histogram​​​, ​​summary​​​, 或者 ​​untyped​​​,定义名称指定的​​Metric​​​的类型。针对同一个给定的​​Metric​​​名称,只能存在一种​​Type​​​。​​TYPE​​​行必须位于该​​Metric​​​的第一行数据样本行之前。如果该​​Metric​​​没有定义对应的​​TYPE​​​行,则默认​​TYPE​​​为​​untyped​​。

剩余的行描述样本(每行对应一个数据样本)使用以下格式

metric_name[{label_name1="label_value",label_name2="label_value",..,label_nameN="label_valueN"}] value [timestamp
  • ​metric_name​​​  和​​label_name​​遵守普罗米修斯惯用的语言表达式限制
  • ​label_value​​​ 可以是任意UTF-8序列字符,如果包含反斜杠 ​​\​​、双引号​​"​​、 换行符​​\n​​字符,需要进行显示转义,形如 ​​\\​​, ​​\"​​, ​​\n​
  • ​value​​​ 代表浮点数,正如Go ​​ParseFloat()​​所需参数。此外,除标准数值外,​​NaN​​、​​+Inf​​和​​-Inf​​分别表示非数字、正无穷大和负无穷大的有效值
  • ​timestamp​​ 数据自身生成时间,为64整数(1970-01-01 00:00:00 UTC到现在的毫秒数) ,正如Go `ParseInt()`所需参数