Named loggers tell you where each message came from and let you tune modules independently.
Calling logging.info(...) uses the root logger. Real code creates a logger per module:
log = logging.getLogger(__name__)getLogger returns the same object for the same name every time, so module-level creation is cheap and safe. Using __name__ gives names like myapp.db.models, which mirror your package structure. That matters because:
%(name)sin the format shows which module produced each line,- you can raise or lower the level of one subsystem:
logging.getLogger("myapp.db").setLevel(logging.DEBUG), - third-party libraries do the same, so you can quiet them:
logging.getLogger("urllib3").setLevel(logging.WARNING).
Logger methods mirror the module functions: log.debug, log.info, log.warning, log.error, log.critical, log.exception, plus log.log(level, msg).
Don't create loggers with dynamic names per request or per object; loggers live forever in a global registry. And never instantiate logging.Logger(...) directly; always go through getLogger so the logger is attached to the hierarchy.
Example
import logging, sys
logging.basicConfig(level=logging.INFO, stream=sys.stdout,
format="%(name)s | %(levelname)s | %(message)s")
db_log = logging.getLogger("myapp.db")
api_log = logging.getLogger("myapp.api")
print(db_log is logging.getLogger("myapp.db")) # True: same object
db_log.info("connected")
api_log.info("listening")
logging.getLogger("myapp.db").setLevel(logging.WARNING) # quiet the db
db_log.info("query took 3 ms") # suppressed
api_log.info("GET /users")
# myapp.db | INFO | connected
# myapp.api | INFO | listening
# myapp.api | INFO | GET /usersRemember
- log = logging.getLogger(__name__) at the top of each module
- Same name, same logger object
- Names form a dotted hierarchy you can tune per subsystem
Watch out for
- Logging through the root logger everywhere, losing the module name and per-module control.