Real apps log to the console and files or services at once, with different detail for each.
A logger decides whether a message is logged; handlers decide where it goes, and formatters decide how it looks. One logger can have many handlers.
Common handlers:
StreamHandler(sys.stderr): console.FileHandler("app.log"): a file.RotatingFileHandler(path, maxBytes=..., backupCount=...)andTimedRotatingFileHandler(path, when="midnight")inlogging.handlers: files that rotate by size or time.SysLogHandler,SMTPHandler,HTTPHandler,QueueHandler: other destinations.
Each handler has its own level, so the logger can let DEBUG through while the console handler shows only INFO and a file handler keeps everything. A record must pass the logger's level, then each handler's level, independently.
Wire it up by hand:
h = logging.FileHandler("app.log", encoding="utf-8")
h.setLevel(logging.DEBUG)
h.setFormatter(logging.Formatter("%(asctime)s %(name)s %(message)s"))
logger.addHandler(h)basicConfig is just a shortcut that does this for the root logger. Attach handlers in one place at startup, usually to the root logger, not in every module, or you'll get duplicate lines.
Example
import logging, sys
log = logging.getLogger("app")
log.setLevel(logging.DEBUG) # logger lets everything through
console = logging.StreamHandler(sys.stdout)
console.setLevel(logging.INFO)
console.setFormatter(logging.Formatter("console: %(levelname)s %(message)s"))
file = logging.FileHandler("app.log", mode="w", encoding="utf-8")
file.setFormatter(logging.Formatter("%(levelname)s:%(name)s:%(message)s"))
log.addHandler(console)
log.addHandler(file)
log.debug("cache miss for key=42")
log.info("request done")
print(open("app.log").read())
# console: INFO request done
# DEBUG:app:cache miss for key=42
# INFO:app:request doneRemember
- Loggers filter, handlers route, formatters render
- Each handler has its own level and formatter
- Rotating handlers in logging.handlers keep log files bounded
Watch out for
- Calling addHandler inside a function that runs many times, multiplying every log line.