UNPKG

backend-mcp

Version:

Generador automático de backends con Node.js, Express, Prisma y módulos configurables. Servidor MCP compatible con npx para agentes IA. Soporta PostgreSQL, MySQL, MongoDB y SQLite.

745 lines (603 loc) 19.5 kB
# 📦 Módulo monitoring **Versión:** 1.0.0 **Categoría:** infrastructure **Descripción:** Comprehensive monitoring and observability module with metrics, logging, tracing, and alerting ## 📊 Estado del Módulo | Componente | Estado | |------------|--------| | Script de inicialización | ✅ Disponible | | Templates | ❌ Faltante | | Ejemplos | ❌ Faltante | ## 🔗 Dependencias ### Opcionales - `database` - `cache` - `email` - `websockets` - `docker` - `ci` ## 📖 Documentación Completa # Monitoring Module Comprehensive monitoring and observability module for MCP Backend framework. ## Features - ✅ Application Performance Monitoring (APM) - ✅ Real-time metrics collection - ✅ Health checks and status monitoring - ✅ Error tracking and alerting - ✅ Request/response logging - ✅ Database query monitoring - ✅ Cache performance tracking - ✅ Custom metrics and dashboards - ✅ Distributed tracing - ✅ Log aggregation and analysis ## Installation This module is automatically installed when using the MCP Backend Generator. ## Configuration ### Environment Variables **General Configuration:** - `MONITORING_ENABLED` (optional) - Enable monitoring (default: true) - `MONITORING_INTERVAL` (optional) - Metrics collection interval in ms (default: 5000) - `MONITORING_RETENTION` (optional) - Data retention period in days (default: 30) **Prometheus Configuration:** - `PROMETHEUS_ENABLED` (optional) - Enable Prometheus metrics (default: true) - `PROMETHEUS_PORT` (optional) - Prometheus metrics port (default: 9090) - `PROMETHEUS_PATH` (optional) - Metrics endpoint path (default: /metrics) **Grafana Configuration:** - `GRAFANA_URL` (optional) - Grafana dashboard URL - `GRAFANA_API_KEY` (optional) - Grafana API key - `GRAFANA_ORG_ID` (optional) - Grafana organization ID **Alerting Configuration:** - `ALERT_WEBHOOK_URL` (optional) - Webhook URL for alerts - `ALERT_EMAIL_ENABLED` (optional) - Enable email alerts (default: false) - `ALERT_SLACK_WEBHOOK` (optional) - Slack webhook for alerts - `ALERT_THRESHOLD_ERROR_RATE` (optional) - Error rate threshold (default: 0.05) - `ALERT_THRESHOLD_RESPONSE_TIME` (optional) - Response time threshold in ms (default: 1000) **Logging Configuration:** - `LOG_LEVEL` (optional) - Log level (default: info) - `LOG_FORMAT` (optional) - Log format: json|text (default: json) - `LOG_FILE_ENABLED` (optional) - Enable file logging (default: true) - `LOG_FILE_PATH` (optional) - Log file path (default: ./logs) ### Configuration File ```typescript // src/config/monitoring.ts export const monitoringConfig = { enabled: process.env.MONITORING_ENABLED !== 'false', interval: parseInt(process.env.MONITORING_INTERVAL || '5000'), retention: parseInt(process.env.MONITORING_RETENTION || '30'), prometheus: { enabled: process.env.PROMETHEUS_ENABLED !== 'false', port: parseInt(process.env.PROMETHEUS_PORT || '9090'), path: process.env.PROMETHEUS_PATH || '/metrics' }, grafana: { url: process.env.GRAFANA_URL, apiKey: process.env.GRAFANA_API_KEY, orgId: process.env.GRAFANA_ORG_ID }, alerting: { webhookUrl: process.env.ALERT_WEBHOOK_URL, emailEnabled: process.env.ALERT_EMAIL_ENABLED === 'true', slackWebhook: process.env.ALERT_SLACK_WEBHOOK, thresholds: { errorRate: parseFloat(process.env.ALERT_THRESHOLD_ERROR_RATE || '0.05'), responseTime: parseInt(process.env.ALERT_THRESHOLD_RESPONSE_TIME || '1000') } }, logging: { level: process.env.LOG_LEVEL || 'info', format: process.env.LOG_FORMAT || 'json', fileEnabled: process.env.LOG_FILE_ENABLED !== 'false', filePath: process.env.LOG_FILE_PATH || './logs' } }; ``` ## Usage ### Basic Monitoring Setup ```typescript import { monitoringService } from './services/monitoring'; import express from 'express'; const app = express(); // Initialize monitoring await monitoringService.initialize(); // Add monitoring middleware app.use(monitoringService.middleware()); // Health check endpoint app.get('/health', monitoringService.healthCheck()); // Metrics endpoint app.get('/metrics', monitoringService.metricsHandler()); // Start metrics collection monitoringService.startMetricsCollection(); ``` ### Custom Metrics ```typescript import { metricsService } from './services/metrics'; // Counter metrics const requestCounter = metricsService.createCounter({ name: 'http_requests_total', help: 'Total number of HTTP requests', labelNames: ['method', 'route', 'status'] }); // Increment counter requestCounter.inc({ method: 'GET', route: '/api/users', status: '200' }); // Histogram metrics const responseTimeHistogram = metricsService.createHistogram({ name: 'http_request_duration_seconds', help: 'HTTP request duration in seconds', labelNames: ['method', 'route'], buckets: [0.1, 0.5, 1, 2, 5] }); // Record response time const timer = responseTimeHistogram.startTimer({ method: 'GET', route: '/api/users' }); // ... process request ... timer(); // Gauge metrics const activeConnectionsGauge = metricsService.createGauge({ name: 'active_connections', help: 'Number of active connections' }); // Set gauge value activeConnectionsGauge.set(42); activeConnectionsGauge.inc(); // Increment by 1 activeConnectionsGauge.dec(5); // Decrement by 5 ``` ### Performance Monitoring ```typescript import { performanceService } from './services/performance'; // Monitor function execution time const monitoredFunction = performanceService.monitor( 'user_creation', async (userData: any) => { return await userService.create(userData); } ); // Monitor with custom labels const result = await performanceService.time( 'database_query', { operation: 'select', table: 'users' }, async () => { return await db.users.findMany(); } ); // Manual timing const timer = performanceService.startTimer('custom_operation'); try { // ... your operation ... timer.success(); } catch (error) { timer.error(error); } ``` ### Health Checks ```typescript import { healthService } from './services/health'; // Register health checks healthService.register('database', async () => { try { await db.$queryRaw`SELECT 1`; return { status: 'healthy', details: 'Database connection OK' }; } catch (error) { return { status: 'unhealthy', details: error.message }; } }); healthService.register('redis', async () => { try { await redis.ping(); return { status: 'healthy', details: 'Redis connection OK' }; } catch (error) { return { status: 'unhealthy', details: error.message }; } }); healthService.register('external_api', async () => { try { const response = await fetch('https://api.example.com/health'); if (response.ok) { return { status: 'healthy', details: 'External API responding' }; } else { return { status: 'degraded', details: `API returned ${response.status}` }; } } catch (error) { return { status: 'unhealthy', details: error.message }; } }); // Get overall health status const health = await healthService.getStatus(); console.log(health); // { // status: 'healthy', // timestamp: '2023-12-01T10:00:00.000Z', // checks: { // database: { status: 'healthy', details: 'Database connection OK' }, // redis: { status: 'healthy', details: 'Redis connection OK' }, // external_api: { status: 'healthy', details: 'External API responding' } // } // } ``` ### Error Tracking ```typescript import { errorService } from './services/error'; // Track errors automatically process.on('uncaughtException', (error) => { errorService.captureException(error, { level: 'fatal', tags: { source: 'uncaught_exception' } }); }); process.on('unhandledRejection', (reason, promise) => { errorService.captureException(reason, { level: 'error', tags: { source: 'unhandled_rejection' }, extra: { promise: promise.toString() } }); }); // Manual error tracking try { await riskyOperation(); } catch (error) { errorService.captureException(error, { level: 'error', tags: { operation: 'risky_operation', user_id: '123' }, extra: { context: 'additional context' } }); throw error; } // Track custom events errorService.captureMessage('User login attempt', { level: 'info', tags: { event: 'login', user_id: '123' } }); ``` ### Request/Response Logging ```typescript import { loggingService } from './services/logging'; import { Request, Response, NextFunction } from 'express'; // Request logging middleware export function requestLoggingMiddleware() { return (req: Request, res: Response, next: NextFunction) => { const startTime = Date.now(); const requestId = generateRequestId(); // Add request ID to headers req.headers['x-request-id'] = requestId; res.setHeader('x-request-id', requestId); // Log request loggingService.info('HTTP Request', { requestId, method: req.method, url: req.url, userAgent: req.get('User-Agent'), ip: req.ip, timestamp: new Date().toISOString() }); // Override res.end to log response const originalEnd = res.end; res.end = function(chunk: any, encoding?: any) { const duration = Date.now() - startTime; loggingService.info('HTTP Response', { requestId, method: req.method, url: req.url, statusCode: res.statusCode, duration, contentLength: res.get('Content-Length'), timestamp: new Date().toISOString() }); // Update metrics requestCounter.inc({ method: req.method, route: req.route?.path || req.url, status: res.statusCode.toString() }); responseTimeHistogram.observe( { method: req.method, route: req.route?.path || req.url }, duration / 1000 ); originalEnd.call(this, chunk, encoding); }; next(); }; } ``` ### Database Query Monitoring ```typescript import { Prisma } from '@prisma/client'; import { queryService } from './services/query'; // Prisma middleware for query monitoring const queryMonitoringMiddleware: Prisma.Middleware = async (params, next) => { const startTime = Date.now(); try { const result = await next(params); const duration = Date.now() - startTime; queryService.recordQuery({ model: params.model, action: params.action, duration, success: true, timestamp: new Date() }); // Alert on slow queries if (duration > 1000) { queryService.alertSlowQuery({ model: params.model, action: params.action, duration, args: params.args }); } return result; } catch (error) { const duration = Date.now() - startTime; queryService.recordQuery({ model: params.model, action: params.action, duration, success: false, error: error.message, timestamp: new Date() }); throw error; } }; // Apply middleware to Prisma client prisma.$use(queryMonitoringMiddleware); ``` ### Alerting System ```typescript import { alertService } from './services/alert'; // Define alert rules alertService.addRule({ name: 'high_error_rate', condition: (metrics) => metrics.errorRate > 0.05, message: 'Error rate is above 5%', severity: 'critical', cooldown: 300000 // 5 minutes }); alertService.addRule({ name: 'slow_response_time', condition: (metrics) => metrics.avgResponseTime > 1000, message: 'Average response time is above 1 second', severity: 'warning', cooldown: 600000 // 10 minutes }); alertService.addRule({ name: 'database_connection_failed', condition: (health) => health.checks.database?.status === 'unhealthy', message: 'Database connection is unhealthy', severity: 'critical', cooldown: 60000 // 1 minute }); // Manual alerts alertService.sendAlert({ title: 'Custom Alert', message: 'Something important happened', severity: 'info', tags: { component: 'user_service' } }); ``` ### Distributed Tracing ```typescript import { tracingService } from './services/tracing'; // Start a trace const trace = tracingService.startTrace('user_registration'); try { // Add span for validation const validationSpan = trace.startSpan('validate_user_data'); await validateUserData(userData); validationSpan.finish(); // Add span for database operation const dbSpan = trace.startSpan('create_user_db'); const user = await userService.create(userData); dbSpan.setTag('user_id', user.id); dbSpan.finish(); // Add span for email sending const emailSpan = trace.startSpan('send_welcome_email'); await emailService.sendWelcomeEmail(user.email); emailSpan.finish(); trace.setTag('success', true); trace.finish(); return user; } catch (error) { trace.setTag('error', true); trace.setTag('error_message', error.message); trace.finish(); throw error; } ``` ### Dashboard Integration ```typescript // src/utils/monitoring/dashboard.ts import { dashboardService } from '../services/dashboard'; export async function createGrafanaDashboard() { const dashboard = { title: 'MCP Backend Monitoring', panels: [ { title: 'Request Rate', type: 'graph', targets: [{ expr: 'rate(http_requests_total[5m])', legendFormat: '{{method}} {{route}}' }] }, { title: 'Response Time', type: 'graph', targets: [{ expr: 'histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))', legendFormat: '95th percentile' }] }, { title: 'Error Rate', type: 'singlestat', targets: [{ expr: 'rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m])', legendFormat: 'Error Rate' }] }, { title: 'Database Connections', type: 'graph', targets: [{ expr: 'database_connections_active', legendFormat: 'Active Connections' }] } ] }; await dashboardService.create(dashboard); } ``` ## Metrics Collection ### System Metrics ```typescript // src/collectors/system.ts import os from 'os'; import process from 'process'; import { metricsService } from '../services/metrics'; const systemMetrics = { cpuUsage: metricsService.createGauge({ name: 'system_cpu_usage_percent', help: 'System CPU usage percentage' }), memoryUsage: metricsService.createGauge({ name: 'system_memory_usage_bytes', help: 'System memory usage in bytes', labelNames: ['type'] }), diskUsage: metricsService.createGauge({ name: 'system_disk_usage_bytes', help: 'System disk usage in bytes', labelNames: ['type'] }) }; export function collectSystemMetrics() { // CPU usage const cpus = os.cpus(); let totalIdle = 0; let totalTick = 0; cpus.forEach(cpu => { for (const type in cpu.times) { totalTick += cpu.times[type]; } totalIdle += cpu.times.idle; }); const idle = totalIdle / cpus.length; const total = totalTick / cpus.length; const usage = 100 - ~~(100 * idle / total); systemMetrics.cpuUsage.set(usage); // Memory usage const totalMem = os.totalmem(); const freeMem = os.freemem(); const usedMem = totalMem - freeMem; systemMetrics.memoryUsage.set({ type: 'total' }, totalMem); systemMetrics.memoryUsage.set({ type: 'used' }, usedMem); systemMetrics.memoryUsage.set({ type: 'free' }, freeMem); // Process memory const processMemory = process.memoryUsage(); systemMetrics.memoryUsage.set({ type: 'process_rss' }, processMemory.rss); systemMetrics.memoryUsage.set({ type: 'process_heap_used' }, processMemory.heapUsed); systemMetrics.memoryUsage.set({ type: 'process_heap_total' }, processMemory.heapTotal); } // Collect metrics every 5 seconds setInterval(collectSystemMetrics, 5000); ``` ### Application Metrics ```typescript // src/collectors/application.ts import { metricsService } from '../services/metrics'; const appMetrics = { activeUsers: metricsService.createGauge({ name: 'app_active_users', help: 'Number of active users' }), businessMetrics: metricsService.createCounter({ name: 'app_business_events_total', help: 'Total business events', labelNames: ['event_type'] }) }; // Track business events export function trackBusinessEvent(eventType: string, metadata?: any) { appMetrics.businessMetrics.inc({ event_type: eventType }); loggingService.info('Business Event', { eventType, metadata, timestamp: new Date().toISOString() }); } // Usage examples trackBusinessEvent('user_registered'); trackBusinessEvent('order_created', { orderId: '123', amount: 99.99 }); trackBusinessEvent('payment_processed', { paymentId: 'pay_123' }); ``` ## Testing ```typescript // tests/monitoring.test.ts import { monitoringService } from '../src/services/monitoring'; import { metricsService } from '../src/services/metrics'; describe('Monitoring Service', () => { beforeEach(async () => { await monitoringService.reset(); }); it('should collect metrics', async () => { const counter = metricsService.createCounter({ name: 'test_counter', help: 'Test counter' }); counter.inc(); counter.inc(); const metrics = await metricsService.getMetrics(); expect(metrics).toContain('test_counter 2'); }); it('should track response times', async () => { const histogram = metricsService.createHistogram({ name: 'test_duration', help: 'Test duration' }); const timer = histogram.startTimer(); await new Promise(resolve => setTimeout(resolve, 100)); timer(); const metrics = await metricsService.getMetrics(); expect(metrics).toContain('test_duration_bucket'); }); it('should perform health checks', async () => { const health = await monitoringService.getHealth(); expect(health.status).toBe('healthy'); expect(health.checks).toBeDefined(); }); }); ``` ```bash npm test -- monitoring ``` ## Dependencies - prom-client (Prometheus metrics) - winston (Logging) - express-prom-bundle (Express metrics) - node-cron (Scheduled tasks) - nodemailer (Email alerts) - axios (HTTP requests) ## Integration - Integrates with all modules for comprehensive monitoring - Provides middleware for Express applications - Monitors database queries and cache operations - Tracks business metrics and user activities ## Error Handling - `MonitoringInitializationError`: Failed to initialize monitoring - `MetricsCollectionError`: Failed to collect metrics - `AlertDeliveryError`: Failed to send alert - `HealthCheckError`: Health check failed - `DashboardError`: Dashboard operation failed ## Best Practices 1. **Metric Naming**: Use consistent, descriptive metric names 2. **Label Management**: Keep label cardinality low to avoid performance issues 3. **Alert Fatigue**: Set appropriate thresholds and cooldowns 4. **Data Retention**: Configure appropriate retention policies 5. **Performance Impact**: Monitor the monitoring system itself 6. **Security**: Secure metrics endpoints and dashboards 7. **Documentation**: Document custom metrics and alerts ## License MIT ## 🔗 Enlaces - [Volver al índice de módulos](./README.md) - [Documentación principal](../README.md) - [Código fuente](../../modules/monitoring/)