const client = require('prom-client'); // Create a Registry to register the metrics const register = new client.Registry(); // Add default metrics (CPU, memory, etc.) client.collectDefaultMetrics({ register, prefix: 'influence_app_' }); // Custom metrics for the Influence application // Email metrics const emailsSentTotal = new client.Counter({ name: 'influence_emails_sent_total', help: 'Total number of emails sent successfully', labelNames: ['campaign_id', 'representative_level'], registers: [register] }); const emailsFailedTotal = new client.Counter({ name: 'influence_emails_failed_total', help: 'Total number of emails that failed to send', labelNames: ['campaign_id', 'error_type'], registers: [register] }); const emailQueueSize = new client.Gauge({ name: 'influence_email_queue_size', help: 'Current number of emails in the queue', registers: [register] }); const emailSendDuration = new client.Histogram({ name: 'influence_email_send_duration_seconds', help: 'Time taken to send an email', labelNames: ['campaign_id'], buckets: [0.1, 0.5, 1, 2, 5, 10], registers: [register] }); // API metrics const httpRequestDuration = new client.Histogram({ name: 'influence_http_request_duration_seconds', help: 'Duration of HTTP requests in seconds', labelNames: ['method', 'route', 'status_code'], buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5], registers: [register] }); const httpRequestsTotal = new client.Counter({ name: 'influence_http_requests_total', help: 'Total number of HTTP requests', labelNames: ['method', 'route', 'status_code'], registers: [register] }); // User metrics const activeUsersGauge = new client.Gauge({ name: 'influence_active_users_total', help: 'Number of currently active users', registers: [register] }); const userRegistrationsTotal = new client.Counter({ name: 'influence_user_registrations_total', help: 'Total number of user registrations', registers: [register] }); const loginAttemptsTotal = new client.Counter({ name: 'influence_login_attempts_total', help: 'Total number of login attempts', labelNames: ['status'], registers: [register] }); // Campaign metrics const campaignCreationsTotal = new client.Counter({ name: 'influence_campaign_creations_total', help: 'Total number of campaigns created', registers: [register] }); const campaignParticipationTotal = new client.Counter({ name: 'influence_campaign_participation_total', help: 'Total number of campaign participations', labelNames: ['campaign_id'], registers: [register] }); const activeCampaignsGauge = new client.Gauge({ name: 'influence_active_campaigns_total', help: 'Number of currently active campaigns', registers: [register] }); const campaignConversionRate = new client.Gauge({ name: 'influence_campaign_conversion_rate', help: 'Campaign conversion rate (participants / visitors)', labelNames: ['campaign_id'], registers: [register] }); // Representative metrics const representativeLookupTotal = new client.Counter({ name: 'influence_representative_lookup_total', help: 'Total number of representative lookups', labelNames: ['lookup_type'], registers: [register] }); const representativeResponsesTotal = new client.Counter({ name: 'influence_representative_responses_total', help: 'Total number of representative responses received', labelNames: ['representative_level'], registers: [register] }); const representativeResponseRate = new client.Gauge({ name: 'influence_representative_response_rate', help: 'Representative response rate (responses / emails sent)', labelNames: ['representative_level'], registers: [register] }); // Rate limiting metrics const rateLimitHitsTotal = new client.Counter({ name: 'influence_rate_limit_hits_total', help: 'Total number of rate limit hits', labelNames: ['endpoint', 'limit_type'], registers: [register] }); // External service metrics const externalServiceRequestsTotal = new client.Counter({ name: 'influence_external_service_requests_total', help: 'Total number of requests to external services', labelNames: ['service', 'status'], registers: [register] }); const externalServiceLatency = new client.Histogram({ name: 'influence_external_service_latency_seconds', help: 'Latency of external service requests', labelNames: ['service'], buckets: [0.1, 0.5, 1, 2, 5, 10], registers: [register] }); // Geographic metrics const participationByPostalCode = new client.Counter({ name: 'influence_participation_by_postal_code_total', help: 'Participation count by postal code prefix', labelNames: ['postal_prefix'], registers: [register] }); // Middleware to track HTTP requests const metricsMiddleware = (req, res, next) => { const start = Date.now(); res.on('finish', () => { const duration = (Date.now() - start) / 1000; const route = req.route ? req.route.path : req.path; const labels = { method: req.method, route: route, status_code: res.statusCode }; httpRequestDuration.observe(labels, duration); httpRequestsTotal.inc(labels); }); next(); }; // Helper functions for common metric operations const metrics = { // Email metrics recordEmailSent: (campaignId, representativeLevel) => { emailsSentTotal.inc({ campaign_id: campaignId, representative_level: representativeLevel }); }, recordEmailFailed: (campaignId, errorType) => { emailsFailedTotal.inc({ campaign_id: campaignId, error_type: errorType }); }, setEmailQueueSize: (size) => { emailQueueSize.set(size); }, observeEmailSendDuration: (campaignId, durationSeconds) => { emailSendDuration.observe({ campaign_id: campaignId }, durationSeconds); }, // User metrics setActiveUsers: (count) => { activeUsersGauge.set(count); }, recordUserRegistration: () => { userRegistrationsTotal.inc(); }, recordLoginAttempt: (success) => { loginAttemptsTotal.inc({ status: success ? 'success' : 'failed' }); }, // Campaign metrics recordCampaignCreation: () => { campaignCreationsTotal.inc(); }, recordCampaignParticipation: (campaignId) => { campaignParticipationTotal.inc({ campaign_id: campaignId }); }, setActiveCampaigns: (count) => { activeCampaignsGauge.set(count); }, setCampaignConversionRate: (campaignId, rate) => { campaignConversionRate.set({ campaign_id: campaignId }, rate); }, // Representative metrics recordRepresentativeLookup: (lookupType) => { representativeLookupTotal.inc({ lookup_type: lookupType }); }, recordRepresentativeResponse: (representativeLevel) => { representativeResponsesTotal.inc({ representative_level: representativeLevel }); }, setRepresentativeResponseRate: (representativeLevel, rate) => { representativeResponseRate.set({ representative_level: representativeLevel }, rate); }, // Rate limiting metrics recordRateLimitHit: (endpoint, limitType) => { rateLimitHitsTotal.inc({ endpoint, limit_type: limitType }); }, // External service metrics recordExternalServiceRequest: (service, success) => { externalServiceRequestsTotal.inc({ service, status: success ? 'success' : 'failed' }); }, observeExternalServiceLatency: (service, durationSeconds) => { externalServiceLatency.observe({ service }, durationSeconds); }, // Geographic metrics recordParticipationByPostalCode: (postalCode) => { const prefix = postalCode.substring(0, 3).toUpperCase(); participationByPostalCode.inc({ postal_prefix: prefix }); }, // Get metrics endpoint handler getMetrics: async () => { return await register.metrics(); }, // Get content type for metrics getContentType: () => { return register.contentType; }, // Middleware middleware: metricsMiddleware }; module.exports = metrics;