diff --git a/src/metrics/registry.ts b/src/metrics/registry.ts index 2668e71d..f59ddf15 100644 --- a/src/metrics/registry.ts +++ b/src/metrics/registry.ts @@ -69,4 +69,22 @@ export function resetMaintenanceMetrics(): void { maintenanceDuration.reset(); } -export { billingDeductDuration, refreshTokenDuration, maintenanceDuration, creditsDuration }; +const adminDuration = new client.Histogram({ + name: 'admin_duration_seconds', + help: 'Latency of /api/admin routes in seconds', + labelNames: ['route', 'status_code'], + buckets: [0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10], +}); + +export function recordAdminDuration(route: string, statusCode: number, durationMs: number): void { + adminDuration.observe( + { route, status_code: String(statusCode) }, + durationMs / 1000, + ); +} + +export function resetAdminMetrics(): void { + adminDuration.reset(); +} + +export { billingDeductDuration, refreshTokenDuration, maintenanceDuration, creditsDuration, adminDuration }; diff --git a/src/middleware/metricsHistogram.ts b/src/middleware/metricsHistogram.ts index 562e71ed..06c55404 100644 --- a/src/middleware/metricsHistogram.ts +++ b/src/middleware/metricsHistogram.ts @@ -1,6 +1,7 @@ import type { Request, Response, NextFunction } from 'express'; import { performance } from 'node:perf_hooks'; import { + recordAdminDuration, recordBillingDeductDuration, recordRefreshTokenDuration, recordMaintenanceDuration, @@ -50,3 +51,19 @@ export function maintenanceHistogramMiddleware( next(); } + +export function adminHistogramMiddleware( + req: Request, + res: Response, + next: NextFunction, +): void { + const start = performance.now(); + const route = req.baseUrl + (req.route?.path ?? req.path); + + res.on('finish', () => { + const durationMs = performance.now() - start; + recordAdminDuration(route, res.statusCode, durationMs); + }); + + next(); +} diff --git a/src/routes/admin.ts b/src/routes/admin.ts index d9023877..adf32b28 100644 --- a/src/routes/admin.ts +++ b/src/routes/admin.ts @@ -2,6 +2,7 @@ import { adminLogMiddleware } from '../middleware/adminLog.js'; import { Router, type Response } from 'express'; import { adminAuth } from '../middleware/adminAuth.js'; import { createAdminIpAllowlist } from '../middleware/ipAllowlist.js'; +import { adminHistogramMiddleware } from '../middleware/metricsHistogram.js'; import { findUsers } from '../repositories/userRepository.js'; import { parsePagination, paginatedResponse } from '../lib/pagination.js'; import { getClientIp } from '../lib/clientIp.js'; @@ -40,6 +41,7 @@ const router = Router(); router.use(createAdminIpAllowlist()); router.use(adminAuth); router.use(adminLogMiddleware); +router.use(adminHistogramMiddleware); router.get('/users', async (req, res, next) => { try { const { limit, offset } = parsePagination(req.query as Record);