[flask]集成Prometheus

打印 上一主题 下一主题

主题 1491|帖子 1491|积分 4473

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
前言

在服务监控中,大抵可分为日志监控和指标监控。日志监控一样平常由类似ELK如许的日志系统去收集分析,而指标监控一样平常是由Prometheus收集服务的一些可量化的指标数据,好比服务响应码、响应时间。
安装sdk
  1. python -m pip install prometheus-client
复制代码
示例

示例1

使用 prometheus_client自带的make_wsgi_app直接绑定到flask实例上,以下client使用的registry是自带默认的REGISTRY。
  1. from flask import Flask
  2. from werkzeug.middleware.dispatcher import DispatcherMiddleware
  3. from prometheus_client import make_wsgi_app, Info
  4. i = Info("my_build_version", "Description of info")
  5. i.info({"version": "1.2.3", "buildhost": "foo@bar"})
  6. app = Flask(__name__)
  7. app.wsgi_app = DispatcherMiddleware(app.wsgi_app, {"/metrics": make_wsgi_app()})
  8. @app.get("/")
  9. def hello():
  10.     return "Hello World!"
  11. if __name__ == "__main__":
  12.     app.run(debug=False)
复制代码
访问http://127.0.0.1:5000/metrics可以看到类似以下的输出,其中包含了自定义的metric my_build_version_info
  1. # HELP python_gc_objects_collected_total Objects collected during gc
  2. # TYPE python_gc_objects_collected_total counter
  3. python_gc_objects_collected_total{generation="0"} 527.0
  4. python_gc_objects_collected_total{generation="1"} 124.0
  5. python_gc_objects_collected_total{generation="2"} 0.0
  6. # HELP python_gc_objects_uncollectable_total Uncollectable objects found during GC
  7. # TYPE python_gc_objects_uncollectable_total counter
  8. python_gc_objects_uncollectable_total{generation="0"} 0.0
  9. python_gc_objects_uncollectable_total{generation="1"} 0.0
  10. python_gc_objects_uncollectable_total{generation="2"} 0.0
  11. # HELP python_gc_collections_total Number of times this generation was collected
  12. # TYPE python_gc_collections_total counter
  13. python_gc_collections_total{generation="0"} 112.0
  14. python_gc_collections_total{generation="1"} 10.0
  15. python_gc_collections_total{generation="2"} 0.0
  16. # HELP python_info Python platform information
  17. # TYPE python_info gauge
  18. python_info{implementation="CPython",major="3",minor="11",patchlevel="2",version="3.11.2"} 1.0
  19. # HELP process_virtual_memory_bytes Virtual memory size in bytes.
  20. # TYPE process_virtual_memory_bytes gauge
  21. process_virtual_memory_bytes 5.11332352e+08
  22. # HELP process_resident_memory_bytes Resident memory size in bytes.
  23. # TYPE process_resident_memory_bytes gauge
  24. process_resident_memory_bytes 4.4941312e+07
  25. # HELP process_start_time_seconds Start time of the process since unix epoch in seconds.
  26. # TYPE process_start_time_seconds gauge
  27. process_start_time_seconds 1.74637360939e+09
  28. # HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
  29. # TYPE process_cpu_seconds_total counter
  30. process_cpu_seconds_total 0.25
  31. # HELP process_open_fds Number of open file descriptors.
  32. # TYPE process_open_fds gauge
  33. process_open_fds 7.0
  34. # HELP process_max_fds Maximum number of open file descriptors.
  35. # TYPE process_max_fds gauge
  36. process_max_fds 1.048576e+06
  37. # HELP my_build_version_info Description of info
  38. # TYPE my_build_version_info gauge
  39. my_build_version_info{buildhost="foo@bar",version="1.2.3"} 1.0
复制代码
如果不想要自带输出的这些gc信息,或者想要手动注册,也可以自定义registry,
  1. from flask import Flask
  2. from werkzeug.middleware.dispatcher import DispatcherMiddleware
  3. from prometheus_client import make_wsgi_app, Info, CollectorRegistry, PROCESS_COLLECTOR, GC_COLLECTOR, PLATFORM_COLLECTOR
  4. registry = CollectorRegistry(auto_describe=True)
  5. registry.register(PROCESS_COLLECTOR)
  6. registry.register(GC_COLLECTOR)
  7. registry.register(PLATFORM_COLLECTOR)
  8. i = Info("my_build_version", "Description of info", registry=registry)
  9. i.info({"version": "1.2.3", "buildhost": "foo@bar"})
  10. app = Flask(__name__)
  11. app.wsgi_app = DispatcherMiddleware(app.wsgi_app, {"/metrics": make_wsgi_app(registry=registry)})
  12. @app.get("/")
  13. def hello():
  14.     return "Hello World!"
  15. if __name__ == "__main__":
  16.     app.run(debug=False)
复制代码
示例2
  1. .
  2. ├── flaskapp.py
  3. ├── pkg
  4. │   ├── __init__.py
  5. │   └── metrics
  6. │       ├── __init__.py
  7. │       └── metrics.py
  8. └── router
  9.     ├── __init__.py
  10.     └── metrics.py
复制代码

  • flaskapp.py
  1. from flask import Flask, request, Response
  2. from pkg.metrics import request_counter
  3. from router import metrics
  4. from random import randint
  5. import json
  6. app = Flask(__name__)
  7. @app.get("/")
  8. def hello():
  9.     status_list = [101, 200, 401, 403, 404, 499, 500, 503, 504]
  10.     request_counter.labels(
  11.         method=request.method,
  12.         path=request.path,
  13.         status=status_list[randint(0, len(status_list) - 1)],
  14.     ).inc()
  15.     return {"message": "Hello World!"}
  16. @app.post("/webhook")
  17. def jsontest():
  18.     # 获取消息头
  19.     print(request.headers)
  20.         # 接收数据并转成字典
  21.     data = request.get_data()
  22.     # data = json.loads(data)
  23.     print(data)
  24.     # 响应json格式数据
  25.     return Response(status=200)
  26. app.register_blueprint(metrics.bp)
  27. if __name__ == "__main__":
  28.     app.run(host="0.0.0.0", port=8000)
复制代码

  • pkg/metrics/__init__.py
  1. from .metrics import registry, request_counter
  2. __all__ = ["registry", "request_counter"]
复制代码

  • pkg/metrics/metrics.py
  1. from prometheus_client import Counter, CollectorRegistry, Info
  2. import socket
  3. from functools import lru_cache
  4. import sys
  5. registry = CollectorRegistry(auto_describe=True)
  6. request_counter = Counter(
  7.     "http_requests_total",
  8.     "Total HTTP Requests",
  9.     ["method", "path", "status"],
  10.     registry=registry,
  11. )
  12. @lru_cache(maxsize=128)
  13. def get_selfip():
  14.     try:
  15.         s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  16.         s.connect(('8.8.8.8', 53))
  17.         return s.getsockname()[0]
  18.     except Exception:
  19.         return "127.0.0.1"
  20. @lru_cache(maxsize=None)
  21. def get_pyversion():
  22.     return sys.version.split(" ")[0]
  23. app_info = Info(
  24.     "app_info",
  25.     "Application info",
  26.     registry=registry,
  27. )
  28. app_info.info({
  29.     "version": "1.0.0",
  30.     "host": get_selfip(),
  31.     "python_version": get_pyversion(),
  32. })
复制代码

  • router/__init__.py内容为空
  • router/metrics.py
  1. from flask import Blueprint, make_response
  2. from pkg.metrics import registry
  3. from prometheus_client import generate_latest
  4. bp = Blueprint("metrics", __name__, url_prefix="/metrics")
  5. @bp.get("/prometheus")
  6. def get_metrics():
  7.     """
  8.     获取 Prometheus 的指标数据
  9.     """
  10.     resp = make_response(generate_latest(registry))
  11.     resp.headers["Content-Type"] = "text/plain"
  12.     return resp
复制代码
示例3-在中间件中采集http请求
  1. from flask import Flask
  2. from werkzeug.middleware.dispatcher import DispatcherMiddleware
  3. from prometheus_client import (
  4.     Counter,
  5.     Histogram,
  6.     make_wsgi_app,
  7.     Info,
  8. )
  9. import time
  10. from random import random, randint
  11. class Metrics:
  12.     def __init__(self):
  13.         self.app_info = Info("python_service_basic_info", "Description of info")
  14.         self.request_counter = Counter(
  15.             "http_request_total", "Total HTTP requests", ["method", "path", "status"]
  16.         )
  17.         self.response_time = Histogram(
  18.             "http_response_time",
  19.             "HTTP response time",
  20.             ["method", "path"],
  21.         )
  22.         self._initialize()
  23.     def _initialize(self):
  24.         self.app_info.info(
  25.             {
  26.                 "version": self._get_app_info_version(),
  27.                 "name": "myapp",
  28.             }
  29.         )
  30.     def _get_app_info_version(self) -> str:
  31.         return "0.1.0"
  32. collector = Metrics()
  33. class MetricsMiddleware:
  34.     def __init__(self, app):
  35.         self.app = app
  36.         self.white_list = frozenset(
  37.             [
  38.                 "/metrics",
  39.                 "/health",
  40.             ]
  41.         )
  42.     def __call__(self, environ, start_response):
  43.         method = environ.get("REQUEST_METHOD", "NaN")
  44.         path = environ.get("PATH_INFO", "NaN")
  45.         resp_status_code = None
  46.         def catching_start_response(status, headers, exc_info=None):
  47.             nonlocal resp_status_code
  48.             resp_status_code = status.split(" ")[0]
  49.             return start_response(status, headers, exc_info)
  50.         start_time = time.time()
  51.         response = self.app(environ, catching_start_response)
  52.         response_time = round(time.time() - start_time, 4)
  53.         if path not in self.white_list:
  54.             collector.request_counter.labels(method, path, resp_status_code).inc()
  55.             collector.response_time.labels(method, path).observe(response_time)
  56.         return response
  57. app = Flask(__name__)
  58. app.wsgi_app = DispatcherMiddleware(app.wsgi_app, {"/metrics": make_wsgi_app()})
  59. app.wsgi_app = MetricsMiddleware(app.wsgi_app)
  60. @app.get("/a1")
  61. def a1():
  62.     return "a1"
  63. @app.get("/a2")
  64. def a2():
  65.     time.sleep(random())
  66.     return "a2"
  67. @app.get("/a3")
  68. def a3():
  69.     time.sleep(randint(1, 3))
  70.     return "a3"
  71. @app.get("/a4")
  72. def a4():
  73.     time.sleep(randint(1, 3))
  74.     raise Exception("a4")
  75. if __name__ == "__main__":
  76.     app.run(host="127.0.0.1", port=5000, debug=False)
复制代码
prometheus配置
  1. scrape_configs:
  2.   # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  3.   - job_name: "python"
  4.     metrics_path: '/metrics/prometheus'
  5.     file_sd_configs:
  6.     - files: ['sd_configs/python/*.yaml']
  7.       refresh_interval:  10s
复制代码
python/target.yaml
  1. - targets: ['192.168.1.112:8000']
  2.   labels:
  3.     instance: 192.168.1.112
复制代码
参考


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

李优秀

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表