propagate_attributes()

Overview

propagate_attributes() is a context manager that attaches attributes to all spans within its scope — including spans created by auto-instrumentation and instrumentation plugins. Uses contextvars for async safety.

1from respan import propagate_attributes

This is the recommended way to attach per-request context (user ID, thread ID, metadata) to traces.

Parameters

All parameters are keyword-only:

ParameterTypeDescription
customer_identifierstrUser/customer identifier. Maps to respan.customer_params.customer_identifier.
customer_emailstrCustomer email. Maps to respan.customer_params.customer_email.
customer_namestrCustomer display name. Maps to respan.customer_params.customer_name.
thread_identifierstrConversation thread ID. Maps to respan.threads.thread_identifier.
custom_identifierstrIndexed custom identifier. Maps to respan.custom_identifier.
group_identifierstrGroup related traces. Maps to respan.trace_group.group_identifier.
environmentstrEnvironment name. Maps to respan.environment.
metadatadictCustom key-value pairs. Each key maps to respan.metadata.<key>. Nested calls merge metadata.
promptdictPrompt config with prompt_id and variables. Stored as JSON for server-side template resolution.

Unsupported keys are logged as warnings and ignored.

Examples

Per-request user and thread tracking

1from respan import Respan, propagate_attributes
2from respan_instrumentation_openai_agents import OpenAIAgentsInstrumentor
3from agents import Agent, Runner
4
5respan = Respan(
6 api_key="your-api-key",
7 instrumentations=[OpenAIAgentsInstrumentor()],
8)
9
10agent = Agent(name="assistant", instructions="You are helpful.")
11
12async def handle_request(user_id: str, thread_id: str, message: str):
13 with propagate_attributes(
14 customer_identifier=user_id,
15 thread_identifier=thread_id,
16 metadata={"source": "web", "plan": "pro"},
17 ):
18 result = await Runner.run(agent, message)
19 return result.final_output

All spans created during Runner.run() — the trace, agent, LLM response, tool calls — will carry the customer_identifier, thread_identifier, and metadata.

Prompt logging

Use the prompt parameter for server-side prompt template resolution:

1with propagate_attributes(
2 prompt={
3 "prompt_id": "abc123",
4 "variables": {"topic": "Python", "style": "concise"},
5 },
6):
7 result = await Runner.run(agent, "Tell me about Python")

The Respan backend resolves the template and displays the rendered prompt alongside the raw messages.

With direct OpenAI SDK

1from respan import Respan, propagate_attributes
2from respan_instrumentation_openai import OpenAIInstrumentor
3from openai import OpenAI
4
5respan = Respan(
6 api_key="your-api-key",
7 instrumentations=[OpenAIInstrumentor()],
8)
9client = OpenAI()
10
11def handle_request(user_id: str, message: str):
12 with propagate_attributes(
13 customer_identifier=user_id,
14 metadata={"source": "api"},
15 ):
16 response = client.chat.completions.create(
17 model="gpt-4o-mini",
18 messages=[{"role": "user", "content": message}],
19 )
20 return response.choices[0].message.content

Nested contexts

Nested calls merge attributes. Inner values override outer values for the same key. Metadata dicts are merged (not replaced).

1with propagate_attributes(
2 customer_identifier="user-A",
3 metadata={"plan": "free", "region": "us"},
4):
5 # Spans here: customer_identifier="user-A", plan="free", region="us"
6
7 with propagate_attributes(
8 customer_identifier="user-B",
9 metadata={"plan": "pro"},
10 ):
11 # Spans here: customer_identifier="user-B", plan="pro", region="us"
12 # customer_identifier overridden, plan overridden, region inherited
13 pass

How it works

  1. Attributes are stored in a ContextVar (_PROPAGATED_ATTRIBUTES).
  2. RespanSpanProcessor.on_start() reads the ContextVar and merges attributes onto every new span.
  3. build_readable_span() also reads the ContextVar and merges into plugin-created spans.
  4. On context exit, the ContextVar is reset to the previous value.

This works correctly with asyncio — each task gets its own copy of the context.

vs respan_span_attributes()

Featurepropagate_attributes()respan_span_attributes()
ScopeAll spans in context (including auto-instrumented)Current active span only
Async safeYes (ContextVar-based)Yes
Nested mergingYes (metadata merges)Yes (inner overrides outer)
Works with pluginsYesOnly for decorator-created spans
Prompt loggingYesNo

Use propagate_attributes() in most cases. Use respan_span_attributes() only when you need to set attributes on a specific decorator-created span.