La primera vez que realmente necesitas observabilidad no es cuando estás mirando tranquilamente un tablero. Es cuando un usuario escribe "el pago es lento", el gráfico de error parece normal y en los registros solo encuentra una fila de mensajes desconectados.
OpenTelemetry fue creado para evitar ese momento: no para tener más gráficos, sino para conectar las piezas. Una solicitud ingresa al API, llama a una base de datos, pasa por un proveedor externo, publica un trabajo en cola y tal vez falla tres servicios después. Sin rastreo distribuido, esa historia se reconstruye a mano. Con OpenTelemetry al menos tienes un mapa.
El punto no es el trace, es la historia
Un trace es una secuencia de span. Dicho así suena frío. En la práctica, cada span es una parte de la historia: POST /checkout, SELECT inventory, call payment provider, publish order.created.
El valor surge cuando empiezas a responder preguntas reales:
- ¿Qué servicio externo se está desacelerando?
- ¿Los errores provienen de una versión específica?
- ¿El problema afecta a todos o sólo a un inquilino?
- ¿Un reintento oculta un tiempo de espera?
- ¿El trabajo asincrónico comienza pero luego muere en otro lugar?
Estas cuestiones no se pueden resolver con un console.log lanzado a toda prisa. De hecho, a menudo el registro añadido en caso de emergencia le ayuda hoy y se convierte en ruido mañana.
¿Cómo pondría esto en una aplicación? Node.js
La configuración más saludable es simple: la aplicación produce telemetría, el Collector decide dónde enviarla.
Node.js app -> OpenTelemetry Collector -> backend di observability
¿Por qué no exportar directamente al proveedor? Porque al principio parece más rápido, luego te das cuenta de que cada servicio tiene diferentes configuraciones, diferentes reintentos, diferentes filtros y ningún punto central para eliminar datos sensibles o cambiar de destino.
El Collector es aburrido en todos los sentidos. Recibe OTLP, realiza procesamiento por lotes, puede filtrar, realizar muestreos, agregar atributos comunes y exportar a múltiples sistemas.
Autoinstrumentación: buena, pero no suficiente
En Node.js comenzaría con la autoinstrumentación. Le brinda visibilidad inmediata de HTTP, marcos compatibles, bases de datos y bibliotecas comunes.
npm install @opentelemetry/sdk-node \ @opentelemetry/auto-instrumentations-node \ @opentelemetry/exporter-trace-otlp-http
Luego inicializas el SDK antes que el resto de la aplicación:
import { NodeSDK } from '@opentelemetry/sdk-node'; import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; const sdk = new NodeSDK({ traceExporter: new OTLPTraceExporter({ url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, }), instrumentations: [getNodeAutoInstrumentations()], }); sdk.start();
Sin embargo, esto ve el marco, no su producto. Sabe que realizó una consulta, pero no sabe que la consulta estaba en "crear pedido" o "renovar suscripción". Para eso necesitas span manuales en los puntos donde el dominio cuenta.
const span = tracer.startSpan('checkout.create_order'); try { span.setAttribute('cart.items_count', input.items.length); const order = await createOrder(input); span.setAttribute('order.id', order.id); return order; } catch (error) { span.recordException(error as Error); throw error; } finally { span.end(); }
No pondría span manuales en ningún lado. Los pondría donde, a las tres de la mañana, me gustaría entender qué pasó sin leer la mitad del código base.
Tres reglas que evitan mucho caos
Primera regla: cada servicio debe tener service.name, entorno y versión. Parece trivial, pero sin estos atributos un trace es mucho menos útil. Cuando una implementación rompe algo, desea filtrar por versión en dos segundos.
Segunda regla: no pongas datos sensibles en los atributos. Los correos electrónicos, tokens, cargas útiles de números enteros y direcciones no deberían terminar en un backend de observabilidad por accidente. Si necesita identificar a un usuario, considere las identificaciones internas, el hash o campos menos confidenciales.
Tercera regla: presta atención a la cardinalidad. user.id como atributo de trace puede tener sentido. Como etiqueta métrica, puede destruir sus costos y rendimiento.
Métricas: pocas, pero buenas
Yo empezaría con métricas muy prácticas:
- tarifas, errores y duración de las solicitudes;
- latencia de dependencias externas;
- número de tiempos de espera y reintentos;
- profundidad de las colas;
- duración del trabajo;
- porcentaje de errores por versión.
El resto se agrega cuando es necesario. Los paneles llenos de gráficos que nadie mira son muebles, no observabilidad.
Registros: sigue siendo útil, pero vinculado
Los registros no desaparecen. Simplemente se vuelven mucho más útiles cuando llevan trace_id y span_id. Por lo tanto, puede comenzar desde un registro de errores y abrir el trace, o comenzar desde un trace lento y leer solo los registros producidos en esa ruta.
Sin correlación, buscas agujas. Con correlación, al menos sabes en qué cajón mirar.
La lista de verificación que usaría antes de decir "estamos cubiertos"
- El trace en realidad cruza múltiples servicios.
- Los registros incluyen
trace_idyspan_id. - El Collector está configurado con límites de memoria y procesamiento por lotes.
- Los errores se registran en span.
- Existe una política de muestreo.
- Las métricas tienen cardinalidad controlada.
- Se filtran datos sensibles.
- Las alertas comienzan a partir de síntomas del usuario, no de gráficos aleatorios.
Conclusión
OpenTelemetry no resuelve los problemas de producción por sí solo. Pero la forma de abordarlos cambia. En lugar de agregar registros a ciegas, comienza a seguir la ruta real de una solicitud.
Para mí la señal de que está funcionando es simple: cuando pasa algo, el equipo deja de preguntar "¿hacia dónde miramos?". y empieza a preguntar "¿por qué esa pieza es lenta?". Ahí es donde la observabilidad se convierte en una herramienta, no en una colección de paneles.