Pull to refresh

Comments 1

Отличная статья, спасибо! Была бы она написана и попалась бы мне на глаза пару недель назад, когда пришлось разбираться с подобным в рамках рабоче работе... 🥲

Хотел бы отметить несколько моментов, которые можно сделать иначе (потенциально более эффективно).

  1. Middleware для автоматического сбора. Вместо того, чтобы заниматься вот этим

# Нормализация пути: /api/activities/12345 → /api/activities/{id}
path = request.url.path
if path.startswith('/api/'):
    parts = path.split('/')
    if len(parts) > 3 and parts[3].isdigit():
        parts[3] = '{id}'
        path = '/'.join(parts)

можно сделать так (основано на ответе на Stackoverflow (https://stackoverflow.com/a/74262037)):

path = request.scope["route"].path

Только важно получать значение после вызова call_next(request), иначе ключа route не будет.
2. Endpoint для метрик.
Согласно документации prometheus-client (https://prometheus.github.io/client_python/exporting/http/fastapi-gunicorn/), вместо кастомного

@app.get("/metrics")
async def metrics():
    """Prometheus metrics endpoint"""
    return PlainTextResponse(
        generate_latest(registry),
        media_type=CONTENT_TYPE_LATEST
    )

можно сделать так:

from prometheus_client import make_asgi_app


# create app

# Add prometheus asgi middleware to route /metrics requests
metrics_app = make_asgi_app()
app.mount("/metrics", metrics_app)

К сожалению, в документации есть небольшой нюанс: поскольку мы маунтим приложение, то путь у него будет рутовый, то есть /metrics/, и на /metrics можно попасть только с редиректом, но Prometheus при скрейпинге метрик по редиректам не ходит. )) Ему нужен (по умолчанию) строго путь /metrics. Фиксится так (проблема и её решение уже описаны в issue (https://github.com/prometheus/client_python/issues/1016) в репозитории prometheus-client):

import re

from prometheus_client import make_asgi_app
from starlette.routing import Mount

# create app

route = Mount("/metrics", make_asgi_app())
route.path_regex = re.compile('^/metrics(?P<path>.*)$')
app.routes.append(route)
Sign up to leave a comment.

Articles