Observability API Reference¶
Structured logging, rich console output, and tracing utilities.
Overview¶
The dspu.observability module provides:
- Structured Logging: Context-based logging with ContextVar
- Rich Console Output: Colors, tables, trees, progress bars
- Tracing Decorators:
@timed,@traced,@logged_errors - Stream Capture: Redirect stdout/stderr to logger
- Configuration: JSON/YAML/HOCON config file support
Logging¶
Configuration¶
dspu.observability.logging.configure_logging
¶
configure_logging(
level: str | int = "INFO",
format: str = "console",
outputs: list[str] | None = None,
show_time: bool = True,
show_name: bool = True,
show_level: bool = True,
) -> None
Configure logging with sensible defaults.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
level
|
str | int
|
Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL). |
'INFO'
|
format
|
str
|
Output format ("console", "json", "rich"). |
'console'
|
outputs
|
list[str] | None
|
List of outputs (e.g., ["stdout", "file:app.log"]). |
None
|
show_time
|
bool
|
Show timestamp in logs. |
True
|
show_name
|
bool
|
Show logger name in logs. |
True
|
show_level
|
bool
|
Show log level in logs. |
True
|
Example
configure_logging(level="INFO", format="json") configure_logging(level="DEBUG", format="rich", outputs=["stdout", "file:app.log"])
StructuredLogger¶
dspu.observability.logging.get_logger
¶
get_logger(name: str | None = None) -> StructuredLogger
Get a structured logger instance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str | None
|
Logger name (typically name). If None, returns root logger. |
None
|
Returns:
| Type | Description |
|---|---|
StructuredLogger
|
StructuredLogger instance. |
Example
logger = get_logger(name) logger.info("Application started", version="1.0.0")
dspu.observability.logging.StructuredLogger
¶
Bases: LoggerAdapter
Logger adapter that adds structured context to log records.
Example
logger = StructuredLogger(logging.getLogger(name)) logger.info("User login", user_id=123, ip="192.168.1.1")
Context Management¶
dspu.observability.logging.LogContext
¶
Context manager for adding temporary log context.
Example
with LogContext(request_id="req-123", user_id=456): ... logger.info("Processing") # Includes request_id and user_id
Initialize log context.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**context
|
Any
|
Context fields to add. |
{}
|
dspu.observability.logging.bind_context
¶
Add persistent context fields to all subsequent logs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**context
|
Any
|
Context fields to add. |
{}
|
Example
bind_context(service="api", version="1.0.0") logger.info("Started") # Includes service and version
Source code in src/dspu/observability/logging.py
dspu.observability.logging.unbind_context
¶
Remove context fields.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*keys
|
str
|
Context field keys to remove. |
()
|
Example
unbind_context("request_id", "user_id")
Source code in src/dspu/observability/logging.py
dspu.observability.logging.clear_context
¶
Decorators¶
Timing¶
dspu.observability.decorators.timed
¶
timed(
logger: Logger | str | None = None,
level: int = INFO,
threshold_ms: float | None = None,
) -> Callable[[F], F]
Decorator to log function execution time.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logger
|
Logger | str | None
|
Logger instance or name. If None, uses function's module logger. |
None
|
level
|
int
|
Log level (default: INFO). |
INFO
|
threshold_ms
|
float | None
|
Only log if execution time exceeds this threshold (milliseconds). |
None
|
Returns:
| Type | Description |
|---|---|
Callable[[F], F]
|
Decorated function. |
Example
@timed() ... def slow_function(): ... time.sleep(1) ... return "done" slow_function() # Logs: "slow_function took 1000.5ms"
@timed(threshold_ms=100) ... def fast_function(): ... return "quick" fast_function() # Not logged (below threshold)
Tracing¶
dspu.observability.decorators.traced
¶
traced(
logger: Logger | str | None = None,
level: int = DEBUG,
log_args: bool = True,
log_result: bool = False,
) -> Callable[[F], F]
Decorator to trace function entry/exit and log arguments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logger
|
Logger | str | None
|
Logger instance or name. If None, uses function's module logger. |
None
|
level
|
int
|
Log level (default: DEBUG). |
DEBUG
|
log_args
|
bool
|
If True, log function arguments. |
True
|
log_result
|
bool
|
If True, log function return value. |
False
|
Returns:
| Type | Description |
|---|---|
Callable[[F], F]
|
Decorated function. |
Error Logging¶
dspu.observability.decorators.logged_errors
¶
logged_errors(
logger: Logger | str | None = None,
level: int = ERROR,
reraise: bool = True,
include_traceback: bool = True,
) -> Callable[[F], F]
Decorator to automatically log exceptions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logger
|
Logger | str | None
|
Logger instance or name. If None, uses function's module logger. |
None
|
level
|
int
|
Log level for errors (default: ERROR). |
ERROR
|
reraise
|
bool
|
If True, re-raise exception after logging (default: True). |
True
|
include_traceback
|
bool
|
If True, include full traceback in log. |
True
|
Returns:
| Type | Description |
|---|---|
Callable[[F], F]
|
Decorated function. |
Example
@logged_errors() ... def risky_operation(): ... raise ValueError("Something went wrong") risky_operation() # Logs error with traceback, then raises
@logged_errors(reraise=False) ... def safe_operation(): ... raise ValueError("Handled") ... return None safe_operation() # Logs error but doesn't raise
Rich Output¶
Console¶
dspu.observability.rich_output.get_console
¶
Get rich console instance.
Returns:
| Type | Description |
|---|---|
Any
|
Rich Console instance. |
Raises:
| Type | Description |
|---|---|
ConfigurationError
|
If rich is not installed. |
Example
console = get_console() console.print("[bold green]Success![/bold green]")
Source code in src/dspu/observability/rich_output.py
dspu.observability.rich_output.print_json
¶
Pretty-print JSON data with syntax highlighting.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
Any
|
Data to print (will be JSON-serialized). |
required |
**kwargs
|
Any
|
Additional arguments for rich JSON printing. |
{}
|
Example
print_json({"user": "alice", "items": [1, 2, 3]})
Source code in src/dspu/observability/rich_output.py
dspu.observability.rich_output.print_table
¶
Display tabular data in a formatted table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
list[dict[str, Any]]
|
List of dictionaries to display. |
required |
title
|
str | None
|
Optional table title. |
None
|
**kwargs
|
Any
|
Additional arguments for rich Table. |
{}
|
Example
data = [ ... {"name": "Alice", "age": 30}, ... {"name": "Bob", "age": 25}, ... ] print_table(data, title="Users")
Source code in src/dspu/observability/rich_output.py
dspu.observability.rich_output.print_tree
¶
Display hierarchical data as a tree.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict[str, Any]
|
Nested dictionary to display. |
required |
title
|
str
|
Tree title. |
'Data'
|
Example
data = { ... "root": { ... "child1": {"leaf1": "value1"}, ... "child2": {"leaf2": "value2"}, ... } ... } print_tree(data)
Source code in src/dspu/observability/rich_output.py
dspu.observability.rich_output.print_traceback
¶
Print enhanced traceback for current exception.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
show_locals
|
bool
|
Show local variables in traceback. |
False
|
max_frames
|
int
|
Maximum number of stack frames to show. |
20
|
**kwargs
|
Any
|
Additional arguments for rich Traceback. |
{}
|
Example
try: ... risky_operation() ... except Exception: ... print_traceback(show_locals=True)
Source code in src/dspu/observability/rich_output.py
dspu.observability.rich_output.inspect_object
¶
Rich inspection of an object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
obj
|
Any
|
Object to inspect. |
required |
methods
|
bool
|
Show methods. |
False
|
private
|
bool
|
Show private attributes. |
False
|
**kwargs
|
Any
|
Additional arguments for rich inspect. |
{}
|
Example
inspect_object(my_object, methods=True)
Source code in src/dspu/observability/rich_output.py
Progress¶
dspu.observability.rich_output.Progress
¶
Progress bar context manager.
Example
with Progress() as progress: ... task = progress.add_task("Processing", total=1000) ... for i in range(1000): ... process_item(i) ... progress.update(task, advance=1)
Initialize progress.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Any
|
Arguments for rich Progress. |
{}
|
Source code in src/dspu/observability/rich_output.py
dspu.observability.rich_output.TaskProgress
¶
Multi-task progress tracking.
Example
with TaskProgress() as progress: ... download = progress.add_task("Downloading", total=100) ... extract = progress.add_task("Extracting", total=100) ... for i in range(100): ... progress.update(download, advance=1)
Initialize task progress.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Any
|
Arguments for rich Progress. |
{}
|
Source code in src/dspu/observability/rich_output.py
Advanced¶
LoggingSetup¶
dspu.observability.setup.LoggingSetup
¶
LoggingSetup(
default_log_config_dir: str = ".",
default_log_config_file: str = "logging.json",
default_log_level: str = "DEBUG",
log_config_env: str | None = None,
log_level_env: str | None = None,
log_file: str | None = None,
logging_config: dict[str, Any] | str | None = None,
source_anchor: Any = None,
default_logger_name: str | None = None,
)
Configure Python logging from files, dictionaries, or environment variables.
Provides flexible logging configuration with support for: - Configuration from JSON, YAML, or HOCON files - Environment variable overrides - Dictionary configuration - Relative path resolution - Stdout/stderr redirection
Example
setup = LoggingSetup( ... default_log_config_file="logging.json", ... default_log_level="INFO", ... log_config_env="LOG_CONFIG", ... log_level_env="LOG_LEVEL", ... ) setup.setup()
Initialize logging setup.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
default_log_config_dir
|
str
|
Directory where logging config file lives. |
'.'
|
default_log_config_file
|
str
|
Default name of logging config file. |
'logging.json'
|
default_log_level
|
str
|
Default log level if no config file found. |
'DEBUG'
|
log_config_env
|
str | None
|
Environment variable for config file override. |
None
|
log_level_env
|
str | None
|
Environment variable for log level override. |
None
|
log_file
|
str | None
|
Actual file to log to (overrides config). |
None
|
logging_config
|
dict[str, Any] | str | None
|
Config reference (file path or dictionary). |
None
|
source_anchor
|
Any
|
Object whose source file is anchor for relative paths. |
None
|
default_logger_name
|
str | None
|
Logger name for default setup. |
None
|
Source code in src/dspu/observability/setup.py
Functions¶
setup
¶
Set up logging per the configured parameters.
Configures Python logging based on: 1. Explicit logging_config (if provided) 2. Config file path from environment variable or defaults 3. Basic config with default log level
Raises:
| Type | Description |
|---|---|
ConfigurationError
|
If config file cannot be loaded. |
Source code in src/dspu/observability/setup.py
setup_with_diversion
¶
Set up logging and divert stdout/stderr to logger.
Returns:
| Type | Description |
|---|---|
Logger
|
Logger instance used for diversion. |
Example
logger = setup.setup_with_diversion() print("This goes to the logger")
Source code in src/dspu/observability/setup.py
Stream Capture¶
dspu.observability.stream_capture.StreamToLogger
¶
Bases: StringIO
File-like stream that redirects writes to a logger.
Redirects stdout/stderr to a logger instance, useful for capturing print statements in logging output. Includes protection against infinite recursion when loggers output to stdout.
Example
logger = logging.getLogger(name) StreamToLogger.subvert(logger, reroute_stdout=True) print("This goes to the logger")
Initialize stream-to-logger adapter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logger
|
Logger
|
Logger to redirect writes to. |
required |
log_level
|
int
|
Logging level for writes (default: INFO). |
INFO
|
Source code in src/dspu/observability/stream_capture.py
Functions¶
subvert
classmethod
¶
subvert(
logger: Logger | None = None,
reroute_stdout: bool = True,
reroute_stderr: bool = True,
) -> None
Subvert stdout and stderr to logger.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logger
|
Logger | None
|
Logger to use for subverting stdout/stderr. If None, creates new logger from class name. |
None
|
reroute_stdout
|
bool
|
If True, reroute stdout to logger. |
True
|
reroute_stderr
|
bool
|
If True, reroute stderr to logger. |
True
|
Example
logger = logging.getLogger(name) StreamToLogger.subvert(logger, reroute_stdout=True) print("This goes to the logger at INFO level")
Source code in src/dspu/observability/stream_capture.py
restore
classmethod
¶
Restore stdout and stderr to their original state.
Example
StreamToLogger.restore() print("This goes to normal stdout")
Source code in src/dspu/observability/stream_capture.py
Usage Examples¶
Basic Logging¶
from dspu.observability import configure_logging, get_logger, LogContext
# Configure
configure_logging(level="INFO", format="rich")
logger = get_logger(__name__)
# Log with structured fields
logger.info("User logged in", user_id=123, ip="192.168.1.1")
# Context manager
with LogContext(request_id="req-123"):
logger.info("Processing request") # Includes request_id
logger.info("Query executed", duration_ms=45)
Tracing Decorators¶
from dspu.observability import timed, traced, logged_errors
@timed()
def slow_operation():
# Execution time automatically logged
time.sleep(1)
@traced(log_args=True, log_result=True)
def process_order(order_id, items):
# Logs entry, args, result, and exit
return {"status": "success", "order_id": order_id}
@logged_errors(reraise=True)
def risky_operation():
# Logs errors with traceback
raise ValueError("Something went wrong")
Rich Output¶
from dspu.observability import print_json, print_table, Progress
# Pretty JSON
data = {"user": {"name": "Alice", "age": 30}}
print_json(data)
# Table
users = [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]
print_table(users, title="Users")
# Progress bar
with Progress() as progress:
task = progress.add_task("Processing", total=100)
for i in range(100):
time.sleep(0.01)
progress.update(task, advance=1)
See Also¶
- Observability Examples - Practical examples
- Observability User Guide - Detailed guide
- Quick Start - Get started quickly