Behavior And Guarantees
Logger and manager concurrency
Logger.add_handler(), Logger.remove_handler(), and logger dispatch now take snapshots of handler state before iterating. LoggerManager also serializes logger creation and default-root configuration so first-use races don't produce duplicate loggers or duplicate default handlers.
Reserved formatter fields
Core record fields are protected:
timestamplevellogger_namemessage
If user-supplied extra data reuses one of these keys, the field is renamed with an extra_ prefix in formatter output.
Examples:
{"message": "shadow"}becomesextra_message{"level": "DEBUG"}becomesextra_level
This preserves the original record shape and avoids silent overwrites.
Optional Rich support
RichHandler lives behind the molcrafts-mollog[rich] extra. Importing mollog does not require rich; the optional dependency is loaded only when RichHandler is accessed.
Exception logging
Records can now carry two additional diagnostic fields:
exceptionstack_info
logger.exception("...") is a shortcut for logger.error("...", exc_info=True).
All built-in formatters and handlers understand these fields:
TextFormatterappends traceback and stack blocks after the main log lineJSONFormatteremitsexceptionandstack_infokeysRichHandlerprints the main record first, then the traceback and stack output
Context-local metadata
Context fields are stored with contextvars, so they follow the current execution context rather than using a mutable process-global dictionary.
Per-record merge precedence is:
- context-local fields
- bound logger fields
- per-call extra fields
This keeps request-scoped metadata implicit while still allowing explicit per-call overrides.
Queue listener shutdown
QueueListener.stop() now uses a short drain window after receiving its stop sentinel. That improves the common race where a small number of records arrive just after shutdown begins.
This is still not a substitute for coordinated shutdown. Production code should stop producers before stopping the listener.
Rotation semantics
Rotating handlers validate their configuration early:
max_bytesmust be greater than0intervalmust be greater than0backup_countmust be greater than or equal to0
When backup_count=0, rotation discards the old segment and starts a fresh file instead of creating archives.