feat: expand Prometheus metrics with request duration, storage, and auth counters

This commit is contained in:
SnapOtter
2026-06-14 12:06:06 +08:00
parent 6237684ec8
commit fb14f41512
5 changed files with 61 additions and 1 deletions
+21
View File
@@ -22,6 +22,7 @@ import { shouldRunStartupCleanup } from "./lib/cleanup.js";
import { buildCsp } from "./lib/csp.js";
import { ensureAiDirs, recoverInterruptedInstalls } from "./lib/feature-status.js";
import { requestDuration } from "./lib/metrics.js";
import { getSettingString } from "./lib/settings-helpers.js";
import { requirePermission } from "./permissions.js";
import {
@@ -270,6 +271,26 @@ app.addHook("onSend", async (_request, reply) => {
reply.header("Content-Security-Policy", buildCsp(_request.url.startsWith("/api/docs")));
});
// Record HTTP request duration for Prometheus (bounded cardinality: 5 route groups * 5 status classes)
app.addHook("onResponse", (request, reply, done) => {
const duration = reply.elapsedTime / 1000;
const url = request.url;
let routeGroup = "other";
if (url.startsWith("/api/v1/tools/") || url.startsWith("/api/v1/jobs/")) routeGroup = "tools";
else if (url.startsWith("/api/auth/") || url.startsWith("/api/v1/enterprise/"))
routeGroup = "auth";
else if (url.startsWith("/api/v1/admin/") || url.startsWith("/api/v1/settings"))
routeGroup = "admin";
else if (url.startsWith("/api/v1/files")) routeGroup = "files";
else if (url.startsWith("/api/v1/scim/")) routeGroup = "scim";
const statusClass = `${Math.floor(reply.statusCode / 100)}xx`;
requestDuration.observe({ route_group: routeGroup, status_class: statusClass }, duration);
done();
});
// Always register rate-limit plugin so per-route limits (login brute-force protection) work.
// max=0 means "unlimited" (50k/min) -- @fastify/rate-limit treats literal 0 as "block all".
await app.register(rateLimit, {