2. Conceptos fundamentales: trace, span, context

Por: Artiko
jaegertracingspantraceopentelemetry

2. Conceptos fundamentales: trace, span, context

Sin entender estos conceptos no se puede operar Jaeger. Son el ABC del distributed tracing y son comunes a Jaeger, Zipkin, Tempo y cualquier backend OpenTelemetry-compatible.


Trace

Un trace es la representación completa de una request o transacción a través de un sistema distribuido.

Cada trace tiene un identificador único (trace_id) de 128 bits y representa una unidad lógica de trabajo: una request HTTP, un mensaje de Kafka procesado, un cron job que corrió, etc.

Características:

flowchart TD
    T[Trace: checkout_user_123\ntrace_id=4bf92f3577b34da6a3ce929d0e0e4736]
    T --> S1[Span: HTTP POST /checkout\n800ms]
    S1 --> S2[Span: validate cart\n50ms]
    S1 --> S3[Span: charge payment\n600ms]
    S3 --> S4[Span: stripe.charge\n550ms]
    S1 --> S5[Span: send email\n100ms]

Span

Un span representa una operación individual con un inicio y un fin: una llamada HTTP, una query SQL, una función crítica.

Cada span contiene:

CampoQué guarda
span_idID único del span (64 bits)
trace_idA qué trace pertenece
parent_span_idQuién es su padre (vacío si es root)
nameNombre legible: GET /api/orders, db.query
start_time / end_timeMarca temporal con precisión de nanosegundos
attributesPares clave-valor (ex-tags)
eventsEventos puntuales con timestamp (ex-logs)
statusOK, ERROR, UNSET
kindSERVER, CLIENT, PRODUCER, CONSUMER, INTERNAL
linksReferencias a otros spans (no padre/hijo)

Anatomía visual de un span

┌───────────────────────────────────────────────┐
│ span_id: 7c3d92ab...                          │
│ name: HTTP GET /api/users/42                  │
│ kind: SERVER                                  │
│ start: 2026-05-10T15:00:01.123Z               │
│ duration: 87ms                                │
│ status: OK                                    │
│ attributes:                                   │
│   http.method: GET                            │
│   http.status_code: 200                       │
│   http.route: /api/users/:id                  │
│   user.id: 42                                 │
│ events:                                       │
│   2026-05-10T15:00:01.180Z "cache_miss"       │
│   2026-05-10T15:00:01.205Z "db_query_start"   │
└───────────────────────────────────────────────┘

Span kind — la pieza que más se olvida

El kind indica el rol del span en la comunicación:

KindCuándo usarlo
SERVERRecibiste una request entrante (handler HTTP, gRPC server)
CLIENTHiciste una request saliente (fetch, llamada a API externa)
PRODUCERPublicaste un mensaje a una cola (Kafka, RabbitMQ)
CONSUMERConsumiste un mensaje de una cola
INTERNALOperación interna (función crítica, transformación) — default

Un par CLIENT + SERVER típicamente forman el cruce entre dos servicios. Si los marcás bien, Jaeger puede dibujar el mapa de dependencias automáticamente.


Trace context

El trace context es la información mínima que se propaga entre procesos para mantener un trace coherente: trace_id, span_id y flags.

Cuando el servicio A llama por HTTP al servicio B, el cliente del servicio A inyecta el contexto en headers, y el servidor de B los lee. Sin esto, B empezaría un trace nuevo sin saber que es continuación del de A.

El estándar dominante es W3C Trace Context (RFC publicado por W3C):

traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
             │  │                                │                │
             │  trace_id (128 bits)             span_id (64 b)   flags
             versión

Y opcionalmente tracestate para metadata vendor-específica.

sequenceDiagram
    participant A as Service A
    participant B as Service B
    participant C as Service C
    A->>B: GET /orders<br/>traceparent: 00-abc...-111-01
    B->>B: span "B handler" (parent=111)
    B->>C: GET /pricing<br/>traceparent: 00-abc...-222-01
    C->>C: span "C handler" (parent=222)
    C-->>B: 200
    B-->>A: 200

Notá que trace_id (abc...) no cambia en toda la cadena. Lo que cambia en cada salto es span_id (el ID del span actual, que será el padre del siguiente).

Capítulo dedicado a esto: 07-context-propagation.


Baggage

El baggage es información extra que viaja junto al contexto y se puede leer en cualquier servicio del trace.

Es una especie de “diccionario global” para esa request. Útil para cosas como:

Un servicio downstream puede leer user.tier sin tener que hacer otra request al servicio de usuarios.

Cuidado: el baggage viaja en headers HTTP en cada salto. Si lo llenás de basura, agregás overhead a cada request. Mantenelo pequeño y semánticamente útil.


Atributos vs eventos vs logs

Tres mecanismos parecidos pero distintos.

Atributos (attributes)

Pares clave-valor que describen el span de forma estática:

http.method = GET
http.status_code = 200
db.system = postgresql
db.statement = SELECT * FROM users WHERE id=$1

Estandarizados por las OpenTelemetry Semantic Conventions. Usá los nombres oficiales — la UI de Jaeger los reconoce y los muestra de manera especial.

Eventos (events)

Cosas puntuales que pasaron dentro del span con timestamp:

t=10ms: "cache_miss"
t=12ms: "db_query_start"
t=78ms: "db_query_done"

Útil para hitos internos sin abrir un span hijo (overkill para algo de pocos microsegundos).

Logs

En OpenTelemetry “log” es una señal aparte (logs, métricas, trazas son las 3 señales). En Jaeger v1 “log” era sinónimo de “evento de span”. Hoy se prefiere el término event.


Status del span

status:
  code: OK | ERROR | UNSET
  description: "connection refused"  (solo si ERROR)

Importante: una excepción en tu código no marca automáticamente el span como ERROR. La instrumentación tiene que llamar span.setStatus(ERROR) explícitamente, o usar las helpers recordException(). Las auto-instrumentaciones de OpenTelemetry suelen hacerlo bien para HTTP/gRPC, pero código manual es responsabilidad tuya.


Un span puede linkear a otros spans que no son su padre. Útiles para:

flowchart LR
    P1[Producer span 1] -.link.-> B[Batch processor span]
    P2[Producer span 2] -.link.-> B
    P3[Producer span 3] -.link.-> B

Resource

Las propiedades del proceso/servicio que emite los spans, comunes a todos sus spans:

service.name = order-service
service.version = 1.4.2
deployment.environment = production
host.name = order-service-7b4f9c-xkj2p
process.runtime.name = node
process.runtime.version = 20.10.0

Se configuran una vez al inicio del proceso. La UI los muestra agrupados como “Process” en el detalle del span.


Diagrama mental completo

flowchart TB
    subgraph Trace[Trace - trace_id]
        S1[Span root\nkind=SERVER]
        S2[Span hijo\nkind=CLIENT]
        S3[Span hijo\nkind=INTERNAL]
        S4[Span nieto\nkind=CLIENT]
        S1 --> S2
        S1 --> S3
        S2 --> S4
    end

    Resource[Resource\nservice.name, env, host] --> S1
    Resource --> S2
    Resource --> S3
    Resource --> S4

    Context[Context\ntrace_id + span_id] -.propagación HTTP/gRPC.-> Otra[Otro proceso]
    Baggage[Baggage\npairs k=v] -.acompaña al context.-> Otra

Con esto en la cabeza, ya estás listo para instalar Jaeger en el próximo capítulo y empezar a generar trazas reales.