feat(ocr): expose /metrics endpoint via prometheus-fastapi-instrumentator
Mount the instrumentator immediately after FastAPI app creation, excluding /health and /metrics from request metrics to keep http_requests_total focused on real application traffic. Refs #652 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@ import pypdfium2 as pdfium
|
|||||||
from fastapi import FastAPI, Form, Header, HTTPException, UploadFile
|
from fastapi import FastAPI, Form, Header, HTTPException, UploadFile
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import StreamingResponse
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
from prometheus_fastapi_instrumentator import Instrumentator
|
||||||
|
|
||||||
from confidence import apply_confidence_markers, get_threshold
|
from confidence import apply_confidence_markers, get_threshold
|
||||||
from spell_check import correct_text, load_spell_checker
|
from spell_check import correct_text, load_spell_checker
|
||||||
@@ -72,6 +73,8 @@ async def lifespan(app: FastAPI):
|
|||||||
|
|
||||||
app = FastAPI(title="Familienarchiv OCR Service", lifespan=lifespan)
|
app = FastAPI(title="Familienarchiv OCR Service", lifespan=lifespan)
|
||||||
|
|
||||||
|
Instrumentator(excluded_handlers=["/health", "/metrics"]).instrument(app).expose(app)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/health")
|
@app.get("/health")
|
||||||
def health():
|
def health():
|
||||||
|
|||||||
@@ -10,3 +10,4 @@ pyvips>=2.2.0
|
|||||||
httpx==0.28.1
|
httpx==0.28.1
|
||||||
pyspellchecker==0.9.0
|
pyspellchecker==0.9.0
|
||||||
opencv-python-headless==4.11.0.86
|
opencv-python-headless==4.11.0.86
|
||||||
|
prometheus-fastapi-instrumentator==7.0.0
|
||||||
|
|||||||
24
ocr-service/test_metrics.py
Normal file
24
ocr-service/test_metrics.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
"""Tests for Prometheus metrics exposed by the OCR service.
|
||||||
|
|
||||||
|
Each test that asserts on a counter/gauge value uses a fresh CollectorRegistry
|
||||||
|
(see decision #3 on issue #652) to keep the metrics isolated between tests.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from httpx import ASGITransport, AsyncClient
|
||||||
|
|
||||||
|
from main import app
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_metrics_endpoint_returns_200():
|
||||||
|
"""`GET /metrics` returns 200 with Prometheus exposition content."""
|
||||||
|
with patch("main.kraken_engine.load_models"), \
|
||||||
|
patch("main.load_spell_checker"):
|
||||||
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
|
||||||
|
response = await client.get("/metrics")
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "text/plain" in response.headers.get("content-type", "")
|
||||||
Reference in New Issue
Block a user