const client = require("prom-client"); function resolveRouteLabel(request) { const url = request?.routeOptions?.url; if (typeof url === "string" && url.trim()) { return url; } const routerPath = request?.routerPath; if (typeof routerPath === "string" && routerPath.trim()) { return routerPath; } const rawUrl = request?.raw?.url; if (typeof rawUrl === "string" && rawUrl.trim()) { return rawUrl.split("?")[0]; } return "unknown"; } function initMetrics(app) { const registry = new client.Registry(); client.collectDefaultMetrics({ register: registry, }); const httpRequestsTotal = new client.Counter({ name: "hyb_http_requests_total", help: "Total number of HTTP requests", labelNames: ["method", "route", "status_code"], registers: [registry], }); const httpRequestDurationSeconds = new client.Histogram({ name: "hyb_http_request_duration_seconds", help: "HTTP request duration in seconds", labelNames: ["method", "route", "status_code"], buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10], registers: [registry], }); const jobRunsTotal = new client.Counter({ name: "hyb_job_runs_total", help: "Total number of background job runs", labelNames: ["job", "driver", "outcome"], registers: [registry], }); const jobProcessedTotal = new client.Counter({ name: "hyb_job_processed_total", help: "Total number of processed items per job", labelNames: ["job", "driver", "kind"], registers: [registry], }); const dbReady = new client.Gauge({ name: "hyb_db_ready", help: "Database readiness probe result (1=ready, 0=not ready)", registers: [registry], }); app.decorate("metrics", { registry, httpRequestsTotal, httpRequestDurationSeconds, jobRunsTotal, jobProcessedTotal, dbReady, }); app.decorateRequest("metricsStart", null); app.addHook("onRequest", async (request) => { request.metricsStart = process.hrtime.bigint(); }); app.addHook("onResponse", async (request, reply) => { const start = request.metricsStart; if (typeof start !== "bigint") { return; } const end = process.hrtime.bigint(); const durationSeconds = Number(end - start) / 1e9; const method = request.method; const route = resolveRouteLabel(request); const statusCode = String(reply.statusCode); httpRequestsTotal.inc({ method, route, status_code: statusCode }); httpRequestDurationSeconds.observe( { method, route, status_code: statusCode }, durationSeconds, ); }); return app.metrics; } module.exports = { initMetrics, };