mirror of
https://gitlab.archlinux.org/archlinux/aurweb.git
synced 2025-02-03 10:43:03 +01:00
fix(fastapi): utilize PROMETHEUS_MULTIPROC_DIR in our own /metrics
Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
parent
1be4ac2fde
commit
dc397f6bd8
3 changed files with 34 additions and 3 deletions
|
@ -9,6 +9,7 @@ from urllib.parse import quote_plus
|
||||||
from fastapi import FastAPI, HTTPException, Request
|
from fastapi import FastAPI, HTTPException, Request
|
||||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
from prometheus_client import multiprocess
|
||||||
from sqlalchemy import and_, or_
|
from sqlalchemy import and_, or_
|
||||||
from starlette.middleware.authentication import AuthenticationMiddleware
|
from starlette.middleware.authentication import AuthenticationMiddleware
|
||||||
from starlette.middleware.sessions import SessionMiddleware
|
from starlette.middleware.sessions import SessionMiddleware
|
||||||
|
@ -29,7 +30,7 @@ app = FastAPI(exception_handlers=errors.exceptions)
|
||||||
# library with custom collectors and expose /metrics.
|
# library with custom collectors and expose /metrics.
|
||||||
instrumentator().add(http_api_requests_total())
|
instrumentator().add(http_api_requests_total())
|
||||||
instrumentator().add(http_requests_total())
|
instrumentator().add(http_requests_total())
|
||||||
instrumentator().instrument(app).expose(app)
|
instrumentator().instrument(app)
|
||||||
|
|
||||||
|
|
||||||
@app.on_event("startup")
|
@app.on_event("startup")
|
||||||
|
@ -79,6 +80,12 @@ async def app_startup():
|
||||||
get_engine()
|
get_engine()
|
||||||
|
|
||||||
|
|
||||||
|
def child_exit(server, worker): # pragma: no cover
|
||||||
|
""" This function is required for gunicorn customization
|
||||||
|
of prometheus multiprocessing. """
|
||||||
|
multiprocess.mark_process_dead(worker.pid)
|
||||||
|
|
||||||
|
|
||||||
@app.exception_handler(HTTPException)
|
@app.exception_handler(HTTPException)
|
||||||
async def http_exception_handler(request, exc):
|
async def http_exception_handler(request, exc):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
""" AURWeb's primary routing module. Define all routes via @app.app.{get,post}
|
""" AURWeb's primary routing module. Define all routes via @app.app.{get,post}
|
||||||
decorators in some way; more complex routes should be defined in their
|
decorators in some way; more complex routes should be defined in their
|
||||||
own modules and imported here. """
|
own modules and imported here. """
|
||||||
|
import os
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
|
|
||||||
from fastapi import APIRouter, Form, HTTPException, Request
|
from fastapi import APIRouter, Form, HTTPException, Request, Response
|
||||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||||
|
from prometheus_client import CONTENT_TYPE_LATEST, CollectorRegistry, generate_latest, multiprocess
|
||||||
from sqlalchemy import and_, case, or_
|
from sqlalchemy import and_, case, or_
|
||||||
|
|
||||||
import aurweb.config
|
import aurweb.config
|
||||||
|
@ -203,7 +206,21 @@ async def index(request: Request):
|
||||||
return render_template(request, "index.html", context)
|
return render_template(request, "index.html", context)
|
||||||
|
|
||||||
|
|
||||||
# A route that returns a error 503. For testing purposes.
|
@router.get("/metrics")
|
||||||
|
async def metrics(request: Request):
|
||||||
|
registry = CollectorRegistry()
|
||||||
|
if os.environ.get("FASTAPI_BACKEND", "") == "gunicorn": # pragma: no cover
|
||||||
|
# This case only ever happens in production, when we are running
|
||||||
|
# gunicorn. We don't test with gunicorn, so we don't cover this path.
|
||||||
|
multiprocess.MultiProcessCollector(registry)
|
||||||
|
data = generate_latest(registry)
|
||||||
|
headers = {
|
||||||
|
"Content-Type": CONTENT_TYPE_LATEST,
|
||||||
|
"Content-Length": str(len(data))
|
||||||
|
}
|
||||||
|
return Response(data, headers=headers)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/raisefivethree", response_class=HTMLResponse)
|
@router.get("/raisefivethree", response_class=HTMLResponse)
|
||||||
async def raise_service_unavailable(request: Request):
|
async def raise_service_unavailable(request: Request):
|
||||||
raise HTTPException(status_code=503)
|
raise HTTPException(status_code=503)
|
||||||
|
|
|
@ -117,3 +117,10 @@ def test_get_successes():
|
||||||
"""
|
"""
|
||||||
successes = get_successes(html)
|
successes = get_successes(html)
|
||||||
assert successes[0].text.strip() == "Test"
|
assert successes[0].text.strip() == "Test"
|
||||||
|
|
||||||
|
|
||||||
|
def test_metrics(client: TestClient):
|
||||||
|
with client as request:
|
||||||
|
resp = request.get("/metrics")
|
||||||
|
assert resp.status_code == int(HTTPStatus.OK)
|
||||||
|
assert resp.headers.get("Content-Type").startswith("text/plain")
|
||||||
|
|
Loading…
Add table
Reference in a new issue