Python Logging Multiple Handlers: Console + File Done Right
Set up Python logging with multiple handlers — console and file at once — plus levels, formatters, rotation, JSON output, and the gotchas that cause duplicates.
Every Python developer starts with print() . It works right up until you need to know *when* something happened, silence the noise in production, or find yesterday's error after the terminal closed. That's when you reach for the logging module — and the single most useful pattern in it is Python logging with multiple handlers : one logger writing to the console *and* a file at the same time, each with its own format and level. This guide builds that setup from scratch, then covers the production patterns and the two gotchas that generate half of all logging questions. What you'll learn logging vs print — what you actually gain Named loggers with getLogger(__name__) (not the root logger) Log levels and how filtering works The core pattern: multiple handlers — console + file, different formats RotatingFileHandler so logs don't eat your disk Logging exceptions with full tracebacks Production pattern: JSON logs to stdout for Docker The classic gotchas: duplicate messages, basicConfig not working Step 1 — Logging vs print print writes to stdout and that's it. logging adds three things print can't give you: levels (so you can silence debug noise without deleting code), destinations (console, file, both), and timestamps/context automatically attached to every message: import logging # The 10-second version. Fine for scripts, not for real apps (see Step 4). logging.basicConfig(level=logging.DEBUG, format="%(levelname)s: %(message)s") logging.debug("connecting to db...") # visible: level is DEBUG logging.info("server started on :8000") logging.warning("disk 80% full") logging.error("payment failed for order 42") Levels, lowest to highest: DEBUG < INFO < WARNING < ERROR < CRITICAL . The logger's level is a filter — set it to WARNING and…