Logging levels, handlers, and formatters
coreintermediateThe stdlib `logging` module replaces `print("HERE")` with a system that has a severity level, can route output to multiple destinations (handlers), and can format each destination differently — all without touching the calling code.
Think of it as
A logger is a newsroom desk, not a printer. A story (log record) is written once, then the desk decides where it goes: the wire (console handler), the morning edition (a file handler), or nowhere at all if it is too minor (filtered by level) — and each outlet can typeset the same story differently (its own formatter).
What we're doing: Show that a record must clear BOTH the logger's level and the handler's level to actually appear — the two-gate filter this concept's mental model describes.
- 4
- The logger itself accepts DEBUG and above — nothing is filtered here yet.
- 9
- The handler is stricter: WARNING and above only, so the DEBUG record never reaches the output.
WARNING orders: low stock
ERROR orders: payment failed
Why this works: The DEBUG call passes the logger's own level check but is then dropped by the handler's stricter WARNING level — a record needs to clear every gate in the chain, not just the logger's, to actually be written anywhere.
- logger.debug(...) — gate 1: logger.setLevel(DEBUG) — passes
- handler.setLevel(WARNING) — gate 2: DEBUG < WARNING — dropped here
- Formatter — only shapes text — never filters what reaches it
- Output — only WARNING and ERROR records actually appear
Calling logging.basicConfig() more than once and expecting it to reconfigure
Wrong
Better
What you see: DEBUG messages still do not appear after the second basicConfig() call — no error, no warning, just silence.
Why: basicConfig() is a no-op if the root logger already has handlers configured (from an earlier basicConfig() call, or a library that added one) — it is designed to configure logging ONCE. Pass force=True to intentionally replace an existing configuration.
The five standard levels
Remember: A record needs to clear the logger's level AND the handler's level — a formatter only changes how it looks, never whether it appears.
See also: structured and json logging · logging exceptions

