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
Markdown
# 📦 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/)