基於Python Django實現Prometheus Exporter
需求描述
運行監控需求,需要采集Nginx 每個URL請求的相關信息,涉及兩個指標:一分鍾內平均響應時間,調用次數,並且為每個指標提供3個標簽:請求方法,請求狀態,請求URL,並向普羅米修斯暴露這些指標相關數據
實踐環境
Python 3.6.5
Django 3.0.6
prometheus-client 0.11.0
代碼設計與實現
說明:為了更好的表達主題,代碼中數據采集部分暫且采用data
變量替代。
基於官方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
不基於官方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
代表浮點數,正如GoParseFloat()
所需參數。此外,除標准數值外,NaN
、+Inf
和-Inf
分別表示非數字、正無窮大和負無窮大的有效值timestamp
數據自身生成時間,為64整數(1970-01-01 00:00:00 UTC到現在的毫秒數) ,正如GoParseInt()
所需參數