I spent years adding print() statements everywhere and wondering why nothing made sense in production. Python's logging module is one of those things that looks simple until you actually need it, and then it's a mess of handlers, formatters, and log levels that nobody configured properly.
Here is how I set up logging in every Python project now ( and why I stopped fighting it ).
The Problem with Print
Every Python developer starts with print(). It works, it's obvious, and it disappears the moment you deploy. No timestamps, no levels, no rotation, no filtering. Just text on stdout that nobody reads.
Then you discover logging and it has five hundred ways to configure the same thing. The docs are thorough but somehow unhelpful. So here is what actually matters.
Basic Setup ( Five Lines That Cover 90% of Cases )
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
log = logging.getLogger(__name__)
log.info("Server started")That is it. Five lines. basicConfig sets up the root logger, and every module after this gets a child logger with __name__ as the name. You get timestamps, levels, and the module name in every message.
Stop there if your project is small. Seriously. Everything after this is for when you need file output, rotation, or multiple services writing to different places.
File Output with Rotation
If you are running anything in production, you need log files. And if you have log files, you need rotation or they will eat your disk in a week.
import logging
from logging.handlers import RotatingFileHandler
log = logging.getLogger(__name__)
log.setLevel(logging.INFO)
handler = RotatingFileHandler(
"app.log",
maxBytes=5_000_000, # 5 MB
backupCount=3,
)
handler.setFormatter(logging.Formatter(
"%(asctime)s [%(levelname)s] %(name)s: %(message)s"
))
log.addHandler(handler)
log.info("This goes to the file, not stdout")Now you get app.log that rotates at 5 MB and keeps 3 backups. No logrotate, no cron, no external tools. Just Python.
Structured Logging for Actual Services
If you are running an API or a service that someone else needs to debug, structured logging ( JSON ) is the only sane choice. Grep is not a log analysis tool.
import logging
import json
class JSONFormatter(logging.Formatter):
def format(self, record):
log_entry = {
"timestamp": self.formatTime(record, self.datefmt),
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
}
if record.exc_info:
log_entry["exception"] = self.formatException(record.exc_info)
return json.dumps(log_entry)
handler = logging.StreamHandler()
handler.setFormatter(JSONFormatter())
log = logging.getLogger("api")
log.addHandler(handler)
log.setLevel(logging.INFO)
log.info("User logged in", extra={"user_id": 42, "ip": "192.168.1.1"})Every log line is valid JSON. Ship it to Elasticsearch, Loki, whatever. No regex parsing, no guesswork.