feat(monitoring): add Prometheus metrics endpoint and Grafana dashboards

Add observability stack with @willsoto/nestjs-prometheus for /metrics endpoint,
Prometheus scraping config, and 4 auto-provisioned Grafana dashboards
(API overview, database, search, business metrics).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-08 03:08:54 +07:00
parent b392bc3570
commit d99dfbafbc
13 changed files with 770 additions and 2 deletions

View File

@@ -23,6 +23,7 @@
"@nestjs/throttler": "^6.5.0",
"@paralleldrive/cuid2": "^3.3.0",
"@prisma/client": "^6.0.0",
"@willsoto/nestjs-prometheus": "^6.1.0",
"bcrypt": "^6.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.15.1",
@@ -36,6 +37,7 @@
"passport-local": "^1.0.0",
"pino": "^9.0.0",
"pino-pretty": "^13.0.0",
"prom-client": "^15.1.3",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.0",
"sanitize-html": "^2.17.2",

View File

@@ -6,6 +6,7 @@ import { NotificationsModule } from '@modules/notifications';
import { PaymentsModule } from '@modules/payments';
import { SubscriptionsModule } from '@modules/subscriptions';
import { AdminModule } from '@modules/admin';
import { MetricsModule } from '@modules/metrics';
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { CqrsModule } from '@nestjs/cqrs';
@@ -24,6 +25,7 @@ import { AppController } from './app.controller';
PaymentsModule,
SubscriptionsModule,
AdminModule,
MetricsModule,
// ── Rate Limiting ──
// Default: 60 requests per 60 seconds per IP

View File

@@ -0,0 +1 @@
export { MetricsModule } from './metrics.module';

View File

@@ -0,0 +1,69 @@
import { Module } from '@nestjs/common';
import {
PrometheusModule,
makeCounterProvider,
makeHistogramProvider,
makeGaugeProvider,
} from '@willsoto/nestjs-prometheus';
@Module({
imports: [
PrometheusModule.register({
path: '/metrics',
defaultMetrics: { enabled: true },
}),
],
providers: [
// ── HTTP Metrics ──
makeHistogramProvider({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests 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],
}),
makeCounterProvider({
name: 'http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'route', 'status_code'],
}),
// ── Database Metrics ──
makeHistogramProvider({
name: 'db_query_duration_seconds',
help: 'Duration of database queries in seconds',
labelNames: ['operation', 'model'],
buckets: [0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5],
}),
makeGaugeProvider({
name: 'db_pool_active_connections',
help: 'Number of active database connections',
}),
// ── Search Metrics ──
makeHistogramProvider({
name: 'search_query_duration_seconds',
help: 'Duration of search queries in seconds',
labelNames: ['collection', 'type'],
buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1],
}),
// ── Business Metrics ──
makeCounterProvider({
name: 'listings_created_total',
help: 'Total number of listings created',
labelNames: ['category'],
}),
makeCounterProvider({
name: 'payments_processed_total',
help: 'Total number of payments processed',
labelNames: ['status', 'method'],
}),
makeGaugeProvider({
name: 'active_subscriptions',
help: 'Number of active subscriptions',
labelNames: ['plan'],
}),
],
exports: [PrometheusModule],
})
export class MetricsModule {}