La observabilidad y el monitoreo se han convertido en competencias esenciales para los ingenieros de QA a medida que los sistemas se desplazan hacia arquitecturas de microservicios distribuidos. Según un informe de Dynatrace 2023, el 85% de las organizaciones experimentó una interrupción de servicio digital en el último año, con un tiempo medio de detección (MTTD) superior a 70 minutos en organizaciones sin prácticas maduras de observabilidad. Según el DORA State of DevOps, los equipos de alto rendimiento logran un MTTR 100 veces más rápido — y la observabilidad es el diferenciador clave. Para los ingenieros de QA, esto significa pasar de la detección reactiva de bugs a la medición proactiva de calidad.

TL;DR: La observabilidad de QA cubre tres pilares: métricas (SLIs, SLOs, tasas de error), logs (logging estructurado, IDs de correlación) y trazas (tracing distribuido con OpenTelemetry). Usa Grafana + Prometheus para visualización de métricas, Jaeger para tracing y define SLOs de calidad.

Entendiendo Monitoring vs. Observabilidad

Aunque a menudo se usan indistintamente, monitoring y observabilidad sirven propósitos diferentes:

Monitoring

Definición: Recopilar, agregar y analizar métricas predefinidas para detectar problemas conocidos.

Características:

  • Responde preguntas conocidas: “¿El sistema está activo?” “¿El uso de CPU está por encima del 80%?”
  • Enfoque reactivo: Las alertas se disparan cuando se exceden umbrales
  • Se enfoca en salud y disponibilidad del sistema
  • Funciona con dashboards y alertas predefinidos

Ejemplo: Alertar cuando el tiempo de respuesta de API excede 500ms durante 5 minutos consecutivos.

Observabilidad

Definición: Entender el estado interno del sistema basándose en salidas externas (logs, métricas, traces) para responder preguntas arbitrarias.

Características:

  • Responde preguntas desconocidas: “¿Por qué falla el checkout para usuarios iOS en Europa?”
  • Enfoque proactivo: Habilita exploración y depuración
  • Se enfoca en entender el comportamiento del sistema
  • Funciona con consultas flexibles y correlación

Ejemplo: Investigar por qué falló la transacción de un usuario específico correlacionando logs, métricas y traces a través de múltiples servicios.

Los Tres Pilares de la Observabilidad

  1. Logs: Eventos discretos con timestamps describiendo qué sucedió
  2. Métricas: Mediciones numéricas a lo largo del tiempo mostrando rendimiento del sistema
  3. Traces: Viaje end-to-end de requests a través de sistemas distribuidos

“Monitoring tells you when something is wrong. Observability tells you why. QA engineers who master both can catch issues in production before customers report them — that’s a fundamentally different quality posture.” — Yuri Kan, Senior QA Lead

ELK Stack para Gestión de Logs

El ELK Stack (Elasticsearch, Logstash, Kibana) proporciona potentes capacidades de agregación, búsqueda y visualización de logs.

Arquitectura ELK Stack

Elasticsearch: Motor de búsqueda y análisis distribuido para almacenar y consultar logs Logstash: Pipeline de procesamiento de datos del lado del servidor para ingerir, transformar y enviar logs Kibana: Herramienta de visualización y exploración para datos de Elasticsearch Beats (a menudo añadido): Recolectores de datos ligeros para reenviar logs desde aplicaciones

Configurando ELK Stack

Setup Docker Compose (docker-compose.yml):

version: '3.8'

services:
  elasticsearch:
    image: docker (como se discute en [Service Mesh Testing: Istio and Linkerd Testing Guide](/es/blog/service-mesh-testing)).elastic.co/elasticsearch/elasticsearch:8.10.0
    container_name: elasticsearch
    environment:

      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - xpack.security.enabled=false
    ports:

      - "9200:9200"
      - "9300:9300"
    volumes:

      - elasticsearch-data:/usr/share/elasticsearch/data
    networks:

      - elk

  logstash:
    image: docker.elastic.co/logstash/logstash:8.10.0
    container_name: logstash
    volumes:

      - ./logstash/pipeline:/usr/share/logstash/pipeline
    ports:

      - "5044:5044"
    networks:

      - elk
    depends_on:

      - elasticsearch

  kibana:
    image: docker.elastic.co/kibana/kibana:8.10.0
    container_name: kibana
    ports:

      - "5601:5601"
    environment:

      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    networks:

      - elk
    depends_on:

      - elasticsearch

volumes:
  elasticsearch-data:

networks:
  elk:
    driver: bridge

Configuración pipeline de Logstash (logstash/pipeline/logstash.conf):

input {
  beats {
    port => 5044
  }

  tcp {
    port => 5000
    codec => json
  }
}

filter {
  # Parsear logs JSON
  if [message] =~ /^\{.*\}$/ {
    json {
      source => "message"
    }
  }

  # Extraer nivel de log
  grok {
    match => {
      "message" => "%{LOGLEVEL:log_level}"
    }
  }

  # Parsear timestamp
  date {
    match => [ "timestamp", "ISO8601", "yyyy-MM-dd HH:mm:ss,SSS" ]
    target => "@timestamp"
  }

  # Filtrar logs de healthcheck
  if [path] == "/health" or [url] == "/healthz" {
    drop { }
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "logs-%{[service][name]}-%{+YYYY.MM.dd}"
  }

  stdout {
    codec => rubydebug
  }
}

Usando Kibana para QA

Creando Index Patterns:

  1. Navegar a Management → Stack Management → Index Patterns
  2. Crear patrón: logs-*
  3. Seleccionar campo timestamp: @timestamp

Construyendo Dashboards Enfocados en QA:

Dashboard de Monitoring de Ejecución de Pruebas:

{
  "title": "Test Execution Monitoring",
  "panels": [
    {
      "title": "Test Pass Rate",
      "type": "metric",
      "query": "service.name:test-runner AND test.status:*"
    },
    {
      "title": "Failed Tests Over Time",
      "type": "line",
      "query": "test.status:failed"
    },
    {
      "title": "Test Duration Distribution",
      "type": "histogram",
      "field": "test.duration"
    }
  ]
}

Ejemplos útiles de Kibana Query Language (KQL):

# Encontrar todos los errores en servicio de checkout
service.name:"checkout" AND log_level:ERROR

# Encontrar respuestas API lentas (>1 segundo)
http.response.time_ms > 1000

# Encontrar intentos de autenticación fallidos
event.action:"login" AND event.outcome:"failure"

# Encontrar errores afectando usuario específico
user.id:"12345" AND log_level:ERROR

# Encontrar errores de conexión a base de datos
message:"connection refused" OR message:"timeout"

# Encontrar errores en los últimos 15 minutos
log_level:ERROR AND @timestamp >= now-15m

# Excluir logs de healthcheck y monitoring
NOT (url:"/health" OR url:"/metrics")

Correlación de Logs para QA

Correlacionar logs a través de servicios usando trace IDs:

Logging de aplicación con contexto de trace (ejemplo Node.js):

const winston = require('winston');
const { v4: uuidv4 } = require('uuid');

const logger = winston.createLogger({
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json()
  ),
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: 'app.log' })
  ]
});

// Middleware para añadir trace ID
function traceMiddleware(req, res, next) {
  req.traceId = req.headers['x-trace-id'] || uuidv4();
  res.setHeader('X-Trace-ID', req.traceId);

  req.logger = logger.child({
    traceId: req.traceId,
    service: 'api-gateway',
    environment: process.env.NODE_ENV
  });

  next();
}

// Usar en requests
app.use(traceMiddleware);

app.post('/checkout', async (req, res) => {
  req.logger.info('Checkout initiated', {
    userId: req.user.id,
    cartItems: req.body.items.length
  });

  try {
    const result = await processCheckout(req.body, req.traceId);
    req.logger.info('Checkout completed', { orderId: result.orderId });
    res.json(result);
  } catch (error) {
    req.logger.error('Checkout failed', {
      error: error.message,
      userId: req.user.id
    });
    res.status(500).json({ error: 'Checkout failed' });
  }
});

Prometheus y Grafana para Métricas

Prometheus recopila y almacena métricas como datos de series temporales, mientras Grafana proporciona visualización y alertas.

Arquitectura Prometheus

Componentes:

  • Prometheus Server: Recopila y almacena métricas
  • Exporters: Exponen métricas de aplicaciones e infraestructura
  • Pushgateway: Permite a trabajos de corta duración enviar métricas
  • Alertmanager: Maneja alertas y notificaciones

Configurando Prometheus

Adición Docker Compose:

  prometheus:
    image: prom/prometheus:v2.47.0
    container_name: prometheus
    volumes:

      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus-data:/prometheus
    command:

      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
    ports:

      - "9090:9090"
    networks:

      - monitoring

  grafana:
    image: grafana/grafana:10.1.0
    container_name: grafana
    volumes:

      - grafana-data:/var/lib/grafana
    environment:

      - GF_SECURITY_ADMIN_PASSWORD=admin
    ports:

      - "3000:3000"
    networks:

      - monitoring
    depends_on:

      - prometheus

Configuración Prometheus (prometheus/prometheus.yml):

global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:

  - job_name: 'prometheus'
    static_configs:

      - targets: ['localhost:9090']

  - job_name: 'api-gateway'
    static_configs:

      - targets: ['api-gateway:8080']
    metrics_path: '/metrics'

  - job_name: 'checkout-service'
    static_configs:

      - targets: ['checkout:8081']

Instrumentando Aplicaciones para Prometheus

Métricas de aplicación Node.js (Express + prom-client):

const express = require('express');
const promClient = require('prom-client');

const app = express();
const register = new promClient.Registry();

promClient.collectDefaultMetrics({ register });

const httpRequestDuration = new promClient.Histogram({
  name: 'http_request_duration_seconds',
  help: 'Duration of HTTP requests in seconds',
  labelNames: ['method', 'route', 'status_code'],
  buckets: [0.1, 0.5, 1, 2, 5]
});

const checkoutTotal = new promClient.Counter({
  name: 'checkout_total',
  help: 'Total number of checkout attempts',
  labelNames: ['status', 'payment_method']
});

register.registerMetric(httpRequestDuration);
register.registerMetric(checkoutTotal);

// Middleware para rastrear requests
app.use((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;

    httpRequestDuration
      .labels(req.method, route, res.statusCode)
      .observe(duration);
  });

  next();
});

// Endpoint de métricas
app.get('/metrics', async (req, res) => {
  res.set('Content-Type', register.contentType);
  res.end(await register.metrics());
});

app.listen(8080);

Dashboards Grafana para QA

Consultas PromQL útiles para QA:

# Tasa de requests por segundo
rate(http_requests_total[5m])

# Porcentaje de tasa de error
(rate(http_requests_total{status_code=~"5.."}[5m]) / rate(http_requests_total[5m])) * 100

# Tiempo de respuesta percentil 95
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))

# Checkouts fallidos en última hora
increase(checkout_total{status="failure"}[1h])

# Duración promedio de checkout
rate(checkout_duration_seconds_sum[5m]) / rate(checkout_duration_seconds_count[5m])

# Porcentaje de uso de memoria
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100

# Uso de CPU
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

Distributed Tracing

El distributed tracing rastrea requests a medida que fluyen a través de microservicios, proporcionando visibilidad end-to-end.

Setup de Jaeger

Adición Docker Compose:

  jaeger:
    image: jaegertracing/all-in-one:1.50
    container_name: jaeger
    environment:

      - COLLECTOR_OTLP_ENABLED=true
    ports:

      - "16686:16686"  # UI
      - "14250:14250"
      - "14268:14268"
    networks:

      - tracing

Instrumentando Node.js con OpenTelemetry:

const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');

const provider = new NodeTracerProvider();
const exporter = new JaegerExporter({
  endpoint: 'http://jaeger:14268/api/traces',
});

provider.addSpanProcessor(new opentelemetry.tracing.SimpleSpanProcessor(exporter));
provider.register();

const tracer = provider.getTracer('checkout-service');

async function processCheckout(order) {
  const span = tracer.startSpan('process_checkout');

  span.setAttributes({
    'order.id': order.id,
    'user.id': order.userId,
  });

  try {
    const result = await createOrder(order);
    span.setStatus({ code: opentelemetry.SpanStatusCode.OK });
    return result;
  } catch (error) {
    span.recordException(error);
    throw error;
  } finally {
    span.end();
  }
}

Synthetic Monitoring

El synthetic monitoring prueba proactivamente la disponibilidad y rendimiento del sistema desde la perspectiva del usuario.

Usando Prometheus Blackbox Exporter

Configuración (blackbox.yml):

modules:
  http_2xx:
    prober: http
    timeout: 5s
    http:
      method: GET
      valid_status_codes: [200]

  http_post_checkout:
    prober: http
    timeout: 10s
    http:
      method: POST
      headers:
        Content-Type: application/json
      body: '{"userId": "test", "items": [{"id": "123"}]}'
      valid_status_codes: [200, 201]

Alerting para QA

Reglas de alerta de Prometheus (alerts/qa-alerts.yml):

groups:

  - name: qa_alerts
    interval: 30s
    rules:

      - alert: HighErrorRate
        expr: |
          (rate(http_requests_total{status_code=~"5.."}[5m]) / rate(http_requests_total[5m])) * 100 > 5
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "High error rate detected"
          description: "Error rate is {{ $value }}%"

      - alert: SlowAPIResponse
        expr: |
          histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 2
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "API response time degraded"

      - alert: ServiceDown
        expr: up{job="api-gateway"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Service {{ $labels.job }} is down"

Conclusión

El monitoring y la observabilidad son componentes esenciales de las prácticas QA modernas. Aprovechando herramientas como ELK Stack para logs, Prometheus y Grafana para métricas, distributed tracing con Jaeger y synthetic monitoring, los equipos QA pueden cambiar del descubrimiento reactivo de bugs al aseguramiento proactivo de calidad.

Estas herramientas permiten a los profesionales QA entender el comportamiento del sistema en producción, identificar cuellos de botella de rendimiento, correlacionar problemas a través de servicios y detectar problemas antes de que impacten a los usuarios.

Puntos Clave:

  • La observabilidad extiende QA más allá del testing tradicional
  • Logs, métricas y traces proporcionan visibilidad comprensiva del sistema
  • ELK Stack habilita búsqueda y análisis potente de logs
  • Prometheus y Grafana rastrean métricas de rendimiento a lo largo del tiempo
  • Distributed tracing revela interacciones de servicios y cuellos de botella
  • Synthetic monitoring valida proactivamente disponibilidad del sistema
  • Alerting habilita respuesta rápida a problemas de calidad
  • Integración con CI/CD proporciona insights continuos de calidad

Ver También

Recursos Oficiales

FAQ

¿Cuál es la diferencia entre monitoreo y observabilidad?

El monitoreo rastrea métricas predefinidas y alerta sobre condiciones de fallo conocidas. La observabilidad es la propiedad de un sistema que permite entender su estado interno a partir de salidas externas (métricas, logs, trazas). La observabilidad permite depurar fallos desconocidos; el monitoreo maneja los conocidos.

¿Qué son SLIs y SLOs y cómo los usan los ingenieros de QA?

SLI (Service Level Indicator) es una métrica medible del comportamiento del servicio: porcentaje de disponibilidad, percentil de latencia, tasa de error. SLO (Service Level Objective) es el valor objetivo para un SLI. Los ingenieros de QA usan SLOs para definir criterios de aceptación de pruebas y umbrales de alertas.

¿Cómo ayuda OpenTelemetry a los ingenieros de QA?

OpenTelemetry proporciona instrumentación independiente del proveedor para tracing distribuido. Los ingenieros de QA usan trazas para entender qué servicios fueron llamados durante un fallo de prueba, medir latencia entre servicios y verificar que los escenarios de prueba ejerciten las rutas de código esperadas.

¿Qué métricas deben monitorear los ingenieros de QA?

Métricas clave de calidad: tasa de error (objetivo < 0,1%), latencia p50/p95/p99, disponibilidad, throughput, tiempo de consulta de base de datos, tasa de aciertos de caché y métricas orientadas al usuario (tiempo de carga de página, Core Web Vitals).

See Also