Skip to main content

Decorator vs Context Manager Patterns

Basalt gives you two equivalent ways to create spans:
  • Decorators (@start_observe, @observe): best for tracing whole functions with minimal code.
  • Context managers (with start_observe(...), with observe(...)): best when you need a span handle to set input/output/attributes during execution.
In practice: use decorators by default, and reach for context managers when you need more control. Use decorators to trace function boundaries. This keeps code readable and consistent.

Context managers (fine-grained control)

Use context managers when you want to enrich spans mid-flight (custom attributes, partial outputs, branching logic).

Choosing between the two

  • Prefer decorators for request handlers, background jobs, and any code with clear function boundaries.
  • Prefer context managers when you need the span handle or you’re tracing a block that isn’t naturally a function.
  • Mixing both is normal: start_observe at the entry point, observe spans for key steps, and auto-instrumentation for provider calls.

Async note

The same patterns work in async code (async functions + async with ... where applicable). See Workflows for full async examples.