Home/Learn/Python A–Z/Logging — Beyond print()

Logging — Beyond print()

Intermediate
Stdlib Power Tools

The logging module gives leveled, timestamped, routable logs — DEBUG/INFO/WARNING/ERROR/CRITICAL — configured once and used via module-level loggers; print() is for humans, logging is for systems.

Overview

print() debugging dies the moment code ships: no timestamps, no severity, no way to silence or route output. logging fixes all three: levels filter noise (DEBUG in development, INFO+ in production), formatters add time/module/line automatically, handlers route to console, files or aggregators. The professional pattern is two lines: logger = logging.getLogger(__name__) in every module, one basicConfig (or dictConfig) at the entry point. exception() inside except blocks captures full tracebacks — the habit that makes 2 AM debugging survivable.

Levels, Configuration & Module Loggers

Configure once at startup; get a named logger per module. Level ordering: DEBUG < INFO < WARNING < ERROR < CRITICAL — the configured level is the minimum that gets through.

Configure once, log everywhere with levels
import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)-8s %(name)s: %(message)s",
    datefmt="%H:%M:%S",
)

logger = logging.getLogger(__name__)      # per-module logger

logger.debug("cache warm-up details")     # hidden (below INFO)
logger.info("server started on :8000")
logger.warning("disk 85% full")
logger.error("payment gateway timed out")

# 14:02:11 INFO     __main__: server started on :8000
# 14:02:11 WARNING  __main__: disk 85% full
# 14:02:11 ERROR    __main__: payment gateway timed out

# Lazy formatting — string built ONLY if the level passes
user_id, ms = "asha", 42
logger.info("user %s served in %d ms", user_id, ms)   # preferred
# vs logger.info(f"user {user_id}...")  — f-string always evaluates

Tracebacks & Log Files

logger.exception() inside an except block logs the message PLUS the full traceback. FileHandler (or RotatingFileHandler) persists logs; a logger can feed console and file simultaneously.

exception() = message + traceback; handlers route output
import logging
from logging.handlers import RotatingFileHandler

logger = logging.getLogger("orders")
logger.setLevel(logging.INFO)

# Console + rotating file (1 MB x 3 backups)
console = logging.StreamHandler()
filelog = RotatingFileHandler("orders.log", maxBytes=1_000_000, backupCount=3)
fmt = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
console.setFormatter(fmt); filelog.setFormatter(fmt)
logger.addHandler(console); logger.addHandler(filelog)

def place_order(payload):
    try:
        amount = payload["amount"] / payload["qty"]
    except (KeyError, ZeroDivisionError):
        logger.exception("order failed: %s", payload)   # + traceback!
        raise

try:
    place_order({"amount": 500, "qty": 0})
except Exception:
    pass
# orders.log now contains the message AND the full stack trace

Key Points to Remember

  • 1getLogger(__name__) per module; configure levels/handlers once at the entry point
  • 2DEBUG for dev detail, INFO for lifecycle, WARNING for smells, ERROR/CRITICAL for failures
  • 3logger.exception() inside except logs the traceback automatically
  • 4Use %-style lazy args in log calls; f-strings evaluate even when filtered out

Interview Questions

Sign in to ask Aria
1

Why is logging preferred over print in production code — give three concrete reasons.

EasyWipro
2

What extra does logger.exception() capture vs logger.error()?

MediumThoughtworks

Ask Aria about Logging — Beyond print()

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…