9. Monitoreo y observabilidad

El monitoreo tradicional se limita a saber si una aplicación está encendida o apagada. La observabilidad, en cambio, busca explicar por qué el sistema se comporta de cierta manera. En una arquitectura de microservicios, donde cada petición atraviesa docenas de componentes, es imprescindible medir latencia, tasas de error, registros estructurados y trazas distribuidas que permitan reconstruir los flujos. Este capítulo describe los pilares de observabilidad y cómo implementarlos con herramientas modernas.

9.1 Métricas clave

Las métricas cuantifican el estado y rendimiento del sistema. Entre las más relevantes destacan la latencia (tiempo de respuesta), el throughput (cantidad de peticiones procesadas), los errores, el uso de recursos (CPU, memoria) y la capacidad disponible. Publicar métricas por servicio, instancia y versión permite identificar cuellos de botella y detectar regresiones tras un despliegue.

Se recomienda utilizar bibliotecas instrumentadas que expongan métricas en un formato estándar. En el ecosistema Java, Micrometer ofrece integraciones con Prometheus, Datadog o New Relic y permite definir contadores, histogramas y timers.

9.1.1 Instrumentación con Micrometer

El siguiente código registra un temporizador para medir la duración de un proceso y expone contadores de errores.

@Service
public class PaymentProcessor {

    private final Timer processingTime;
    private final Counter errorCounter;

    public PaymentProcessor(MeterRegistry registry) {
        this.processingTime = Timer.builder("payments.processing.time")
                .publishPercentileHistogram()
                .publishPercentiles(0.5, 0.95, 0.99)
                .tag("service", "payments")
                .register(registry);
        this.errorCounter = Counter.builder("payments.errors.total")
                .tag("service", "payments")
                .register(registry);
    }

    public void process(PaymentRequest request) {
        processingTime.record(() -> {
            try {
                executeWorkflow(request);
            } catch (Exception ex) {
                errorCounter.increment();
                throw ex;
            }
        });
    }
}

Las métricas se exponen en el endpoint /actuator/prometheus y Prometheus las recolecta periódicamente para analizar tendencias y generar alertas.

9.2 Centralización de logs

Los registros locales se pierden cuando una instancia falla o se recrea. La centralización de logs recopila eventos de todas las instancias y los almacena en una plataforma consultable. Tecnologías como Elastic Stack, Fluentd y Grafana Loki permiten indexar, consultar y correlacionar logs con métricas y trazas.

Los servicios deben registrar eventos en formato estructurado (JSON) con campos como timestamp, nivel, servicio, instancia, identificador de correlación y mensaje. Así se facilita construir dashboards y reglas de detección de incidentes.

9.2.1 Configuración de Logback estructurado

El siguiente fragmento de Logback genera registros JSON que luego son recolectados por un agente de logs.

<configuration>
  <appender name="JSON" class="net.logstash.logback.appender.LoggingEventCompositeJsonEncoder">
    <encoder>
      <providers>
        <timestamp></timestamp>
        <pattern>
          <pattern>{"level":"%level","logger":"%logger","message":"%message","traceId":"%X{traceId}"}</pattern>
        </pattern>
        <provider class="net.logstash.logback.composite.loggingevent.StackTraceJsonProvider"/>
      </providers>
    </encoder>
  </appender>
  <root level="INFO">
    <appender-ref ref="JSON"/>
  </root>
</configuration>

Con los logs centralizados se pueden correlacionar incidentes con despliegues recientes, detectar anomalías y cumplir requisitos normativos de auditoría.

9.3 Trazabilidad distribuida

Las trazas distribuidas permiten seguir una petición a través de todos los servicios. Cada llamada se identifica con un trace ID y los segmentos internos se agrupan como spans. Herramientas como OpenTelemetry, Jaeger y Zipkin simplifican la instrumentación y visualizan los flujos.

Los agentes y SDKs de OpenTelemetry capturan automáticamente solicitudes HTTP, llamadas a bases de datos y operaciones de mensajería. Las trazas ayudan a detectar servicios lentos, errores intermitentes y problemas de configuración de red.

9.3.1 Exportación de trazas con OpenTelemetry

El ejemplo muestra cómo configurar un tracer y exportar las trazas a Jaeger desde Java.

@Configuration
public class TracingConfig {

    @Bean
    public OpenTelemetry openTelemetry() {
        OtlpGrpcSpanExporter spanExporter = OtlpGrpcSpanExporter.builder()
                .setEndpoint("http://jaeger-collector.observability:4317")
                .build();

        SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
                .setResource(Resource.getDefault().toBuilder()
                        .put(AttributeKey.stringKey("service.name"), "orders-service")
                        .build())
                .addSpanProcessor(BatchSpanProcessor.builder(spanExporter).build())
                .build();

        return OpenTelemetrySdk.builder()
                .setTracerProvider(tracerProvider)
                .build();
    }
}

Una vez instrumentados los servicios, Jaeger o Zipkin permiten explorar las trazas, filtrar por latencia y analizar la jerarquía de llamadas.

9.4 Alertas y tableros de control

Las alertas permiten actuar antes de que un incidente impacte al usuario. Se definen reglas con umbrales o condiciones de rate of change que disparan notificaciones hacia canales como correo, Slack o PagerDuty. Prometheus es ampliamente usado para recolectar métricas y generar alertas a través de Alertmanager.

Los tableros de control ofrecen una vista ejecutiva y operativa del estado de la plataforma. Grafana permite combinar métricas, logs y trazas en paneles interactivos y compartirlos con equipos de negocio.

9.4.1 Regla de alerta por latencia y panel en Grafana

A continuación se define una alerta de Prometheus que verifica el percentil 95 de latencia y un panel de Grafana que muestra la evolución.

# Alertmanager rule
groups:
  - name: latency
    rules:
      - alert: HighLatencyP95
        expr: histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket{service="orders-service"}[5m])) by (le))
        for: 10m
        labels:
          severity: critical
        annotations:
          summary: "Latencia elevada en orders-service"
          description: "El percentil 95 supera 800ms durante 10 minutos."

# Grafana panel (JSON abreviado)
{
  "title": "Latencia P95 orders-service",
  "type": "timeseries",
  "targets": [
    {
      "expr": "histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket{service=\"orders-service\"}[5m])) by (le))",
      "legendFormat": "P95"
    }
  ],
  "fieldConfig": {
    "defaults": {
      "unit": "s",
      "thresholds": {
        "mode": "absolute",
        "steps": [
          { "color": "green", "value": null },
          { "color": "yellow", "value": 0.6 },
          { "color": "red", "value": 0.8 }
        ]
      }
    }
  }
}

Las alertas deben revisarse periódicamente para evitar fatiga y calibrarse con datos históricos. Los tableros se respaldan con anotaciones automáticas que indiquen despliegues, incidentes o cambios significativos en el tráfico.