Documentación
Monitoreo y Métricas

Monitoreo y Métricas

Fox Framework incluye un sistema completo de monitoreo y métricas que te permite observar el comportamiento de tu aplicación en tiempo real, detectar problemas antes de que afecten a los usuarios y tomar decisiones basadas en datos.

Características del Sistema de Monitoreo

  • Métricas en tiempo real: Recolección continua de métricas de rendimiento
  • Health checks: Verificación automática del estado de la aplicación
  • Alertas inteligentes: Notificaciones proactivas sobre problemas
  • Dashboards: Visualización de métricas y tendencias
  • Distributed tracing: Seguimiento de requests a través de microservicios
  • Error tracking: Captura y análisis de errores
  • Performance profiling: Análisis detallado de rendimiento

Configuración Inicial

Instalación de Dependencias

npm install @foxframework/monitoring prometheus-client jaeger-client winston-elasticsearch

Configuración Base

import { MonitoringModule } from '@foxframework/monitoring';
import { FoxFactory } from '@foxframework/core';
 
const server = FoxFactory.createServer({
  port: 3000,
  monitoring: {
    // Métricas Prometheus
    metrics: {
      enabled: true,
      endpoint: '/metrics',
      prefix: 'fox_app_',
      collectDefaultMetrics: true
    },
    
    // Health checks
    health: {
      enabled: true,
      endpoint: '/health',
      interval: 30000, // 30 segundos
      timeout: 5000    // 5 segundos
    },
    
    // Distributed tracing
    tracing: {
      enabled: true,
      jaeger: {
        endpoint: process.env.JAEGER_ENDPOINT,
        serviceName: 'fox-framework-app',
        samplingRate: 0.1 // 10% de traces
      }
    },
    
    // Error tracking
    errorTracking: {
      enabled: true,
      sentry: {
        dsn: process.env.SENTRY_DSN,
        environment: process.env.NODE_ENV
      }
    }
  }
});

Métricas de Aplicación

Métricas Automáticas

Fox Framework recolecta automáticamente estas métricas:

// Métricas HTTP
- http_requests_total: Contador de requests HTTP
- http_request_duration_seconds: Duración de requests
- http_response_size_bytes: Tamaño de respuestas
- http_requests_in_flight: Requests activos
 
// Métricas del sistema
- nodejs_heap_used_bytes: Memoria heap utilizada
- nodejs_heap_total_bytes: Memoria heap total
- nodejs_external_memory_bytes: Memoria externa
- nodejs_gc_duration_seconds: Duración de garbage collection
 
// Métricas de base de datos
- db_connections_active: Conexiones activas
- db_connections_idle: Conexiones inactivas
- db_query_duration_seconds: Duración de queries
- db_query_errors_total: Errores de queries

Métricas Personalizadas

import { MetricsCollector } from '@foxframework/monitoring';
 
export class BusinessMetrics {
  private metrics: MetricsCollector;
 
  constructor() {
    this.metrics = new MetricsCollector();
    this.setupMetrics();
  }
 
  private setupMetrics() {
    // Contador de usuarios registrados
    this.metrics.counter('users_registered_total', 'Total users registered', ['source']);
    
    // Histograma de tiempo de login
    this.metrics.histogram('user_login_duration_seconds', 'User login duration');
    
    // Gauge de usuarios activos
    this.metrics.gauge('users_active_current', 'Currently active users');
    
    // Contador de órdenes procesadas
    this.metrics.counter('orders_processed_total', 'Total orders processed', ['status']);
  }
 
  // Incrementar usuarios registrados
  incrementUsersRegistered(source: string) {
    this.metrics.incrementCounter('users_registered_total', { source });
  }
 
  // Registrar tiempo de login
  recordLoginDuration(duration: number) {
    this.metrics.observeHistogram('user_login_duration_seconds', duration);
  }
 
  // Actualizar usuarios activos
  setActiveUsers(count: number) {
    this.metrics.setGauge('users_active_current', count);
  }
 
  // Incrementar órdenes procesadas
  incrementOrdersProcessed(status: 'success' | 'failed') {
    this.metrics.incrementCounter('orders_processed_total', { status });
  }
}

Uso en Controladores

import { FoxController, Request, Response } from '@foxframework/core';
import { Metrics } from '@foxframework/monitoring';
 
export class UserController extends FoxController {
  constructor(
    private userService: UserService,
    private businessMetrics: BusinessMetrics
  ) {
    super();
  }
 
  @Metrics.timed('user_registration_duration')
  async register(req: Request, res: Response): Promise<void> {
    const timer = Metrics.startTimer('user_registration_process');
    
    try {
      const user = await this.userService.create(req.body);
      
      // Registrar métrica de negocio
      this.businessMetrics.incrementUsersRegistered(req.body.source || 'web');
      
      timer.end({ status: 'success' });
      
      return res.status(201).json({
        success: true,
        data: user
      });
    } catch (error) {
      timer.end({ status: 'error' });
      
      // Métrica de error
      Metrics.incrementCounter('user_registration_errors_total', {
        error_type: error.constructor.name
      });
      
      throw error;
    }
  }
}

Health Checks

Health Check Básico

import { HealthCheck, HealthStatus } from '@foxframework/monitoring';
 
export class AppHealthCheck extends HealthCheck {
  constructor(
    private databaseService: DatabaseService,
    private cacheService: CacheService,
    private emailService: EmailService
  ) {
    super();
  }
 
  async checkHealth(): Promise<HealthStatus> {
    const checks = await this.runChecks([
      this.checkDatabase(),
      this.checkCache(),
      this.checkEmailService(),
      this.checkExternalAPI()
    ]);
 
    return {
      status: this.determineOverallStatus(checks),
      timestamp: new Date().toISOString(),
      version: process.env.APP_VERSION || 'unknown',
      uptime: process.uptime(),
      checks
    };
  }
 
  private async checkDatabase(): Promise<CheckResult> {
    const start = Date.now();
    
    try {
      await this.databaseService.ping();
      
      return {
        name: 'database',
        status: 'healthy',
        responseTime: Date.now() - start,
        details: {
          type: 'postgresql',
          connections: await this.databaseService.getConnectionCount()
        }
      };
    } catch (error) {
      return {
        name: 'database',
        status: 'unhealthy',
        responseTime: Date.now() - start,
        error: error.message
      };
    }
  }
 
  private async checkCache(): Promise<CheckResult> {
    const start = Date.now();
    
    try {
      await this.cacheService.ping();
      
      return {
        name: 'cache',
        status: 'healthy',
        responseTime: Date.now() - start,
        details: {
          type: 'redis',
          memory: await this.cacheService.getMemoryUsage()
        }
      };
    } catch (error) {
      return {
        name: 'cache',
        status: 'degraded', // Cache no es crítico
        responseTime: Date.now() - start,
        error: error.message
      };
    }
  }
}

Health Check Avanzado

export class DetailedHealthCheck extends HealthCheck {
  // Health check con diferentes niveles de detalle
  async checkHealth(level: 'basic' | 'detailed' | 'full' = 'basic'): Promise<HealthStatus> {
    const checks: CheckResult[] = [];
    
    // Checks básicos (siempre incluidos)
    checks.push(await this.checkBasicHealth());
    
    if (level === 'detailed' || level === 'full') {
      checks.push(
        await this.checkDatabasePerformance(),
        await this.checkMemoryUsage(),
        await this.checkDiskSpace()
      );
    }
    
    if (level === 'full') {
      checks.push(
        await this.checkNetworkLatency(),
        await this.checkDependencyHealth(),
        await this.checkSecurityStatus()
      );
    }
 
    return {
      status: this.determineOverallStatus(checks),
      timestamp: new Date().toISOString(),
      level,
      checks
    };
  }
 
  private async checkDatabasePerformance(): Promise<CheckResult> {
    const start = Date.now();
    
    try {
      const stats = await this.databaseService.getPerformanceStats();
      const responseTime = Date.now() - start;
      
      const status = responseTime > 1000 ? 'degraded' : 'healthy';
      
      return {
        name: 'database_performance',
        status,
        responseTime,
        details: {
          slowQueries: stats.slowQueries,
          avgQueryTime: stats.avgQueryTime,
          activeConnections: stats.activeConnections
        }
      };
    } catch (error) {
      return {
        name: 'database_performance',
        status: 'unhealthy',
        responseTime: Date.now() - start,
        error: error.message
      };
    }
  }
}

Distributed Tracing

Configuración de Jaeger

import { JaegerTracer } from '@foxframework/monitoring';
 
const tracer = new JaegerTracer({
  serviceName: 'fox-framework-app',
  jaeger: {
    endpoint: process.env.JAEGER_ENDPOINT || 'http://localhost:14268/api/traces',
    tags: {
      'fox-framework.version': '1.0.0',
      'environment': process.env.NODE_ENV
    }
  },
  sampler: {
    type: 'probabilistic',
    param: 0.1 // 10% de sampling
  }
});

Instrumentación Automática

import { TraceMiddleware } from '@foxframework/monitoring';
 
// Middleware automático para HTTP requests
app.use(TraceMiddleware.http({
  tracer,
  ignoreRoutes: ['/health', '/metrics'],
  includeHeaders: ['user-agent', 'authorization'],
  includeBodies: process.env.NODE_ENV === 'development'
}));
 
// Instrumentación automática de base de datos
TraceMiddleware.database(tracer, {
  includeQueryParameters: false, // Por seguridad
  slowQueryThreshold: 1000 // ms
});

Trazas Manuales

import { Tracer } from '@foxframework/monitoring';
 
export class OrderService {
  async processOrder(orderId: string): Promise<Order> {
    const span = Tracer.startSpan('order.process', {
      tags: {
        'order.id': orderId,
        'service': 'order-service'
      }
    });
 
    try {
      // Validar orden
      const validationSpan = Tracer.startSpan('order.validate', { childOf: span });
      const order = await this.validateOrder(orderId);
      validationSpan.setTag('order.status', order.status);
      validationSpan.finish();
 
      // Procesar pago
      const paymentSpan = Tracer.startSpan('payment.process', { childOf: span });
      await this.processPayment(order);
      paymentSpan.finish();
 
      // Actualizar inventario
      const inventorySpan = Tracer.startSpan('inventory.update', { childOf: span });
      await this.updateInventory(order);
      inventorySpan.finish();
 
      span.setTag('order.processed', true);
      span.log({ event: 'order_completed', orderId });
      
      return order;
    } catch (error) {
      span.setTag('error', true);
      span.log({ event: 'error', message: error.message });
      throw error;
    } finally {
      span.finish();
    }
  }
}

Error Tracking

Configuración de Sentry

import { ErrorTracker } from '@foxframework/monitoring';
 
const errorTracker = new ErrorTracker({
  sentry: {
    dsn: process.env.SENTRY_DSN,
    environment: process.env.NODE_ENV,
    release: process.env.APP_VERSION,
    beforeSend: (event, hint) => {
      // Filtrar errores sensibles
      if (event.exception?.values?.[0]?.value?.includes('password')) {
        return null;
      }
      return event;
    }
  }
});

Captura de Errores

import { ErrorHandler } from '@foxframework/monitoring';
 
export class UserController extends FoxController {
  async createUser(req: Request, res: Response): Promise<void> {
    try {
      const user = await this.userService.create(req.body);
      res.status(201).json({ success: true, data: user });
    } catch (error) {
      // Capturar error con contexto
      ErrorHandler.captureException(error, {
        user: req.user,
        request: {
          url: req.url,
          method: req.method,
          body: this.sanitizeBody(req.body)
        },
        tags: {
          'operation': 'user.create',
          'controller': 'UserController'
        }
      });
 
      throw error;
    }
  }
 
  private sanitizeBody(body: any): any {
    const { password, ...sanitized } = body;
    return sanitized;
  }
}

Dashboards y Visualización

Configuración de Grafana

grafana/dashboards/fox-framework-app.json

{
  "dashboard": {
    "title": "Fox Framework App Monitoring",
    "panels": [
      {
        "title": "HTTP Request Rate",
        "type": "graph",
        "targets": [
          {
            "expr": "rate(fox_app_http_requests_total[5m])",
            "legendFormat": "{{method}} {{status}}"
          }
        ]
      },
      {
        "title": "Response Time",
        "type": "graph",
        "targets": [
          {
            "expr": "histogram_quantile(0.95, rate(fox_app_http_request_duration_seconds_bucket[5m]))",
            "legendFormat": "95th percentile"
          }
        ]
      },
      {
        "title": "Error Rate",
        "type": "singlestat",
        "targets": [
          {
            "expr": "rate(fox_app_http_requests_total{status=~\"5..\"}[5m])",
            "legendFormat": "Error Rate"
          }
        ]
      }
    ]
  }
}

Dashboard Personalizado

import { DashboardBuilder } from '@foxframework/monitoring';
 
export class BusinessDashboard {
  static build(): Dashboard {
    return new DashboardBuilder('Fox Framework Business Metrics')
      .addPanel('User Registrations', {
        type: 'graph',
        query: 'rate(fox_app_users_registered_total[1h])',
        unit: 'per hour'
      })
      .addPanel('Active Users', {
        type: 'singlestat',
        query: 'fox_app_users_active_current',
        unit: 'users'
      })
      .addPanel('Order Processing Rate', {
        type: 'graph',
        query: 'rate(fox_app_orders_processed_total[5m])',
        legend: '{{status}}'
      })
      .addPanel('Revenue', {
        type: 'graph',
        query: 'increase(fox_app_revenue_total[1d])',
        unit: 'currency'
      })
      .build();
  }
}

Alertas y Notificaciones

Configuración de Alertas

import { AlertManager } from '@foxframework/monitoring';
 
const alertManager = new AlertManager({
  rules: [
    {
      name: 'High Error Rate',
      query: 'rate(fox_app_http_requests_total{status=~"5.."}[5m]) > 0.1',
      duration: '2m',
      severity: 'critical',
      annotations: {
        summary: 'High error rate detected',
        description: 'Error rate is above 10% for 2 minutes'
      }
    },
    {
      name: 'High Response Time',
      query: 'histogram_quantile(0.95, rate(fox_app_http_request_duration_seconds_bucket[5m])) > 1.0',
      duration: '5m',
      severity: 'warning',
      annotations: {
        summary: 'High response time detected',
        description: '95th percentile response time is above 1 second'
      }
    },
    {
      name: 'Database Connection Issues',
      query: 'fox_app_db_connections_active / fox_app_db_connections_total > 0.8',
      duration: '1m',
      severity: 'warning',
      annotations: {
        summary: 'Database connection pool usage high',
        description: 'Database connection pool is 80% full'
      }
    }
  ],
  
  notifications: [
    {
      name: 'slack',
      webhook: process.env.SLACK_WEBHOOK_URL,
      channel: '#alerts'
    },
    {
      name: 'email',
      smtp: {
        host: process.env.SMTP_HOST,
        auth: {
          user: process.env.SMTP_USER,
          pass: process.env.SMTP_PASS
        }
      },
      recipients: ['devops@company.com']
    }
  ]
});

Alertas Personalizadas

export class BusinessAlerts {
  private alertManager: AlertManager;
 
  constructor(alertManager: AlertManager) {
    this.alertManager = alertManager;
  }
 
  setupBusinessAlerts() {
    // Alerta por caída en registros
    this.alertManager.addRule({
      name: 'Low User Registration Rate',
      query: 'rate(fox_app_users_registered_total[1h]) < 5',
      duration: '30m',
      severity: 'warning',
      annotations: {
        summary: 'User registration rate is below normal',
        description: 'Less than 5 users registered in the last hour'
      }
    });
 
    // Alerta por fallas en órdenes
    this.alertManager.addRule({
      name: 'High Order Failure Rate',
      query: 'rate(fox_app_orders_processed_total{status="failed"}[5m]) / rate(fox_app_orders_processed_total[5m]) > 0.05',
      duration: '2m',
      severity: 'critical',
      annotations: {
        summary: 'High order failure rate',
        description: 'More than 5% of orders are failing'
      }
    });
  }
}

Performance Profiling

CPU Profiling

import { Profiler } from '@foxframework/monitoring';
 
export class PerformanceMonitor {
  static async profileCPU(duration: number = 30000): Promise<void> {
    const profiler = new Profiler();
    
    console.log('Starting CPU profiling...');
    profiler.startCPUProfiling();
    
    setTimeout(() => {
      const profile = profiler.stopCPUProfiling();
      
      // Guardar perfil
      fs.writeFileSync(`cpu-profile-${Date.now()}.cpuprofile`, JSON.stringify(profile));
      
      // Analizar hot spots
      const hotSpots = this.analyzeCPUProfile(profile);
      console.log('CPU Hot Spots:', hotSpots);
    }, duration);
  }
 
  static async profileMemory(): Promise<void> {
    const heapSnapshot = process.memoryUsage();
    
    console.log('Memory Usage:', {
      rss: `${Math.round(heapSnapshot.rss / 1024 / 1024)} MB`,
      heapTotal: `${Math.round(heapSnapshot.heapTotal / 1024 / 1024)} MB`,
      heapUsed: `${Math.round(heapSnapshot.heapUsed / 1024 / 1024)} MB`,
      external: `${Math.round(heapSnapshot.external / 1024 / 1024)} MB`
    });
  }
}

Integración con CI/CD

GitHub Actions para Monitoreo

name: Deploy with Monitoring
 
on:
  push:
    branches: [main]
 
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    
    - name: Deploy application
      run: |
        # Deploy logic here
        echo "Deploying application..."
    
    - name: Setup monitoring
      run: |
        # Configure Prometheus
        kubectl apply -f k8s/monitoring/prometheus.yaml
        
        # Configure Grafana dashboards
        kubectl apply -f k8s/monitoring/grafana-dashboard.yaml
        
        # Setup alerts
        kubectl apply -f k8s/monitoring/alerts.yaml
    
    - name: Verify deployment health
      run: |
        sleep 30 # Wait for deployment
        curl -f http://your-app.com/health || exit 1
    
    - name: Send deployment notification
      uses: 8398a7/action-slack@v3
      with:
        status: ${{ job.status }}
        webhook_url: ${{ secrets.SLACK_WEBHOOK }}

Mejores Prácticas

1. Métricas

  • Define SLIs (Service Level Indicators) claros
  • Usa labels consistentes en métricas
  • Evita alta cardinalidad en labels
  • Implementa métricas de negocio relevantes

2. Health Checks

  • Implementa checks rápidos y confiables
  • Diferencia entre liveness y readiness
  • Incluye dependencias críticas
  • Usa timeouts apropiados

3. Alertas

  • Crea alertas accionables
  • Evita alert fatigue
  • Usa diferentes niveles de severidad
  • Incluye runbooks en las alertas

4. Performance

  • Monitorea la sobrecarga del monitoreo
  • Usa sampling apropiado para traces
  • Optimiza queries de métricas
  • Implementa rate limiting en endpoints de métricas

El sistema de monitoreo de Fox Framework te proporciona visibilidad completa sobre tu aplicación, permitiéndote mantener alta disponibilidad y excelente experiencia de usuario.