Cap 2: Conceptos Core

Por: Artiko
opentelemetryresourcescontextbaggagesemantic-conventions

Resource

Un Resource describe la entidad que genera la telemetría. Es un conjunto de atributos que identifican el proceso, el host y el entorno.

# Resource attributes típicos
service.name: "order-service"
service.version: "2.1.0"
service.instance.id: "pod-abc123"
host.name: "worker-node-7"
os.type: "linux"
deployment.environment: "production"
k8s.namespace.name: "payments"
k8s.pod.name: "order-service-7d8f9c"

El Resource se configura una vez al inicializar el SDK y se adjunta automáticamente a todos los traces, métricas y logs del proceso.

from opentelemetry.sdk.resources import Resource

resource = Resource.create({
    "service.name": "order-service",
    "service.version": "2.1.0",
    "deployment.environment": "production",
})

Context y Context Propagation

El Context es el mecanismo que permite pasar información de observabilidad entre funciones y entre servicios — sin modificar las firmas de las funciones.

Context en proceso

Dentro de un proceso, el contexto viaja de forma implícita a través de variables de contexto (thread-local, async context, etc.):

# OTel guarda el span activo en el contexto implícito
with tracer.start_as_current_span("my-span") as span:
    # cualquier código aquí puede obtener el span activo
    resultado = procesar_pedido()  # esta función puede crear sub-spans
    # sin pasar nada explícitamente

Context propagation entre servicios

Cuando una request cruza un boundary de red (HTTP, gRPC, mensajes), el contexto se inyecta en los headers y se extrae en el receptor:

sequenceDiagram
    participant A as Servicio A
    participant B as Servicio B

    A->>A: Crear span "checkout"
    Note over A: trace_id=abc, span_id=111
    A->>B: POST /payment\ntraceparent: 00-abc...-111-01
    B->>B: Extraer contexto del header
    B->>B: Crear span "process-payment"\n(hijo de span 111)
    B-->>A: 200 OK

El header traceparent es parte del estándar W3C Trace Context — el formato que OTel usa por defecto.

Formato: 00-{trace_id}-{parent_span_id}-{flags} Ejemplo: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01

Propagators

OTel soporta múltiples formatos de propagación:

PropagatorHeaderCuándo usar
W3C TraceContexttraceparentDefault, sistemas modernos
W3C BaggagebaggageDatos de contexto cross-service
B3 (Zipkin)X-B3-TraceId etc.Sistemas legacy con Zipkin
Jaegeruber-trace-idSistemas legacy con Jaeger

Baggage

El Baggage permite propagar datos arbitrarios de usuario a través de toda la cadena de servicios. A diferencia del Context de trazas (que es para observabilidad), el Baggage es para datos de negocio.

from opentelemetry import baggage

# Servicio A: agrega datos al baggage
ctx = baggage.set_baggage("user.tier", "premium")
ctx = baggage.set_baggage("request.region", "eu-west-1", context=ctx)

# Servicio B, C, D... reciben automáticamente estos valores
tier = baggage.get_baggage("user.tier")  # "premium"

Advertencia: El Baggage viaja en headers HTTP — no pongas datos sensibles ni grandes.

Semantic Conventions

Las Semantic Conventions son el diccionario de nombres de atributos y valores que OTel estandariza. Evitan que cada equipo invente sus propios nombres:

# Sin convenciones semánticas:
http.status        → 200
response.code      → 200
statusCode         → 200
http_status_code   → 200

# Con convenciones semánticas (todos usan):
http.response.status_code → 200

Atributos HTTP comunes

http.request.method      → "GET", "POST"
http.response.status_code → 200, 404, 500
url.full                 → "https://api.example.com/users"
url.path                 → "/users"
server.address           → "api.example.com"
server.port              → 443
network.protocol.version → "1.1", "2"

Atributos de base de datos

db.system          → "postgresql", "mysql", "redis"
db.name            → "orders"
db.operation.name  → "SELECT", "INSERT"
db.query.text      → "SELECT * FROM orders WHERE id = ?"
server.address     → "db.internal"
server.port        → 5432

Atributos de mensajería

messaging.system          → "kafka", "rabbitmq", "sqs"
messaging.destination.name → "orders.created"
messaging.operation.name  → "publish", "receive"
messaging.message.id      → "msg-abc123"

Instrumentation Scope

Cada señal lleva información de quién la instrumentó:

tracer = trace.get_tracer(
    "com.mycompany.myservice",  # nombre del scope
    "1.0.0",                    # versión del scope
)

Esto permite filtrar en el backend: “muéstrame solo spans generados por el SDK de HTTP, no los de mi código de negocio.”

La jerarquía de componentes

graph TD
    TP[TracerProvider\nMeterProvider\nLoggerProvider] -->|crea| T[Tracer\nMeter\nLogger]
    T -->|crea| S[Span\nInstrument\nLogRecord]
    S -->|adjunta| R[Resource]
    S -->|propaga| C[Context]
    TP -->|usa| EXP[Exporter]
    TP -->|usa| SAM[Sampler]
    TP -->|usa| PROC[Processor]

Provider — Fábrica de instrumentos. Se configura al inicio de la aplicación.

Tracer / Meter / Logger — Instrumentos con nombre. Cada librería o módulo tiene el suyo.

Span / Instrument / LogRecord — Las señales individuales que se generan y exportan.

Sampling

No todos los traces necesitan exportarse — en sistemas de alto tráfico, exportar el 100% sería prohibitivo. El Sampler decide qué traces capturar:

SamplerComportamiento
AlwaysOnCaptura el 100% de traces
AlwaysOffNo captura nada
TraceIdRatioBased(0.1)Captura el 10% aleatoriamente
ParentBasedRespeta la decisión del padre (propagada en header)
from opentelemetry.sdk.trace.sampling import TraceIdRatioBased, ParentBased

# Capturar 10% pero siempre respetar la decisión del upstream
sampler = ParentBased(root=TraceIdRatioBased(0.1))

El sampling ocurre en la cabeza del trace (head-based sampling) o en el Collector (tail-based sampling, más poderoso pero más complejo).