or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

logging.mddocs/

0

# Logging and Events

1

2

Comprehensive structured logging and event recording capabilities with OpenTelemetry integration. The logging API provides severity-based log record emission with trace correlation, while the events API enables structured event recording with rich attributes and context integration.

3

4

## Capabilities

5

6

### Logger Creation and Management

7

8

Get loggers for structured log record emission with proper instrumentation scope identification.

9

10

```python { .api }

11

def get_logger(

12

name: str,

13

instrumenting_library_version: Optional[str] = None,

14

logger_provider: Optional[LoggerProvider] = None,

15

schema_url: Optional[str] = None,

16

attributes: Optional[Attributes] = None,

17

) -> Logger:

18

"""

19

Returns a Logger for use by the given instrumentation library.

20

21

Parameters:

22

- name: The name of the instrumentation scope

23

- instrumenting_library_version: Optional version of the instrumentation library

24

- logger_provider: Optional specific LoggerProvider to use

25

- schema_url: Optional Schema URL of the emitted telemetry

26

- attributes: Optional attributes of the emitted telemetry

27

28

Returns:

29

Logger instance for emitting log records

30

"""

31

32

def get_logger_provider() -> LoggerProvider:

33

"""Gets the current global LoggerProvider object."""

34

35

def set_logger_provider(logger_provider: LoggerProvider) -> None:

36

"""

37

Sets the current global LoggerProvider object.

38

This can only be done once, a warning will be logged if any further attempt is made.

39

"""

40

```

41

42

### Log Record Structure and Emission

43

44

Create and emit structured log records with severity levels and rich attributes.

45

46

```python { .api }

47

class Logger(ABC):

48

"""Abstract base class for loggers."""

49

50

def emit(self, log_record: LogRecord) -> None:

51

"""

52

Emit a LogRecord.

53

54

Parameters:

55

- log_record: The LogRecord to emit

56

"""

57

58

class LogRecord:

59

"""A LogRecord instance represents an occurrence of an event."""

60

61

def __init__(

62

self,

63

timestamp: Optional[int] = None,

64

observed_timestamp: Optional[int] = None,

65

trace_id: Optional[int] = None,

66

span_id: Optional[int] = None,

67

trace_flags: Optional[TraceFlags] = None,

68

severity_text: Optional[str] = None,

69

severity_number: Optional[SeverityNumber] = None,

70

body: Optional[Any] = None,

71

resource: Optional[Resource] = None,

72

attributes: Optional[Attributes] = None,

73

) -> None:

74

"""

75

Create a new LogRecord.

76

77

Parameters:

78

- timestamp: Time when the log record occurred

79

- observed_timestamp: Time when the log record was observed

80

- trace_id: Trace ID for correlation

81

- span_id: Span ID for correlation

82

- trace_flags: Trace flags for correlation

83

- severity_text: Severity level as text

84

- severity_number: Severity level as number

85

- body: The log record body/message

86

- resource: Resource information

87

- attributes: Additional attributes

88

"""

89

90

@property

91

def timestamp(self) -> Optional[int]:

92

"""Returns the timestamp of the log record."""

93

94

@property

95

def observed_timestamp(self) -> Optional[int]:

96

"""Returns the observed timestamp of the log record."""

97

98

@property

99

def trace_id(self) -> Optional[int]:

100

"""Returns the trace ID for correlation."""

101

102

@property

103

def span_id(self) -> Optional[int]:

104

"""Returns the span ID for correlation."""

105

106

@property

107

def trace_flags(self) -> Optional[TraceFlags]:

108

"""Returns the trace flags for correlation."""

109

110

@property

111

def severity_text(self) -> Optional[str]:

112

"""Returns the severity text."""

113

114

@property

115

def severity_number(self) -> Optional[SeverityNumber]:

116

"""Returns the severity number."""

117

118

@property

119

def body(self) -> Optional[Any]:

120

"""Returns the log record body."""

121

122

@property

123

def attributes(self) -> Optional[Attributes]:

124

"""Returns the log record attributes."""

125

```

126

127

### Severity Levels

128

129

Standard severity levels for log records following OpenTelemetry specification.

130

131

```python { .api }

132

class SeverityNumber(Enum):

133

"""Severity levels for log records."""

134

135

TRACE = 1 # Trace level (most verbose)

136

TRACE2 = 2

137

TRACE3 = 3

138

TRACE4 = 4

139

DEBUG = 5 # Debug level

140

DEBUG2 = 6

141

DEBUG3 = 7

142

DEBUG4 = 8

143

INFO = 9 # Info level

144

INFO2 = 10

145

INFO3 = 11

146

INFO4 = 12

147

WARN = 13 # Warning level

148

WARN2 = 14

149

WARN3 = 15

150

WARN4 = 16

151

ERROR = 17 # Error level

152

ERROR2 = 18

153

ERROR3 = 19

154

ERROR4 = 20

155

FATAL = 21 # Fatal level (least verbose)

156

FATAL2 = 22

157

FATAL3 = 23

158

FATAL4 = 24

159

```

160

161

### Logger Provider Implementations

162

163

Provider classes for different deployment scenarios.

164

165

```python { .api }

166

class LoggerProvider(ABC):

167

"""Abstract base class for logger providers."""

168

169

def get_logger(

170

self,

171

name: str,

172

instrumenting_library_version: Optional[str] = None,

173

schema_url: Optional[str] = None,

174

attributes: Optional[Attributes] = None,

175

) -> Logger:

176

"""Returns a Logger for use by the given instrumentation library."""

177

178

class NoOpLoggerProvider(LoggerProvider):

179

"""The default LoggerProvider, used when no implementation is available."""

180

181

def get_logger(

182

self,

183

name: str,

184

instrumenting_library_version: Optional[str] = None,

185

schema_url: Optional[str] = None,

186

attributes: Optional[Attributes] = None,

187

) -> Logger:

188

"""Returns a no-op logger."""

189

190

class NoOpLogger(Logger):

191

"""No-op implementation of Logger."""

192

193

def emit(self, log_record: LogRecord) -> None:

194

"""No-op emit implementation."""

195

```

196

197

### Event Logger System

198

199

Structured event recording with enhanced semantics and type safety.

200

201

```python { .api }

202

def get_event_logger_provider() -> EventLoggerProvider:

203

"""Gets the current global EventLoggerProvider object."""

204

205

def set_event_logger_provider(event_logger_provider: EventLoggerProvider) -> None:

206

"""

207

Sets the current global EventLoggerProvider object.

208

This can only be done once, a warning will be logged if any further attempt is made.

209

"""

210

211

def get_event_logger(

212

name: str,

213

instrumenting_library_version: Optional[str] = None,

214

event_logger_provider: Optional[EventLoggerProvider] = None,

215

schema_url: Optional[str] = None,

216

attributes: Optional[Attributes] = None,

217

) -> EventLogger:

218

"""

219

Returns an EventLogger for use by the given instrumentation library.

220

221

Parameters:

222

- name: The name of the instrumentation scope

223

- instrumenting_library_version: Optional version of the instrumentation library

224

- event_logger_provider: Optional specific EventLoggerProvider to use

225

- schema_url: Optional Schema URL of the emitted telemetry

226

- attributes: Optional attributes of the emitted telemetry

227

228

Returns:

229

EventLogger instance for emitting events

230

"""

231

```

232

233

### Event Structure and Operations

234

235

Create and emit structured events with rich semantic information.

236

237

```python { .api }

238

class EventLogger(ABC):

239

"""Abstract base class for event loggers."""

240

241

def emit(self, event: Event) -> None:

242

"""

243

Emit an Event.

244

245

Parameters:

246

- event: The Event to emit

247

"""

248

249

class Event(LogRecord):

250

"""An Event is a specialized LogRecord with additional semantics."""

251

252

def __init__(

253

self,

254

name: str,

255

timestamp: Optional[int] = None,

256

attributes: Optional[Attributes] = None,

257

**kwargs,

258

) -> None:

259

"""

260

Create a new Event.

261

262

Parameters:

263

- name: The name of the event

264

- timestamp: Time when the event occurred

265

- attributes: Event attributes

266

- kwargs: Additional LogRecord parameters

267

"""

268

269

@property

270

def name(self) -> str:

271

"""Returns the event name."""

272

```

273

274

### Event Logger Provider Implementations

275

276

Provider classes for event logger creation and management.

277

278

```python { .api }

279

class EventLoggerProvider(ABC):

280

"""Abstract base class for event logger providers."""

281

282

def get_event_logger(

283

self,

284

name: str,

285

instrumenting_library_version: Optional[str] = None,

286

schema_url: Optional[str] = None,

287

attributes: Optional[Attributes] = None,

288

) -> EventLogger:

289

"""Returns an EventLogger for use by the given instrumentation library."""

290

291

class NoOpEventLoggerProvider(EventLoggerProvider):

292

"""The default EventLoggerProvider, used when no implementation is available."""

293

294

def get_event_logger(

295

self,

296

name: str,

297

instrumenting_library_version: Optional[str] = None,

298

schema_url: Optional[str] = None,

299

attributes: Optional[Attributes] = None,

300

) -> EventLogger:

301

"""Returns a no-op event logger."""

302

303

class ProxyEventLoggerProvider(EventLoggerProvider):

304

"""Proxy event logger provider for late binding of real providers."""

305

306

def get_event_logger(

307

self,

308

name: str,

309

instrumenting_library_version: Optional[str] = None,

310

schema_url: Optional[str] = None,

311

attributes: Optional[Attributes] = None,

312

) -> EventLogger:

313

"""Returns an event logger from the real provider or proxy event logger."""

314

315

class NoOpEventLogger(EventLogger):

316

"""No-op implementation of EventLogger."""

317

318

def emit(self, event: Event) -> None:

319

"""No-op emit implementation."""

320

321

class ProxyEventLogger(EventLogger):

322

"""Proxy event logger for late binding of real event loggers."""

323

324

def emit(self, event: Event) -> None:

325

"""Emit using real event logger or no-op."""

326

```

327

328

## Usage Examples

329

330

### Basic Structured Logging

331

332

```python

333

from opentelemetry._logs import get_logger, LogRecord, SeverityNumber

334

from opentelemetry import trace

335

import time

336

337

# Get a logger

338

logger = get_logger(__name__)

339

340

# Create and emit log records

341

def log_user_action(user_id: str, action: str):

342

# Get current trace context for correlation

343

current_span = trace.get_current_span()

344

span_context = current_span.get_span_context()

345

346

log_record = LogRecord(

347

timestamp=int(time.time() * 1_000_000_000), # nanoseconds

348

severity_text="INFO",

349

severity_number=SeverityNumber.INFO,

350

body=f"User {user_id} performed action: {action}",

351

attributes={

352

"user.id": user_id,

353

"action": action,

354

"service.name": "user-service",

355

},

356

trace_id=span_context.trace_id if span_context.is_valid else None,

357

span_id=span_context.span_id if span_context.is_valid else None,

358

trace_flags=span_context.trace_flags if span_context.is_valid else None,

359

)

360

361

logger.emit(log_record)

362

363

# Usage

364

log_user_action("12345", "login")

365

```

366

367

### Event-Based Logging

368

369

```python

370

from opentelemetry._events import get_event_logger, Event

371

from opentelemetry import trace, baggage

372

import time

373

374

# Get an event logger

375

event_logger = get_event_logger(__name__)

376

377

def record_purchase_event(user_id: str, product_id: str, amount: float):

378

"""Record a structured purchase event."""

379

380

# Create event with rich attributes

381

event = Event(

382

name="user.purchase",

383

timestamp=int(time.time() * 1_000_000_000),

384

attributes={

385

"user.id": user_id,

386

"product.id": product_id,

387

"purchase.amount": amount,

388

"currency": "USD",

389

"event.category": "commerce",

390

}

391

)

392

393

# Emit the event

394

event_logger.emit(event)

395

396

def record_system_event(event_name: str, **attributes):

397

"""Record a generic system event."""

398

event = Event(

399

name=event_name,

400

attributes=attributes

401

)

402

event_logger.emit(event)

403

404

# Usage examples

405

record_purchase_event("12345", "prod-789", 29.99)

406

record_system_event("service.startup", service="user-service", version="1.2.3")

407

```

408

409

### Logging with Trace Correlation

410

411

```python

412

from opentelemetry import trace

413

from opentelemetry._logs import get_logger, LogRecord, SeverityNumber

414

415

logger = get_logger(__name__)

416

tracer = trace.get_tracer(__name__)

417

418

def process_order_with_logging(order_id: str):

419

"""Process an order with comprehensive logging."""

420

421

with tracer.start_as_current_span("process-order") as span:

422

span.set_attribute("order.id", order_id)

423

424

try:

425

# Log start of processing

426

log_info("Order processing started", {

427

"order.id": order_id,

428

"stage": "start"

429

})

430

431

# Simulate processing steps

432

validate_order(order_id)

433

log_info("Order validated", {"order.id": order_id, "stage": "validation"})

434

435

charge_payment(order_id)

436

log_info("Payment charged", {"order.id": order_id, "stage": "payment"})

437

438

ship_order(order_id)

439

log_info("Order shipped", {"order.id": order_id, "stage": "shipping"})

440

441

# Log successful completion

442

log_info("Order processing completed", {

443

"order.id": order_id,

444

"stage": "complete",

445

"result": "success"

446

})

447

448

except Exception as e:

449

# Log error with exception details

450

log_error(f"Order processing failed: {e}", {

451

"order.id": order_id,

452

"error.type": type(e).__name__,

453

"error.message": str(e)

454

})

455

span.record_exception(e)

456

raise

457

458

def log_info(message: str, attributes: dict = None):

459

"""Helper function for info logging with trace correlation."""

460

current_span = trace.get_current_span()

461

span_context = current_span.get_span_context()

462

463

log_record = LogRecord(

464

timestamp=int(time.time() * 1_000_000_000),

465

severity_text="INFO",

466

severity_number=SeverityNumber.INFO,

467

body=message,

468

attributes=attributes or {},

469

trace_id=span_context.trace_id if span_context.is_valid else None,

470

span_id=span_context.span_id if span_context.is_valid else None,

471

trace_flags=span_context.trace_flags if span_context.is_valid else None,

472

)

473

474

logger.emit(log_record)

475

476

def log_error(message: str, attributes: dict = None):

477

"""Helper function for error logging with trace correlation."""

478

current_span = trace.get_current_span()

479

span_context = current_span.get_span_context()

480

481

log_record = LogRecord(

482

timestamp=int(time.time() * 1_000_000_000),

483

severity_text="ERROR",

484

severity_number=SeverityNumber.ERROR,

485

body=message,

486

attributes=attributes or {},

487

trace_id=span_context.trace_id if span_context.is_valid else None,

488

span_id=span_context.span_id if span_context.is_valid else None,

489

trace_flags=span_context.trace_flags if span_context.is_valid else None,

490

)

491

492

logger.emit(log_record)

493

```

494

495

### Structured Logging with Baggage Context

496

497

```python

498

from opentelemetry import baggage, context

499

from opentelemetry._logs import get_logger, LogRecord, SeverityNumber

500

import time

501

502

logger = get_logger(__name__)

503

504

def enhanced_logging_example():

505

"""Demonstrate logging with baggage context."""

506

507

# Set baggage context

508

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

509

ctx = baggage.set_baggage("request.id", "req-abc-123", ctx)

510

ctx = baggage.set_baggage("service.version", "1.2.3", ctx)

511

512

token = context.attach(ctx)

513

514

try:

515

# Log with baggage automatically included

516

log_with_baggage("Processing user request", {

517

"operation": "user.profile.update",

518

"timestamp": time.time()

519

})

520

521

# Simulate some processing

522

process_user_update()

523

524

log_with_baggage("User request completed successfully", {

525

"operation": "user.profile.update",

526

"result": "success"

527

})

528

529

except Exception as e:

530

log_with_baggage(f"User request failed: {e}", {

531

"operation": "user.profile.update",

532

"result": "error",

533

"error.type": type(e).__name__

534

}, SeverityNumber.ERROR)

535

536

finally:

537

context.detach(token)

538

539

def log_with_baggage(message: str, attributes: dict = None, severity: SeverityNumber = SeverityNumber.INFO):

540

"""Log with automatic baggage inclusion."""

541

542

# Get all baggage and include in attributes

543

all_baggage = baggage.get_all()

544

combined_attributes = dict(all_baggage)

545

546

if attributes:

547

combined_attributes.update(attributes)

548

549

log_record = LogRecord(

550

timestamp=int(time.time() * 1_000_000_000),

551

severity_text=severity.name,

552

severity_number=severity,

553

body=message,

554

attributes=combined_attributes,

555

)

556

557

logger.emit(log_record)

558

```

559

560

### Custom Event Types

561

562

```python

563

from opentelemetry._events import get_event_logger, Event

564

from opentelemetry import trace

565

import time

566

from typing import Dict, Any

567

568

event_logger = get_event_logger(__name__)

569

570

class SecurityEvent(Event):

571

"""Custom event type for security-related events."""

572

573

def __init__(self, event_type: str, user_id: str, source_ip: str, **kwargs):

574

super().__init__(

575

name=f"security.{event_type}",

576

attributes={

577

"security.event_type": event_type,

578

"user.id": user_id,

579

"source.ip": source_ip,

580

"event.category": "security",

581

**kwargs

582

}

583

)

584

585

class BusinessEvent(Event):

586

"""Custom event type for business-related events."""

587

588

def __init__(self, event_type: str, entity_id: str, **kwargs):

589

super().__init__(

590

name=f"business.{event_type}",

591

attributes={

592

"business.event_type": event_type,

593

"entity.id": entity_id,

594

"event.category": "business",

595

**kwargs

596

}

597

)

598

599

# Usage examples

600

def record_security_events():

601

# Failed login attempt

602

security_event = SecurityEvent(

603

event_type="login_failed",

604

user_id="user123",

605

source_ip="192.168.1.100",

606

reason="invalid_password",

607

attempt_count=3

608

)

609

event_logger.emit(security_event)

610

611

# Successful login

612

security_event = SecurityEvent(

613

event_type="login_success",

614

user_id="user123",

615

source_ip="192.168.1.100",

616

session_id="sess-abc-456"

617

)

618

event_logger.emit(security_event)

619

620

def record_business_events():

621

# Order placed

622

business_event = BusinessEvent(

623

event_type="order_placed",

624

entity_id="order-789",

625

customer_id="cust-123",

626

order_total=99.99,

627

currency="USD"

628

)

629

event_logger.emit(business_event)

630

631

# Payment processed

632

business_event = BusinessEvent(

633

event_type="payment_processed",

634

entity_id="payment-456",

635

order_id="order-789",

636

amount=99.99,

637

payment_method="credit_card"

638

)

639

event_logger.emit(business_event)

640

```

641

642

### Integration with Standard Python Logging

643

644

```python

645

import logging

646

from opentelemetry._logs import get_logger, LogRecord, SeverityNumber

647

from opentelemetry import trace

648

import time

649

650

# Custom handler to bridge Python logging to OpenTelemetry

651

class OpenTelemetryLogHandler(logging.Handler):

652

def __init__(self):

653

super().__init__()

654

self.otel_logger = get_logger(__name__)

655

656

def emit(self, record: logging.LogRecord):

657

# Convert Python log level to OpenTelemetry severity

658

severity_map = {

659

logging.DEBUG: SeverityNumber.DEBUG,

660

logging.INFO: SeverityNumber.INFO,

661

logging.WARNING: SeverityNumber.WARN,

662

logging.ERROR: SeverityNumber.ERROR,

663

logging.CRITICAL: SeverityNumber.FATAL,

664

}

665

666

# Get current trace context

667

current_span = trace.get_current_span()

668

span_context = current_span.get_span_context()

669

670

# Create OpenTelemetry log record

671

otel_record = LogRecord(

672

timestamp=int(record.created * 1_000_000_000), # Convert to nanoseconds

673

severity_text=record.levelname,

674

severity_number=severity_map.get(record.levelno, SeverityNumber.INFO),

675

body=record.getMessage(),

676

attributes={

677

"python.logger.name": record.name,

678

"python.module": record.module,

679

"python.function": record.funcName,

680

"python.lineno": record.lineno,

681

},

682

trace_id=span_context.trace_id if span_context.is_valid else None,

683

span_id=span_context.span_id if span_context.is_valid else None,

684

trace_flags=span_context.trace_flags if span_context.is_valid else None,

685

)

686

687

self.otel_logger.emit(otel_record)

688

689

# Set up Python logging with OpenTelemetry handler

690

def setup_logging():

691

# Create and configure handler

692

handler = OpenTelemetryLogHandler()

693

handler.setLevel(logging.INFO)

694

695

# Add to root logger

696

root_logger = logging.getLogger()

697

root_logger.addHandler(handler)

698

root_logger.setLevel(logging.INFO)

699

700

# Usage

701

setup_logging()

702

703

# Now standard Python logging will be captured by OpenTelemetry

704

logger = logging.getLogger(__name__)

705

706

def example_function():

707

tracer = trace.get_tracer(__name__)

708

with tracer.start_as_current_span("example-operation"):

709

logger.info("Starting operation")

710

711

try:

712

# Some operation that might fail

713

result = risky_operation()

714

logger.info(f"Operation completed successfully: {result}")

715

716

except Exception as e:

717

logger.error(f"Operation failed: {e}", exc_info=True)

718

raise

719

```