Baggage¶
When to Use¶
Baggage carries small key/value pairs alongside trace context between services or processes. For example, a service might classify work as interactive or batch and make that classification available to downstream instrumentation.
Use baggage when an approved value is needed downstream without passing a telemetry-only argument through every function. Use a span or log attribute for a local fact, and a resource attribute for service identity. Trace context already carries trace and span identity; baggage does not need copies of those IDs.
Baggage is not exported telemetry and is not authenticated application data. Prefer non-sensitive classifications with a limited set of values. Avoid secrets, emails, payment data, raw user input, and unrestricted user or account IDs. Permission to record an identifier locally does not imply permission to transmit it elsewhere.
Python Context Pattern¶
The active context holds the current span and baggage. set_baggage returns a new context containing the added value; it does not change the active context. context.attach makes the new context active and returns a token used to restore the previous context.
This example makes a traffic class available within a scope, explicitly adds it to a span, and serializes context into a dictionary of outgoing headers. It prepares headers but does not send a request:
from opentelemetry import baggage, context, propagate, trace
tracer = trace.get_tracer("checkout.telemetry")
request_context = baggage.set_baggage("traffic.class", "interactive")
context_token = context.attach(request_context)
try:
with tracer.start_as_current_span("checkout.prepare") as span:
traffic_class = baggage.get_baggage("traffic.class")
if traffic_class in ("interactive", "batch"):
span.set_attribute("traffic.class", traffic_class)
outgoing_headers: dict[str, str] = {}
propagate.inject(outgoing_headers)
finally:
context.detach(context_token)
The finally block restores the previous context even if the operation raises. Without it, later work may see baggage that belongs to this operation.
Transmission and Recording¶
Propagation writes context into a carrier, such as request headers or message metadata. With the default tracecontext,baggage propagators, propagate.inject writes both trace context and baggage. Send those headers through the real transport while the operation is active. Instrumented clients normally handle injection themselves.
The receiver extracts context from the received carrier and makes it active for the receiving operation. Server instrumentation normally owns this scope; custom transports need explicit extraction and context handling.
Baggage values do not automatically become span, log, or metric attributes. The example explicitly copies an allowed traffic class to the span. Copy only the values a signal needs. A metric attribute also needs a limited value set and an acceptable time-series cost, even if the baggage value is otherwise approved.
Trust and Outgoing Requests¶
Incoming baggage has no built-in integrity check. Treat it as untrusted: validate keys and values, limit the number of entries, and cap their encoded size to fit the service's header budget. Never use baggage for authorization, billing, identity verification, tenant isolation, or privileged routing.
Client instrumentation may send baggage to third-party endpoints automatically. Define which values may leave the service and enforce that policy where requests are sent. Depending on the integration, this may mean selecting allowed keys, activating a context without baggage, or propagating only trace context.
Inspect the transmitted headers. Filtering telemetry attributes does not filter baggage, and removing baggage from active context does not erase headers already prepared for sending.
Background Work¶
Async tasks may inherit the context active when they are created. Decide whether background work should retain the originating request's context, especially when it outlives that request. Serialize approved context explicitly for processes and queues; it cannot cross those boundaries through process memory alone.
What to Verify¶
Inject into a test carrier and extract it in a separate scope. Verify that allowed values survive and absent values remain absent. Confirm that baggage appears in telemetry only where explicitly copied.
Test concurrent requests, exceptions, background work, and a request to a third-party endpoint. Inspect trace-context headers and baggage headers separately: disabling one does not necessarily disable the other.