stdlib logging cheatsheet. For structured logging, see Cheatsheet 08
.
Basic
import logging
logging.basicConfig(level=logging.INFO)
log = logging.getLogger(__name__)
log.debug("...")
log.info("...")
log.warning("...")
log.error("...")
log.critical("...")
log.exception("...") # logs + stack trace from current except block
Named logger per module
# In each module
log = logging.getLogger(__name__)
Logger names form a hierarchy: myapp.services.users is a child of myapp.services.
basicConfig (simple)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(name)s %(levelname)s %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
stream=sys.stdout,
)
dictConfig (production)
LOG_CONFIG = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"json": {
"()": "pythonjsonlogger.jsonlogger.JsonFormatter",
"format": "%(asctime)s %(name)s %(levelname)s %(message)s",
},
"console": {
"format": "%(asctime)s %(levelname)s %(name)s: %(message)s",
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "console" if not PROD else "json",
"stream": "ext://sys.stdout",
},
},
"loggers": {
"": { # root
"level": "INFO",
"handlers": ["console"],
},
"uvicorn": {"level": "WARNING"},
"sqlalchemy.engine": {"level": "WARNING"},
},
}
import logging.config
logging.config.dictConfig(LOG_CONFIG)
Handlers
# File
fh = logging.FileHandler("app.log")
log.addHandler(fh)
# Rotating
from logging.handlers import RotatingFileHandler
rh = RotatingFileHandler("app.log", maxBytes=10*1024*1024, backupCount=5)
# Timed rotating
from logging.handlers import TimedRotatingFileHandler
trh = TimedRotatingFileHandler("app.log", when="midnight", backupCount=7)
# Syslog
from logging.handlers import SysLogHandler
sh = SysLogHandler(address=("syslog.local", 514))
# SMTP (alert via email)
from logging.handlers import SMTPHandler
smtp = SMTPHandler(
mailhost="smtp.example.com",
fromaddr="[email protected]",
toaddrs=["[email protected]"],
subject="App alert",
)
smtp.setLevel(logging.CRITICAL)
For prod: stream to stdout; let your log shipper handle files / rotation / SMTP.
Formatters
fmt = logging.Formatter(
"%(asctime)s %(name)s %(levelname)s %(message)s",
datefmt="%Y-%m-%dT%H:%M:%S",
)
handler.setFormatter(fmt)
Available fields:
%(asctime)s
%(name)s
%(levelname)s
%(message)s
%(filename)s
%(lineno)d
%(funcName)s
%(thread)d
%(threadName)s
%(process)d
Custom fields (extra)
log.info("user_login", extra={"user_id": 42, "ip": "..."})
# In formatter:
"%(asctime)s %(message)s user_id=%(user_id)s ip=%(ip)s"
Doesn’t error if extras are missing? Use a custom Filter to add defaults.
Filters
class RequestIDFilter(logging.Filter):
def filter(self, record):
record.request_id = current_request_id.get() or "-"
return True
logger.addFilter(RequestIDFilter())
Levels
DEBUG (10)
INFO (20)
WARNING (30) (default for root)
ERROR (40)
CRITICAL (50)
Set per-logger:
logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING)
Log to stdout (Docker / K8s)
import sys
handler = logging.StreamHandler(sys.stdout)
Don’t log to files in containers; let the orchestrator collect stdout.
JSON logging (without structlog)
uv add python-json-logger
from pythonjsonlogger import jsonlogger
handler = logging.StreamHandler()
formatter = jsonlogger.JsonFormatter(
"%(asctime)s %(name)s %(levelname)s %(message)s"
)
handler.setFormatter(formatter)
logging.getLogger().addHandler(handler)
Suppress noisy libraries
logging.getLogger("urllib3.connectionpool").setLevel(logging.WARNING)
logging.getLogger("asyncio").setLevel(logging.WARNING)
logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING)
Capture warnings
logging.captureWarnings(True)
# Now Python warnings (deprecation, etc.) go through logging
Common mistakes
printfor logging — no level, no structure.f-stringin log message:log.info(f"user {user.id}")— formats even if not logged. Uselog.info("user %s", user.id).- Logging at DEBUG in production — high volume.
- PII in log messages.
Read this next
If you want my dictConfig + JSON + per-module setup, it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .