or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

bound-loggers.mdconfiguration.mdcontext-management.mddevelopment-tools.mdexception-handling.mdindex.mdlogger-creation.mdoutput-loggers.mdprocessors.mdstdlib-integration.mdtesting.md

exception-handling.mddocs/

0

# Exception and Traceback Handling

1

2

Advanced exception processing including structured traceback extraction, rich formatting, and JSON-serializable exception representations. These tools provide comprehensive support for capturing, formatting, and analyzing exceptions in structured logging scenarios.

3

4

## Capabilities

5

6

### Exception Data Structures

7

8

Data classes for representing structured exception and traceback information.

9

10

```python { .api }

11

class Frame:

12

"""

13

Dataclass representing a single stack frame.

14

15

Contains information about a single frame in a stack trace,

16

including filename, line number, function name, and local variables.

17

"""

18

19

filename: str

20

"""Path to the source file."""

21

22

lineno: int

23

"""Line number in the source file."""

24

25

name: str

26

"""Function or method name."""

27

28

locals: dict[str, str] | None = None

29

"""Local variables in the frame (if captured)."""

30

31

class SyntaxError_:

32

"""

33

Dataclass representing SyntaxError details.

34

35

Specific information for syntax errors, including the problematic

36

line and character offset.

37

"""

38

39

offset: int

40

"""Character offset where syntax error occurred."""

41

42

filename: str

43

"""Filename where syntax error occurred."""

44

45

line: str

46

"""The problematic line of code."""

47

48

lineno: int

49

"""Line number where syntax error occurred."""

50

51

msg: str

52

"""Syntax error message."""

53

54

class Stack:

55

"""

56

Dataclass representing an exception and its stack frames.

57

58

Contains complete information about a single exception including

59

its type, value, notes, and the stack frames leading to it.

60

"""

61

62

exc_type: str

63

"""Exception type name."""

64

65

exc_value: str

66

"""String representation of exception value."""

67

68

exc_notes: list[str] = field(default_factory=list)

69

"""Exception notes (Python 3.11+)."""

70

71

syntax_error: SyntaxError_ | None = None

72

"""SyntaxError details if applicable."""

73

74

is_cause: bool = False

75

"""True if this exception was the cause of another."""

76

77

frames: list[Frame] = field(default_factory=list)

78

"""Stack frames for this exception."""

79

80

is_group: bool = False

81

"""True if this is part of an exception group."""

82

83

exceptions: list[Trace] = field(default_factory=list)

84

"""Nested exceptions for exception groups."""

85

86

class Trace:

87

"""

88

Container for complete stack trace information.

89

90

Top-level container that holds all the stacks for a complete

91

exception trace, including chained exceptions.

92

"""

93

94

stacks: list[Stack]

95

"""List of Stack objects representing the complete trace."""

96

```

97

98

### Exception Processing

99

100

Functions and classes for extracting and processing exception information.

101

102

```python { .api }

103

def extract(

104

exc_type,

105

exc_value,

106

traceback,

107

*,

108

show_locals=False,

109

locals_max_length=10,

110

locals_max_string=80,

111

locals_hide_dunder=True,

112

locals_hide_sunder=False,

113

use_rich=True

114

) -> Trace:

115

"""

116

Extract structured exception information from exc_info tuple.

117

118

Args:

119

exc_type: Exception type

120

exc_value: Exception instance

121

traceback: Traceback object

122

show_locals (bool): Include local variables in frames

123

locals_max_length (int): Maximum number of locals to show per frame

124

locals_max_string (int): Maximum length of local variable strings

125

locals_hide_dunder (bool): Hide dunder variables (__var__)

126

locals_hide_sunder (bool): Hide sunder variables (_var)

127

use_rich (bool): Use rich library for enhanced extraction if available

128

129

Returns:

130

Trace: Structured representation of the exception trace

131

"""

132

133

class ExceptionDictTransformer:

134

"""

135

Transform exceptions into JSON-serializable dictionaries.

136

137

Converts exception information into structured dictionaries

138

that can be easily serialized and analyzed.

139

"""

140

141

def __init__(

142

self,

143

show_locals=True,

144

locals_max_length=10,

145

locals_max_string=80,

146

locals_hide_dunder=True,

147

locals_hide_sunder=False,

148

suppress=(),

149

max_frames=50,

150

use_rich=True

151

):

152

"""

153

Initialize ExceptionDictTransformer.

154

155

Args:

156

show_locals (bool): Include local variables in output

157

locals_max_length (int): Maximum number of locals per frame

158

locals_max_string (int): Maximum length of local variable strings

159

locals_hide_dunder (bool): Hide dunder variables

160

locals_hide_sunder (bool): Hide sunder variables

161

suppress (tuple): Module names to suppress in tracebacks

162

max_frames (int): Maximum number of frames to include

163

use_rich (bool): Use rich library if available

164

"""

165

166

def __call__(self, exc_info) -> list[dict[str, Any]]:

167

"""

168

Transform exception info to structured dictionaries.

169

170

Args:

171

exc_info: Exception info tuple (type, value, traceback)

172

173

Returns:

174

list: List of dictionaries representing the exception trace

175

"""

176

```

177

178

### Utility Functions

179

180

Helper functions for safe string conversion and representation.

181

182

```python { .api }

183

def safe_str(obj) -> str:

184

"""

185

Safely convert object to string representation.

186

187

Handles cases where str() might raise an exception by

188

providing fallback representations.

189

190

Args:

191

obj: Object to convert to string

192

193

Returns:

194

str: String representation of the object

195

"""

196

197

def to_repr(obj, max_length=None, max_string=None, use_rich=True) -> str:

198

"""

199

Safe repr conversion with length limits and rich formatting.

200

201

Args:

202

obj: Object to represent

203

max_length (int, optional): Maximum length of result string

204

max_string (int, optional): Maximum length for string values

205

use_rich (bool): Use rich library for enhanced formatting

206

207

Returns:

208

str: String representation with length limits applied

209

"""

210

```

211

212

## Usage Examples

213

214

### Basic Exception Extraction

215

216

```python

217

import structlog

218

from structlog import tracebacks

219

220

def problematic_function():

221

local_var = "important data"

222

user_data = {"id": 123, "name": "Alice"}

223

raise ValueError("Something went wrong with the data")

224

225

try:

226

problematic_function()

227

except Exception:

228

import sys

229

230

# Extract structured exception information

231

trace = tracebacks.extract(*sys.exc_info(), show_locals=True)

232

233

# Examine the trace structure

234

for stack in trace.stacks:

235

print(f"Exception: {stack.exc_type}")

236

print(f"Message: {stack.exc_value}")

237

238

for frame in stack.frames:

239

print(f" File: {frame.filename}:{frame.lineno}")

240

print(f" Function: {frame.name}")

241

if frame.locals:

242

print(f" Locals: {frame.locals}")

243

```

244

245

### JSON-Serializable Exception Processing

246

247

```python

248

import structlog

249

from structlog import tracebacks, processors

250

import json

251

252

# Configure processor to transform exceptions to dictionaries

253

transformer = tracebacks.ExceptionDictTransformer(

254

show_locals=True,

255

locals_max_length=5,

256

locals_max_string=100

257

)

258

259

def exception_to_dict_processor(logger, method_name, event_dict):

260

"""Custom processor to handle exceptions."""

261

if "exc_info" in event_dict:

262

exc_info = event_dict.pop("exc_info")

263

if exc_info and exc_info != (None, None, None):

264

event_dict["exception_trace"] = transformer(exc_info)

265

return event_dict

266

267

structlog.configure(

268

processors=[

269

processors.TimeStamper(),

270

exception_to_dict_processor,

271

processors.JSONRenderer()

272

],

273

wrapper_class=structlog.BoundLogger,

274

)

275

276

logger = structlog.get_logger()

277

278

def nested_function():

279

data = {"items": [1, 2, 3], "config": {"debug": True}}

280

raise KeyError("Missing required key 'user_id'")

281

282

def calling_function():

283

context = "user_processing"

284

nested_function()

285

286

try:

287

calling_function()

288

except Exception:

289

logger.exception("Processing failed", component="user_service")

290

# Output includes structured exception trace as JSON

291

```

292

293

### Custom Exception Formatting

294

295

```python

296

import structlog

297

from structlog import tracebacks, processors

298

299

def custom_exception_processor(logger, method_name, event_dict):

300

"""Custom processor for detailed exception formatting."""

301

if "exc_info" in event_dict:

302

exc_info = event_dict.pop("exc_info")

303

if exc_info and exc_info != (None, None, None):

304

# Extract detailed trace information

305

trace = tracebacks.extract(

306

*exc_info,

307

show_locals=True,

308

locals_max_length=3,

309

locals_hide_dunder=True

310

)

311

312

# Create custom exception summary

313

exception_summary = {

314

"error_type": trace.stacks[-1].exc_type,

315

"error_message": trace.stacks[-1].exc_value,

316

"stack_depth": sum(len(stack.frames) for stack in trace.stacks),

317

"top_frame": {

318

"file": trace.stacks[-1].frames[-1].filename,

319

"line": trace.stacks[-1].frames[-1].lineno,

320

"function": trace.stacks[-1].frames[-1].name

321

}

322

}

323

324

event_dict["exception_summary"] = exception_summary

325

326

return event_dict

327

328

structlog.configure(

329

processors=[

330

processors.TimeStamper(),

331

custom_exception_processor,

332

processors.JSONRenderer()

333

],

334

wrapper_class=structlog.BoundLogger,

335

)

336

337

logger = structlog.get_logger()

338

339

def process_data(data):

340

if not data:

341

raise ValueError("Data cannot be empty")

342

return len(data)

343

344

try:

345

result = process_data(None)

346

except Exception:

347

logger.exception("Data processing failed", operation="length_calculation")

348

```

349

350

### Exception Chain Analysis

351

352

```python

353

import structlog

354

from structlog import tracebacks

355

356

def analyze_exception_chain():

357

"""Demonstrate exception chaining analysis."""

358

359

def database_operation():

360

raise ConnectionError("Database connection failed")

361

362

def service_operation():

363

try:

364

database_operation()

365

except ConnectionError as e:

366

raise RuntimeError("Service operation failed") from e

367

368

def api_handler():

369

try:

370

service_operation()

371

except RuntimeError as e:

372

raise ValueError("API request failed") from e

373

374

try:

375

api_handler()

376

except Exception:

377

import sys

378

379

# Extract the complete exception chain

380

trace = tracebacks.extract(*sys.exc_info())

381

382

print(f"Exception chain has {len(trace.stacks)} levels:")

383

for i, stack in enumerate(trace.stacks):

384

print(f" Level {i + 1}: {stack.exc_type} - {stack.exc_value}")

385

print(f" Is cause: {stack.is_cause}")

386

print(f" Frames: {len(stack.frames)}")

387

388

analyze_exception_chain()

389

```

390

391

### Rich Exception Formatting Integration

392

393

```python

394

import structlog

395

from structlog import tracebacks, processors, dev

396

397

# Configure with rich exception formatting

398

def rich_exception_processor(logger, method_name, event_dict):

399

"""Processor that uses rich for exception formatting."""

400

if "exc_info" in event_dict:

401

exc_info = event_dict.pop("exc_info")

402

if exc_info and exc_info != (None, None, None):

403

# Use ExceptionDictTransformer with rich enabled

404

transformer = tracebacks.ExceptionDictTransformer(

405

show_locals=True,

406

use_rich=True,

407

locals_max_length=5

408

)

409

410

event_dict["rich_exception"] = transformer(exc_info)

411

412

return event_dict

413

414

structlog.configure(

415

processors=[

416

processors.TimeStamper(),

417

rich_exception_processor,

418

processors.JSONRenderer(indent=2)

419

],

420

wrapper_class=structlog.BoundLogger,

421

)

422

423

logger = structlog.get_logger()

424

425

def complex_operation():

426

data = {"users": [{"id": 1, "name": "Alice"}], "config": {"retry": 3}}

427

items = [1, 2, 3, 4, 5]

428

429

# Simulate complex operation with multiple locals

430

for i, item in enumerate(items):

431

if item == 4:

432

raise IndexError(f"Invalid item at index {i}: {item}")

433

434

try:

435

complex_operation()

436

except Exception:

437

logger.exception("Complex operation failed", operation_id="op_123")

438

```

439

440

### Safe String Conversion

441

442

```python

443

from structlog import tracebacks

444

445

class ProblematicObject:

446

"""Object that raises exception in __str__."""

447

448

def __str__(self):

449

raise RuntimeError("Cannot convert to string")

450

451

def __repr__(self):

452

return "ProblematicObject(broken)"

453

454

# Test safe string conversion

455

obj = ProblematicObject()

456

457

# This would raise an exception:

458

# str(obj)

459

460

# But this handles it safely:

461

safe_string = tracebacks.safe_str(obj)

462

print(f"Safe string: {safe_string}")

463

464

# Test safe repr with limits

465

long_dict = {f"key_{i}": f"value_{i}" * 100 for i in range(10)}

466

limited_repr = tracebacks.to_repr(long_dict, max_length=200)

467

print(f"Limited repr: {limited_repr}")

468

```

469

470

### Exception Suppression

471

472

```python

473

import structlog

474

from structlog import tracebacks

475

476

# Configure transformer to suppress certain modules

477

transformer = tracebacks.ExceptionDictTransformer(

478

show_locals=False,

479

suppress=("logging", "structlog._config", "structlog.processors"),

480

max_frames=10

481

)

482

483

def application_code():

484

"""User application code."""

485

library_code()

486

487

def library_code():

488

"""Simulated library code that should be suppressed."""

489

raise ValueError("Library error")

490

491

try:

492

application_code()

493

except Exception:

494

import sys

495

496

# Transform with suppression

497

result = transformer(sys.exc_info())

498

499

# Examine which frames were included/suppressed

500

for trace_dict in result:

501

print(f"Exception: {trace_dict.get('exc_type')}")

502

print(f"Frames shown: {len(trace_dict.get('frames', []))}")

503

504

for frame in trace_dict.get('frames', []):

505

print(f" {frame['filename']}:{frame['lineno']} in {frame['name']}")

506

```

507

508

### Integration with Structured Logging

509

510

```python

511

import structlog

512

from structlog import tracebacks, processors

513

514

class StructuredExceptionProcessor:

515

"""Processor that creates structured exception data."""

516

517

def __init__(self):

518

self.transformer = tracebacks.ExceptionDictTransformer(

519

show_locals=True,

520

locals_max_length=3,

521

max_frames=20

522

)

523

524

def __call__(self, logger, method_name, event_dict):

525

if "exc_info" in event_dict:

526

exc_info = event_dict.pop("exc_info")

527

528

if exc_info and exc_info != (None, None, None):

529

# Create structured exception data

530

trace_data = self.transformer(exc_info)

531

532

# Extract key information

533

if trace_data:

534

last_exception = trace_data[-1]

535

event_dict.update({

536

"error": {

537

"type": last_exception["exc_type"],

538

"message": last_exception["exc_value"],

539

"traceback": trace_data

540

}

541

})

542

543

return event_dict

544

545

structlog.configure(

546

processors=[

547

processors.TimeStamper(),

548

StructuredExceptionProcessor(),

549

processors.JSONRenderer()

550

],

551

wrapper_class=structlog.BoundLogger,

552

)

553

554

logger = structlog.get_logger()

555

556

def business_logic(user_data):

557

if not user_data.get("email"):

558

raise ValueError("Email is required for user registration")

559

560

try:

561

business_logic({"name": "Alice"})

562

except Exception:

563

logger.exception(

564

"User registration failed",

565

user_name="Alice",

566

registration_step="validation"

567

)

568

```