or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

context.mdindex.mdlogging.mdmetrics.mdpropagation.mdtracing.md

propagation.mddocs/

0

# Cross-Service Propagation

1

2

Comprehensive context propagation system for distributed tracing and baggage across service boundaries. OpenTelemetry propagation enables automatic context extraction and injection using standard HTTP headers with support for W3C Trace Context and W3C Baggage specifications.

3

4

## Capabilities

5

6

### Global Propagation Operations

7

8

Extract and inject context using the globally configured propagator.

9

10

```python { .api }

11

def extract(

12

carrier: CarrierT,

13

context: Optional[Context] = None,

14

getter: Getter[CarrierT] = default_getter,

15

) -> Context:

16

"""

17

Uses the configured propagator to extract a Context from the carrier.

18

19

Parameters:

20

- carrier: Object containing values used to construct a Context

21

- context: Optional Context to use, defaults to root context if not set

22

- getter: Object that can retrieve values from the carrier

23

24

Returns:

25

Context extracted from the carrier

26

"""

27

28

def inject(

29

carrier: CarrierT,

30

context: Optional[Context] = None,

31

setter: Setter[CarrierT] = default_setter,

32

) -> None:

33

"""

34

Uses the configured propagator to inject a Context into the carrier.

35

36

Parameters:

37

- carrier: Medium used by propagators to write values

38

- context: Optional Context to use, defaults to current context if not set

39

- setter: Object that can set values on the carrier

40

"""

41

42

def get_global_textmap() -> TextMapPropagator:

43

"""

44

Returns the global text map propagator.

45

46

Returns:

47

The current global TextMapPropagator instance

48

"""

49

50

def set_global_textmap(http_text_format: TextMapPropagator) -> None:

51

"""

52

Sets the global text map propagator.

53

54

Parameters:

55

- http_text_format: The TextMapPropagator to set as global

56

"""

57

```

58

59

### Text Map Propagator Interface

60

61

Base interface for implementing propagators that work with text-based carriers.

62

63

```python { .api }

64

class TextMapPropagator(ABC):

65

"""Abstract base class for text map propagators."""

66

67

def extract(

68

self,

69

carrier: CarrierT,

70

context: Optional[Context] = None,

71

getter: Getter[CarrierT] = default_getter,

72

) -> Context:

73

"""

74

Extract context from a carrier.

75

76

Parameters:

77

- carrier: The carrier containing context data

78

- context: Optional context to merge extracted data into

79

- getter: Object to retrieve values from carrier

80

81

Returns:

82

Context with extracted data

83

"""

84

85

def inject(

86

self,

87

carrier: CarrierT,

88

context: Optional[Context] = None,

89

setter: Setter[CarrierT] = default_setter,

90

) -> None:

91

"""

92

Inject context into a carrier.

93

94

Parameters:

95

- carrier: The carrier to inject context into

96

- context: Optional context to inject, defaults to current context

97

- setter: Object to set values in carrier

98

"""

99

100

@property

101

def fields(self) -> Set[str]:

102

"""

103

Returns the fields this propagator will read or write.

104

105

Returns:

106

Set of field names used by this propagator

107

"""

108

```

109

110

### Carrier Access Patterns

111

112

Generic interfaces for reading from and writing to different carrier types.

113

114

```python { .api }

115

class Getter(Generic[CarrierT], ABC):

116

"""Abstract base class for retrieving values from carriers."""

117

118

def get(self, carrier: CarrierT, key: str) -> Optional[List[str]]:

119

"""

120

Retrieve the value(s) associated with the key from the carrier.

121

122

Parameters:

123

- carrier: The carrier to retrieve values from

124

- key: The key of the value to retrieve

125

126

Returns:

127

List of values associated with the key, or None if not found

128

"""

129

130

def keys(self, carrier: CarrierT) -> List[str]:

131

"""

132

Retrieve all keys from the carrier.

133

134

Parameters:

135

- carrier: The carrier to retrieve keys from

136

137

Returns:

138

List of all keys in the carrier

139

"""

140

141

class Setter(Generic[CarrierT], ABC):

142

"""Abstract base class for setting values in carriers."""

143

144

def set(self, carrier: CarrierT, key: str, value: str) -> None:

145

"""

146

Set a value in the carrier.

147

148

Parameters:

149

- carrier: The carrier to set the value in

150

- key: The key to set

151

- value: The value to set

152

"""

153

```

154

155

### Default Carrier Implementations

156

157

Default implementations for common carrier patterns.

158

159

```python { .api }

160

class DefaultGetter(Getter[Dict[str, str]]):

161

"""Default getter for dict-like carriers."""

162

163

def get(self, carrier: Dict[str, str], key: str) -> Optional[List[str]]:

164

"""Get value from dict carrier."""

165

166

def keys(self, carrier: Dict[str, str]) -> List[str]:

167

"""Get all keys from dict carrier."""

168

169

class DefaultSetter(Setter[Dict[str, str]]):

170

"""Default setter for dict-like carriers."""

171

172

def set(self, carrier: Dict[str, str], key: str, value: str) -> None:

173

"""Set value in dict carrier."""

174

175

default_getter: DefaultGetter

176

default_setter: DefaultSetter

177

```

178

179

### Composite Propagator

180

181

Combine multiple propagators for comprehensive context propagation.

182

183

```python { .api }

184

class CompositePropagator(TextMapPropagator):

185

"""Propagator that combines multiple propagators."""

186

187

def __init__(self, propagators: Sequence[TextMapPropagator]) -> None:

188

"""

189

Initialize with a list of propagators.

190

191

Parameters:

192

- propagators: Sequence of TextMapPropagator instances to combine

193

"""

194

195

def extract(

196

self,

197

carrier: CarrierT,

198

context: Optional[Context] = None,

199

getter: Getter[CarrierT] = default_getter,

200

) -> Context:

201

"""Extract using all propagators in sequence."""

202

203

def inject(

204

self,

205

carrier: CarrierT,

206

context: Optional[Context] = None,

207

setter: Setter[CarrierT] = default_setter,

208

) -> None:

209

"""Inject using all propagators."""

210

211

@property

212

def fields(self) -> Set[str]:

213

"""Return combined fields from all propagators."""

214

```

215

216

### W3C Trace Context Propagator

217

218

Standard W3C Trace Context propagation implementation.

219

220

```python { .api }

221

class TraceContextTextMapPropagator(TextMapPropagator):

222

"""W3C Trace Context propagator for trace correlation."""

223

224

def extract(

225

self,

226

carrier: CarrierT,

227

context: Optional[Context] = None,

228

getter: Getter[CarrierT] = default_getter,

229

) -> Context:

230

"""

231

Extract trace context from traceparent and tracestate headers.

232

233

Parameters:

234

- carrier: The carrier containing HTTP headers or equivalent

235

- context: Optional context to merge extracted data into

236

- getter: Object to retrieve header values from carrier

237

238

Returns:

239

Context with extracted trace information

240

"""

241

242

def inject(

243

self,

244

carrier: CarrierT,

245

context: Optional[Context] = None,

246

setter: Setter[CarrierT] = default_setter,

247

) -> None:

248

"""

249

Inject trace context into traceparent and tracestate headers.

250

251

Parameters:

252

- carrier: The carrier to inject headers into

253

- context: Optional context to inject, defaults to current context

254

- setter: Object to set header values in carrier

255

"""

256

257

@property

258

def fields(self) -> Set[str]:

259

"""Returns {'traceparent', 'tracestate'}."""

260

```

261

262

### W3C Baggage Propagator

263

264

Standard W3C Baggage propagation implementation.

265

266

```python { .api }

267

class W3CBaggagePropagator(TextMapPropagator):

268

"""W3C Baggage propagator for cross-service data propagation."""

269

270

def extract(

271

self,

272

carrier: CarrierT,

273

context: Optional[Context] = None,

274

getter: Getter[CarrierT] = default_getter,

275

) -> Context:

276

"""

277

Extract baggage from baggage header.

278

279

Parameters:

280

- carrier: The carrier containing HTTP headers or equivalent

281

- context: Optional context to merge extracted data into

282

- getter: Object to retrieve header values from carrier

283

284

Returns:

285

Context with extracted baggage information

286

"""

287

288

def inject(

289

self,

290

carrier: CarrierT,

291

context: Optional[Context] = None,

292

setter: Setter[CarrierT] = default_setter,

293

) -> None:

294

"""

295

Inject baggage into baggage header.

296

297

Parameters:

298

- carrier: The carrier to inject headers into

299

- context: Optional context to inject, defaults to current context

300

- setter: Object to set header values in carrier

301

"""

302

303

@property

304

def fields(self) -> Set[str]:

305

"""Returns {'baggage'}."""

306

```

307

308

### Baggage Operations

309

310

Manage baggage key-value pairs for cross-service context propagation.

311

312

```python { .api }

313

def get_all(context: Optional[Context] = None) -> Mapping[str, object]:

314

"""

315

Returns all name/value pairs in the Baggage.

316

317

Parameters:

318

- context: The Context to use, if not set uses current Context

319

320

Returns:

321

The name/value pairs in the Baggage as a read-only mapping

322

"""

323

324

def get_baggage(name: str, context: Optional[Context] = None) -> Optional[object]:

325

"""

326

Provides access to the value for a name/value pair in the Baggage.

327

328

Parameters:

329

- name: The name of the value to retrieve

330

- context: The Context to use, if not set uses current Context

331

332

Returns:

333

The value associated with the given name, or None if not present

334

"""

335

336

def set_baggage(

337

name: str,

338

value: object,

339

context: Optional[Context] = None

340

) -> Context:

341

"""

342

Sets a value in the Baggage.

343

344

Parameters:

345

- name: The name of the value to set

346

- value: The value to set

347

- context: The Context to use, if not set uses current Context

348

349

Returns:

350

A Context with the value updated

351

"""

352

353

def remove_baggage(name: str, context: Optional[Context] = None) -> Context:

354

"""

355

Removes a value from the Baggage.

356

357

Parameters:

358

- name: The name of the value to remove

359

- context: The Context to use, if not set uses current Context

360

361

Returns:

362

A Context with the name/value removed

363

"""

364

365

def clear(context: Optional[Context] = None) -> Context:

366

"""

367

Removes all values from the Baggage.

368

369

Parameters:

370

- context: The Context to use, if not set uses current Context

371

372

Returns:

373

A Context with all baggage entries removed

374

"""

375

```

376

377

## Usage Examples

378

379

### Basic HTTP Header Propagation

380

381

```python

382

from opentelemetry import propagate, trace, baggage

383

import requests

384

385

# Server side - extract context from incoming request

386

def handle_request(request):

387

# Extract context from HTTP headers

388

context = propagate.extract(request.headers)

389

390

# Activate the extracted context

391

token = context.attach(context)

392

try:

393

# Process request with trace context

394

tracer = trace.get_tracer(__name__)

395

with tracer.start_as_current_span("handle-request") as span:

396

span.set_attribute("http.method", request.method)

397

398

# Baggage is automatically available

399

user_id = baggage.get_baggage("user.id")

400

if user_id:

401

span.set_attribute("user.id", user_id)

402

403

return process_request()

404

finally:

405

context.detach(token)

406

407

# Client side - inject context into outgoing request

408

def make_request():

409

tracer = trace.get_tracer(__name__)

410

with tracer.start_as_current_span("http-request") as span:

411

# Set baggage for propagation

412

ctx = baggage.set_baggage("user.id", "12345")

413

token = context.attach(ctx)

414

415

try:

416

# Prepare outgoing request

417

headers = {}

418

419

# Inject current context into headers

420

propagate.inject(headers)

421

422

# Make request with propagated context

423

response = requests.get(

424

"http://api.example.com/data",

425

headers=headers

426

)

427

428

span.set_attribute("http.status_code", response.status_code)

429

return response.json()

430

finally:

431

context.detach(token)

432

```

433

434

### Custom Carrier Implementation

435

436

```python

437

from opentelemetry import propagate

438

from opentelemetry.propagators.textmap import Getter, Setter

439

from typing import Dict, List, Optional

440

441

# Custom carrier for message queue metadata

442

class MessageMetadata:

443

def __init__(self):

444

self.headers: Dict[str, str] = {}

445

446

def get_header(self, key: str) -> Optional[str]:

447

return self.headers.get(key)

448

449

def set_header(self, key: str, value: str) -> None:

450

self.headers[key] = value

451

452

def get_all_headers(self) -> Dict[str, str]:

453

return self.headers.copy()

454

455

# Custom getter for message metadata

456

class MessageGetter(Getter[MessageMetadata]):

457

def get(self, carrier: MessageMetadata, key: str) -> Optional[List[str]]:

458

value = carrier.get_header(key)

459

return [value] if value is not None else None

460

461

def keys(self, carrier: MessageMetadata) -> List[str]:

462

return list(carrier.get_all_headers().keys())

463

464

# Custom setter for message metadata

465

class MessageSetter(Setter[MessageMetadata]):

466

def set(self, carrier: MessageMetadata, key: str, value: str) -> None:

467

carrier.set_header(key, value)

468

469

# Usage with custom carrier

470

def publish_message(data):

471

metadata = MessageMetadata()

472

473

# Inject context into message metadata

474

propagate.inject(

475

metadata,

476

setter=MessageSetter()

477

)

478

479

# Publish with propagated context

480

message_broker.publish(data, metadata)

481

482

def consume_message(data, metadata):

483

# Extract context from message metadata

484

context = propagate.extract(

485

metadata,

486

getter=MessageGetter()

487

)

488

489

# Process with extracted context

490

token = context.attach(context)

491

try:

492

process_message(data)

493

finally:

494

context.detach(token)

495

```

496

497

### Flask Integration Example

498

499

```python

500

from flask import Flask, request, g

501

from opentelemetry import propagate, trace, baggage

502

from opentelemetry.propagators.textmap import Getter

503

504

app = Flask(__name__)

505

506

# Custom getter for Flask request headers

507

class FlaskRequestGetter(Getter):

508

def get(self, carrier, key: str):

509

return carrier.headers.getlist(key)

510

511

def keys(self, carrier):

512

return list(carrier.headers.keys())

513

514

@app.before_request

515

def before_request():

516

# Extract context from incoming request

517

context = propagate.extract(

518

request,

519

getter=FlaskRequestGetter()

520

)

521

522

# Store context in Flask's g object

523

g.otel_context = context

524

g.otel_token = context.attach(context)

525

526

@app.after_request

527

def after_request(response):

528

# Clean up context

529

if hasattr(g, 'otel_token'):

530

context.detach(g.otel_token)

531

return response

532

533

@app.route('/api/users')

534

def get_users():

535

tracer = trace.get_tracer(__name__)

536

with tracer.start_as_current_span("get-users") as span:

537

# Context and baggage automatically available

538

user_role = baggage.get_baggage("user.role")

539

540

span.set_attribute("http.method", request.method)

541

span.set_attribute("http.url", request.url)

542

543

if user_role:

544

span.set_attribute("user.role", user_role)

545

546

return {"users": get_user_list()}

547

548

def make_external_request(url):

549

"""Make external request with context propagation."""

550

headers = {}

551

552

# Inject current context

553

propagate.inject(headers)

554

555

# Make request

556

response = requests.get(url, headers=headers)

557

return response.json()

558

```

559

560

### Environment-Based Propagator Configuration

561

562

```python

563

import os

564

from opentelemetry import propagate

565

from opentelemetry.propagators.composite import CompositePropagator

566

from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator

567

from opentelemetry.baggage.propagation import W3CBaggagePropagator

568

569

# Configure propagators based on environment

570

def setup_propagators():

571

# Default propagators

572

propagators = [

573

TraceContextTextMapPropagator(),

574

W3CBaggagePropagator(),

575

]

576

577

# Check environment configuration

578

env_propagators = os.environ.get("OTEL_PROPAGATORS", "tracecontext,baggage")

579

580

if env_propagators == "none":

581

# Disable all propagation

582

propagators = []

583

elif "jaeger" in env_propagators:

584

# Add Jaeger propagator if available

585

try:

586

from opentelemetry.propagators.jaeger import JaegerPropagator

587

propagators.append(JaegerPropagator())

588

except ImportError:

589

pass

590

591

# Set global propagator

592

composite = CompositePropagator(propagators)

593

propagate.set_global_textmap(composite)

594

595

# Initialize propagators

596

setup_propagators()

597

```

598

599

### Testing Propagation

600

601

```python

602

from opentelemetry import propagate, trace, baggage

603

from opentelemetry.propagators.textmap import DefaultGetter, DefaultSetter

604

605

def test_context_propagation():

606

"""Test context extraction and injection."""

607

tracer = trace.get_tracer(__name__)

608

609

# Create a span and set baggage

610

with tracer.start_as_current_span("test-span") as span:

611

ctx = baggage.set_baggage("test.key", "test.value")

612

token = context.attach(ctx)

613

614

try:

615

# Inject into headers

616

headers = {}

617

propagate.inject(headers)

618

619

print("Injected headers:")

620

for key, value in headers.items():

621

print(f" {key}: {value}")

622

623

# Extract from headers (simulating different service)

624

extracted_context = propagate.extract(headers)

625

626

# Verify extraction

627

token2 = context.attach(extracted_context)

628

try:

629

# Should have baggage and trace context

630

test_value = baggage.get_baggage("test.key")

631

current_span = trace.get_current_span()

632

633

print(f"Extracted baggage: {test_value}")

634

print(f"Extracted span: {current_span.get_span_context()}")

635

636

assert test_value == "test.value"

637

assert current_span.get_span_context().is_valid

638

639

finally:

640

context.detach(token2)

641

finally:

642

context.detach(token)

643

644

# Run test

645

test_context_propagation()

646

```

647

648

## Type Definitions

649

650

```python { .api }

651

from typing import TypeVar, Generic, Dict, List, Optional, Set

652

653

CarrierT = TypeVar("CarrierT") # Type variable for carrier objects

654

```