File size: 5,311 Bytes
054900e |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
from __future__ import annotations
import asyncio
from typing import TYPE_CHECKING
import prometheus_client
from aiohttp.web_exceptions import HTTPException
from aiohttp.web_middlewares import middleware
if TYPE_CHECKING:
from aiohttp.typedefs import Handler, Middleware
from aiohttp.web_request import Request
from aiohttp.web_response import StreamResponse
METRICS_PREFIX = "tgbot"
def prometheus_middleware_factory(
metrics_prefix: str = METRICS_PREFIX,
registry: prometheus_client.CollectorRegistry | None = None,
) -> Middleware:
used_registry = registry or prometheus_client.REGISTRY
requests_metrics = prometheus_client.Counter(
name=f"{metrics_prefix}_requests",
documentation="Total requests by method, scheme, remote and path template.",
labelnames=["method", "scheme", "remote", "path_template"],
registry=used_registry,
)
responses_metrics = prometheus_client.Counter(
name=f"{metrics_prefix}_responses",
documentation="Total responses by method, scheme, remote, path template and status code.",
labelnames=["method", "scheme", "remote", "path_template", "status_code"],
registry=used_registry,
)
requests_processing_time_metrics = prometheus_client.Histogram(
name=f"{metrics_prefix}_request_duration",
documentation="Histogram of requests processing time by method, "
"scheme, remote, path template and status code (in seconds)",
labelnames=["method", "scheme", "remote", "path_template", "status_code"],
unit="seconds",
registry=used_registry,
)
requests_in_progress_metrics = prometheus_client.Gauge(
name=f"{metrics_prefix}_requests_in_progress",
documentation="Gauge of requests by method, scheme, remote and path template currently being processed.",
labelnames=["method", "scheme", "remote", "path_template"],
registry=used_registry,
)
exceptions_metrics = prometheus_client.Counter(
name=f"{metrics_prefix}_exceptions",
documentation="Total exceptions raised by path, scheme, remote, path template and exception type.",
labelnames=["method", "scheme", "remote", "path_template", "exception_type"],
registry=used_registry,
)
@middleware
async def prometheus_middleware(request: Request, handler: Handler) -> StreamResponse:
loop = asyncio.get_running_loop() or asyncio.get_event_loop()
try:
path_template = getattr(
getattr(
request.match_info.route,
"resource",
None,
),
"canonical",
"__not_matched__",
)
except AttributeError:
path_template = "__not_matched__"
requests_metrics.labels(
method=request.method,
scheme=request.scheme,
remote=request.remote,
path_template=path_template,
).inc()
requests_in_progress_metrics.labels(
method=request.method,
scheme=request.scheme,
remote=request.remote,
path_template=path_template,
).inc()
request_start_time = loop.time()
try:
response = await handler(request)
request_end_time = loop.time()
except Exception as e: # noqa: BLE001
request_end_time = loop.time()
status = e.status if isinstance(e, HTTPException) else 500
responses_metrics.labels(
method=request.method,
scheme=request.scheme,
remote=request.remote,
path_template=path_template,
status_code=status,
).inc()
exceptions_metrics.labels(
method=request.method,
scheme=request.scheme,
remote=request.remote,
path_template=path_template,
exception_type=type(e).__name__,
).inc()
requests_processing_time_metrics.labels(
method=request.method,
scheme=request.scheme,
remote=request.remote,
path_template=path_template,
status_code=status,
).observe(request_end_time - request_start_time)
raise e from None
else:
responses_metrics.labels(
method=request.method,
scheme=request.scheme,
remote=request.remote,
path_template=path_template,
status_code=response.status,
).inc()
requests_processing_time_metrics.labels(
method=request.method,
scheme=request.scheme,
remote=request.remote,
path_template=path_template,
status_code=response.status,
).observe(request_end_time - request_start_time)
finally:
requests_in_progress_metrics.labels(
method=request.method,
scheme=request.scheme,
remote=request.remote,
path_template=path_template,
).dec()
return response
return prometheus_middleware
|