Python’s built-in logging module lets you record messages from your code. With proper configuration, you can:
logger.info()).logging.info() etc.basicConfig() – quick way to configure simple logging.dictConfig() – advanced configuration using dictionaries/JSON/YAML.getLogger(__name__) in each file.Typical logging setup for a small script:
logging.logging.basicConfig(...) once at the start of your program.logging.getLogger(__name__).logger.debug(), logger.info(), etc.For bigger projects, you usually:
logging.config.dictConfig().Configure logging once to print simple messages to the console.
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
logging.debug("This is a debug message") # Will NOT show (level is INFO)
logging.info("Application started")
logging.warning("Low disk space")
logging.error("An error occurred")
Send logs to a file instead of (or in addition to) the console.
import logging
logging.basicConfig(
filename="app.log",
filemode="a", # append to file
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
logger.debug("Debug details for troubleshooting")
logger.info("App started")
logger.error("Something went wrong")
Create your own logger with different handlers and formats.
import logging
logger = logging.getLogger("myapp")
logger.setLevel(logging.DEBUG)
# Console handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# File handler
file_handler = logging.FileHandler("myapp.log")
file_handler.setLevel(logging.DEBUG)
# Formatter
formatter = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# Attach handlers
logger.addHandler(console_handler)
logger.addHandler(file_handler)
logger.info("This will go to console and file")
logger.debug("This will go only to file (not console)")
Use a dictionary for complex setups (easily convertable from JSON/YAML).
import logging
import logging.config
# Centralized logging configuration dictionary
LOGGING_CONFIG = {
"version": 1,
"formatters": {
"standard": {
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "INFO",
"formatter": "standard",
"stream": "ext://sys.stdout",
},
"file": {
"class": "logging.FileHandler",
"level": "DEBUG",
"formatter": "standard",
"filename": "service.log",
"mode": "a",
},
},
"loggers": {
"myservice": {
"level": "DEBUG",
"handlers": ["console", "file"],
"propagate": False,
}
},
}
# Apply configuration and get a logger for this service
logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger("myservice")
# Application log messages
logger.info("Service started")
logger.debug("Debugging information")
2025-09-11 14:30:00,123 - INFO - Application started2025-09-11 14:30:00,124 - WARNING - Low disk spaceapp.log instead of the console.logger.info() appears in both console and myapp.log.logger.debug() appears only in myapp.log (console level is INFO).service.log receive logs with the same format, and you can adjust behavior just by changing the configuration dictionary.Proper logging configuration makes it easy to diagnose issues in development and production without changing lots of code.
DEBUG logs temporarily.logger = logging.getLogger(__name__) in each module for clear log origins.main.py).basicConfig() in reusable libraries; let the main app configure logging.DEBUG for developers, INFO for normal events, WARNING/ERROR/CRITICAL for problems.dictConfig() or external config files for bigger projects so you can change logging without editing code.basicConfig() example to include the module name using %(name)s in the format.LOGGING_CONFIG dictionary and apply it with logging.config.dictConfig() for a pretend web service.db.log.INFO to DEBUG) and see how the output changes.