The stdlib logging module is configured once, at the application entry point, usually with dictConfig. Libraries obtain a module-level logger and add a NullHandler, leaving configuration to the application.
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler()) # library onlyStructured records are emitted as fields rather than formatted strings, which makes them queryable in log aggregation systems:
log.info("rows_processed", symbol=symbol, rows=n, duration_s=elapsed)| Library | Notes |
|---|---|
logging |
Stdlib. Handlers, filters, hierarchical loggers, dictConfig. |
| structlog | Structured logging with processor chains; integrates with stdlib logging. |
| Loguru | Single-object API, simple setup. Configures global state, so it is generally avoided in libraries. |
| OpenTelemetry | Traces, metrics and logs with context propagation across services; vendor-neutral exporters. |
| Logfire | Observability platform from the Pydantic team, built on OpenTelemetry. |
For services, traces are usually more informative than logs; log records carry the trace and span IDs so the two can be correlated.