Add prometheus-fastapi-instrumentator for HTTP metrics #168
No reviewers
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
forgejo_admin/pal-e-api!168
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "167-add-prometheus-fastapi-instrumentator-fo"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Wire up
prometheus-fastapi-instrumentatormiddleware to expose real HTTP request duration and count metrics on the existing/metricsendpoint. Thepal_e_docs_upgauge is preserved for backwards compatibility.Changes
pyproject.toml: addedprometheus-fastapi-instrumentator>=7.0dependencysrc/pal_e_docs/main.py: initializedInstrumentatorwithexcluded_handlers=["/healthz", "/metrics"]andshould_ignore_untemplated=True, called.instrument(app)after router registrationsrc/pal_e_docs/routes/health.py: replaced stub response withprometheus_client.generate_latest()output; registeredpal_e_docs_upas a realGaugeset to 1tests/test_health.py: splittest_metricsinto two tests: one verifying the app gauge, one verifying HTTP instrumentation metrics appear after a tracked requestTest Plan
ruff checkandruff format --checkclean on all changed filesReview Checklist
Related
plan-pal-e-platform-- platform observabilityReview -- Pass
Reviewed all 4 changed files (22 additions, 2 deletions). No issues found.
pyproject.toml -- Dependency added in correct location, version pin is appropriate.
main.py -- Instrumentator initialized after all routers are registered.
excluded_handlerscorrectly omits/healthzand/metricsfrom instrumentation.should_ignore_untemplated=Trueprevents unmatched routes from polluting cardinality.health.py --
pal_e_docs_upGauge created at module level (singleton pattern).generate_latest()returns bytes whichPlainTextResponsehandles correctly. Media type follows Prometheus exposition format spec.test_health.py -- Two focused tests. Instrumentation test correctly makes a tracked request before asserting metrics exist (instrumentator only populates after first observation).
No secrets, no unnecessary changes, no regressions (498 tests pass).
PR #168 Review
BLOCKERS
None.
NITS
generate_latest()returns bytes --PlainTextResponsehandlesbytescontent correctly, so this works. However, for explicitness and to avoid future confusion if someone reads the code expecting a string, a comment noting thatgenerate_latest()returnsbytes(notstr) could help. Non-blocking.Truncated branch name -- The branch is
167-add-prometheus-fastapi-instrumentator-fo(truncated at 50 chars). Harmless, but worth noting -- Forgejo's branch name length limit clipped it. No action needed.Test assertion specificity --
test_metrics_contains_http_instrumentationasserts"http_request_duration" in response.text. The actual metric name from the instrumentator ishttp_request_duration_seconds(with a_bucket,_count,_sumsuffix for the histogram). The substring match works but is loose. A more specific assertion like"http_request_duration_seconds" in response.textwould be marginally more precise. Non-blocking.ANALYSIS
Instrumentator without
.expose()-- correct design. The standard pattern is.instrument(app).expose(app), which auto-registers a/metricsroute. This PR intentionally omits.expose()becausehealth.pyalready defines a manual/metricsendpoint usingprometheus_client.generate_latest(). Sinceprometheus-fastapi-instrumentatorregisters its metrics in the defaultprometheus_clientglobal registry,generate_latest()will collect both the custompal_e_docs_upgauge and all auto-instrumented HTTP histogram/counter metrics in a single scrape. This is the right call -- it avoids a duplicate route conflict and preserves the existing endpoint.Exclusion filters are correct.
/healthzand/metricsare excluded from instrumentation viaexcluded_handlers, preventing self-referential metric inflation from health checks and Prometheus scrapes.should_ignore_untemplated=Trueprevents 404 noise from polluting cardinality.Gauge at module level is safe.
pal_e_docs_up = Gauge(...)registers once at import time. The test conftest importsapponce per session, so there is no risk of duplicate Gauge registration errors across tests.Content type is correct.
text/plain; version=0.0.4matches the Prometheus exposition format specification.Consistent with existing patterns. The embedding worker (
embedding_worker.py) already usesprometheus_clientdirectly (Counter,Gauge,Histogram,generate_latest). This PR extends that pattern to the main API. Both processes share the sameprometheus_clientlibrary, and the instrumentator adds HTTP-level metrics that the worker's custom metrics do not cover.SOP COMPLIANCE
167-add-prometheus-fastapi-instrumentator-foreferences #167)plan-pal-e-platform)VERDICT: APPROVED