Skip to content

Commit f31cb6d

Browse files
Merge pull request #319 from Tebrihk/MetricsEndpoint
Metrics Endpoint
2 parents dff8c2a + 00f1309 commit f31cb6d

2 files changed

Lines changed: 195 additions & 2 deletions

File tree

src/monitoring/metrics/metrics-collection.service.ts

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import { Injectable, OnModuleInit } from '@nestjs/common';
2-
import { Registry, collectDefaultMetrics, Histogram, Gauge } from 'prom-client';
2+
import { Registry, collectDefaultMetrics, Histogram, Gauge, Counter } from 'prom-client';
33

44
@Injectable()
55
export class MetricsCollectionService implements OnModuleInit {
66
private registry: Registry;
77
public httpRequestDuration: Histogram;
88
public dbQueryDuration: Histogram;
99
public activeConnections: Gauge;
10+
public userRegistrations: Counter;
11+
public assessmentCompletions: Counter;
12+
public learningPathProgress: Gauge;
13+
public cacheHitRate: Gauge;
14+
public queueProcessingTime: Histogram;
15+
public emailCampaignsSent: Counter;
16+
public backupOperations: Counter;
1017

1118
constructor() {
1219
this.registry = new Registry();
@@ -35,6 +42,63 @@ export class MetricsCollectionService implements OnModuleInit {
3542
help: 'Number of active connections',
3643
registers: [this.registry],
3744
});
45+
46+
// User Registrations Counter
47+
this.userRegistrations = new Counter({
48+
name: 'user_registrations_total',
49+
help: 'Total number of user registrations',
50+
labelNames: ['user_type', 'source'],
51+
registers: [this.registry],
52+
});
53+
54+
// Assessment Completions Counter
55+
this.assessmentCompletions = new Counter({
56+
name: 'assessment_completions_total',
57+
help: 'Total number of assessment completions',
58+
labelNames: ['assessment_type', 'difficulty'],
59+
registers: [this.registry],
60+
});
61+
62+
// Learning Path Progress Gauge
63+
this.learningPathProgress = new Gauge({
64+
name: 'learning_path_progress_percentage',
65+
help: 'Average learning path progress percentage',
66+
labelNames: ['path_id', 'user_id'],
67+
registers: [this.registry],
68+
});
69+
70+
// Cache Hit Rate Gauge
71+
this.cacheHitRate = new Gauge({
72+
name: 'cache_hit_rate_percentage',
73+
help: 'Cache hit rate percentage',
74+
labelNames: ['cache_type'],
75+
registers: [this.registry],
76+
});
77+
78+
// Queue Processing Time Histogram
79+
this.queueProcessingTime = new Histogram({
80+
name: 'queue_processing_duration_seconds',
81+
help: 'Duration of queue job processing in seconds',
82+
labelNames: ['queue_name', 'job_type'],
83+
buckets: [0.1, 0.5, 1, 2, 5, 10, 30],
84+
registers: [this.registry],
85+
});
86+
87+
// Email Campaigns Sent Counter
88+
this.emailCampaignsSent = new Counter({
89+
name: 'email_campaigns_sent_total',
90+
help: 'Total number of email campaigns sent',
91+
labelNames: ['campaign_type', 'status'],
92+
registers: [this.registry],
93+
});
94+
95+
// Backup Operations Counter
96+
this.backupOperations = new Counter({
97+
name: 'backup_operations_total',
98+
help: 'Total number of backup operations',
99+
labelNames: ['operation_type', 'status'],
100+
registers: [this.registry],
101+
});
38102
}
39103

40104
onModuleInit() {
@@ -57,4 +121,33 @@ export class MetricsCollectionService implements OnModuleInit {
57121
recordDbQuery(queryType: string, table: string, duration: number) {
58122
this.dbQueryDuration.observe({ query_type: queryType, table }, duration);
59123
}
124+
125+
// Custom business metrics methods
126+
recordUserRegistration(userType: string, source: string) {
127+
this.userRegistrations.inc({ user_type: userType, source });
128+
}
129+
130+
recordAssessmentCompletion(assessmentType: string, difficulty: string) {
131+
this.assessmentCompletions.inc({ assessment_type: assessmentType, difficulty });
132+
}
133+
134+
updateLearningPathProgress(pathId: string, userId: string, progress: number) {
135+
this.learningPathProgress.set({ path_id: pathId, user_id: userId }, progress);
136+
}
137+
138+
updateCacheHitRate(cacheType: string, hitRate: number) {
139+
this.cacheHitRate.set({ cache_type: cacheType }, hitRate);
140+
}
141+
142+
recordQueueProcessingTime(queueName: string, jobType: string, duration: number) {
143+
this.queueProcessingTime.observe({ queue_name: queueName, job_type: jobType }, duration);
144+
}
145+
146+
recordEmailCampaignSent(campaignType: string, status: string) {
147+
this.emailCampaignsSent.inc({ campaign_type: campaignType, status });
148+
}
149+
150+
recordBackupOperation(operationType: string, status: string) {
151+
this.backupOperations.inc({ operation_type: operationType, status });
152+
}
60153
}

src/monitoring/monitoring.controller.ts

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Controller, Get, Res, Query } from '@nestjs/common';
12
import { Controller, Get, Res, VERSION_NEUTRAL, Version } from '@nestjs/common';
23
import { MetricsCollectionService } from './metrics/metrics-collection.service';
34
import { Response } from 'express';
@@ -9,7 +10,7 @@ export class MonitoringController {
910
constructor(
1011
private readonly metricsService: MetricsCollectionService,
1112
private readonly scheduledTaskMonitoringService: ScheduledTaskMonitoringService,
12-
) {}
13+
) { }
1314

1415
@Get()
1516
async getMetrics(@Res() res: Response) {
@@ -18,6 +19,105 @@ export class MonitoringController {
1819
res.send(metrics);
1920
}
2021

22+
@Get('unified')
23+
async getUnifiedMetrics(
24+
@Query('format') format?: string,
25+
@Query('include') include?: string,
26+
@Query('exclude') exclude?: string,
27+
) {
28+
const includeTypes = include?.split(',').map(s => s.trim()) || [];
29+
const excludeTypes = exclude?.split(',').map(s => s.trim()) || [];
30+
31+
// Get base Prometheus metrics
32+
const prometheusMetrics = await this.metricsService.getMetrics();
33+
34+
// Get scheduled tasks dashboard
35+
const scheduledTasksMetrics = this.scheduledTaskMonitoringService.getDashboard();
36+
37+
// Aggregate metrics from different sources
38+
const unifiedMetrics = {
39+
prometheus: prometheusMetrics,
40+
scheduledTasks: scheduledTasksMetrics,
41+
timestamp: new Date().toISOString(),
42+
metadata: {
43+
totalMetrics: prometheusMetrics.split('\n').filter(line => line && !line.startsWith('#')).length,
44+
includeTypes,
45+
excludeTypes,
46+
}
47+
};
48+
49+
// Return in requested format
50+
if (format === 'json') {
51+
return unifiedMetrics;
52+
}
53+
54+
// Default to Prometheus format
55+
const metrics = await this.metricsService.getMetrics();
56+
return metrics;
57+
}
58+
59+
@Get('health')
60+
async getMetricsHealth() {
61+
return {
62+
status: 'healthy',
63+
timestamp: new Date().toISOString(),
64+
services: {
65+
metricsCollection: 'active',
66+
scheduledTasks: 'active',
67+
},
68+
registry: {
69+
metricsCount: (await this.metricsService.getMetrics()).split('\n').filter(line => line && !line.startsWith('#')).length,
70+
}
71+
};
72+
}
73+
74+
@Get('custom')
75+
async getCustomMetrics(@Query('type') type?: string) {
76+
const customMetrics = {
77+
user_registrations: {
78+
name: 'user_registrations_total',
79+
help: 'Total number of user registrations',
80+
type: 'counter',
81+
},
82+
assessment_completions: {
83+
name: 'assessment_completions_total',
84+
help: 'Total number of assessment completions',
85+
type: 'counter',
86+
},
87+
learning_path_progress: {
88+
name: 'learning_path_progress_percentage',
89+
help: 'Average learning path progress percentage',
90+
type: 'gauge',
91+
},
92+
cache_hit_rate: {
93+
name: 'cache_hit_rate_percentage',
94+
help: 'Cache hit rate percentage',
95+
type: 'gauge',
96+
},
97+
queue_processing_time: {
98+
name: 'queue_processing_duration_seconds',
99+
help: 'Duration of queue job processing in seconds',
100+
type: 'histogram',
101+
},
102+
email_campaigns_sent: {
103+
name: 'email_campaigns_sent_total',
104+
help: 'Total number of email campaigns sent',
105+
type: 'counter',
106+
},
107+
backup_operations: {
108+
name: 'backup_operations_total',
109+
help: 'Total number of backup operations',
110+
type: 'counter',
111+
},
112+
};
113+
114+
if (type) {
115+
return customMetrics[type] || { error: 'Metric type not found' };
116+
}
117+
118+
return customMetrics;
119+
}
120+
21121
@Get('scheduled-tasks/dashboard')
22122
getScheduledTasksDashboard() {
23123
return this.scheduledTaskMonitoringService.getDashboard();

0 commit comments

Comments
 (0)