Skip to content

Metrics

OpenTremor Core exposes a /metrics endpoint in Prometheus format — no separate installation, no dependency on the monitoring pack.


When prometheus-client is installed (included by default), the server exposes:

GET /metrics — Prometheus scrape endpoint (no auth required)

The endpoint is served by prometheus_client’s own ASGI app and is excluded from span recording. Auth is intentionally not required so Prometheus can scrape without credentials.


Every metric below carries an org_id label, letting you slice or filter any panel/query down to one tenant — see Organization filtering.

MetricTypeLabelsDescription
http_request_duration_secondsHistogramhandler, method, status, org_idRequest latency — use for rate, p95, p99
MetricTypeLabelsDescription
mcp_ingest_totalCounteranalyzer, org_idPlan ingest calls
mcp_ingested_resources_totalCounteranalyzer, org_idResources parsed and stored
mcp_analysis_totalCounterresource_type, org_idAnalysis submissions (PUT /resource/{hash}/analysis)
mcp_findings_totalCounterresource_type, severity, org_idIndividual findings — severity is CRITICAL, HIGH, MEDIUM, LOW, or INFO
mcp_llm_analysis_duration_secondsHistogramresource_type, org_idLLM round-trip latency: time from GET /{ns}/resource/next to PUT /resource/{hash}/analysis

mcp_analysis_total/mcp_findings_total increment identically regardless of how the analysis was produced — client-led PUT, server-side POST .../analyze, or a GitHub-webhook-triggered run — since all three funnel through the same analysis_service.record_analysis(). Billing (usage_events/LLM token cost) and findings-triage state changes are not exposed as Prometheus metrics — query them via GET /orgs/{org_id}/usage and GET /findings/summary instead.


org_id is resolved from the same request-scoped context var the telemetry span system already uses (controllers/observability/telemetry.py’s get_org_id()/set_org_id()) — no separate plumbing per metric. A request that never resolves an org (health checks, the metrics scrape itself, public report links, the GitHub webhook, SSO callbacks before login) carries org_id="_none" rather than an empty label value, so it’s always an explicit, queryable string, never blank.

# Request rate for one org only
sum(rate(http_request_duration_seconds_count{org_id="7c3c6e2e-..."}[5m]))
# Findings rate broken out per org
sum(rate(mcp_findings_total[5m])) by (org_id, severity)

Point any Prometheus instance at the server:

scrape_configs:
- job_name: opentremor-core
static_configs:
- targets:
- <host>:8000
metrics_path: /metrics

A pre-built Grafana dashboard covering these metrics (HTTP traffic, analysis pipeline, findings by severity) ships as part of the separate OpenTremor Monitoring repo, not this repo.