zoukankan      html  css  js  c++  java
  • Prometheus 基于Python Django实现Prometheus Exporter

    基于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 
    '] # 存放 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
    '] # 存放 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
    '
    
                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('
    ')
                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)格式是面向行的。行由换行符( )分隔。最后一行必须以换行字符结尾。空行将被忽略

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

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

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

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

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

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

    作者:授客
    QQ:1033553122
    全国软件测试QQ交流群:7156436

    Git地址:https://gitee.com/ishouke
    友情提示:限于时间仓促,文中可能存在错误,欢迎指正、评论!
    作者五行缺钱,如果觉得文章对您有帮助,请扫描下边的二维码打赏作者,金额随意,您的支持将是我继续创作的源动力,打赏后如有任何疑问,请联系我!!!
               微信打赏                        支付宝打赏                  全国软件测试交流QQ群  
                  

  • 相关阅读:
    美团霸面---我想说说心里话。
    docker在ubuntu14.04下的安装笔记
    ubuntu14.04 upgrade出现【Ubuntu is running in low-graphics mode】问题的一个解决办法
    Python2和Python3在windows下共存
    Python发送邮件
    Python在安装第三方模块遇到的问题及解决办法
    127.0.0.1和localhost完全相等吗?
    vim总结
    linux shell学习笔记
    Jenkins +JUnit
  • 原文地址:https://www.cnblogs.com/shouke/p/15426777.html
Copyright © 2011-2022 走看看