logging
The logging module is Python’s built-in solution for application logging. It provides a flexible, hierarchical framework for emitting log messages from Python programs. Unlike simple print statements, logging lets you control message severity, route messages to different destinations, and configure output format without modifying your application code.
Syntax
import logging
# Get a logger for the current module
logger = logging.getLogger(__name__)
# Configure basic logging
logging.basicConfig(level=logging.INFO)
# Emit log messages
logger.debug("Detailed diagnostic info")
logger.info("Normal operation message")
logger.warning("Something unexpected happened")
logger.error("A serious problem occurred")
logger.critical("Program cannot continue")
Key Functions
The module provides module-level functions that operate on the root logger:
| Function | Description |
|---|---|
logging.getLogger(name) | Returns a logger instance; same name returns same instance |
logging.basicConfig(**kwargs) | Configures the root logger with handlers, level, format |
logging.debug(msg, *args, **kwargs) | Log a DEBUG message |
logging.info(msg, *args, **kwargs) | Log an INFO message |
logging.warning(msg, *args, **kwargs) | Log a WARNING message |
logging.error(msg, *args, **kwargs) | Log an ERROR message |
logging.critical(msg, *args, **kwargs) | Log a CRITICAL message |
logging.exception(msg, *args, **kwargs) | Log an ERROR with exception traceback |
logging.disable(level) | Disable all logging at or below a given level |
basicConfig Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
level | int | NOTSET | Minimum level to log |
filename | str | None | File to log to (if None, logs to console) |
filemode | str | 'a' | File open mode (‘a’ or ‘w’) |
format | str | %(levelname)s:%(name)s:%(message)s | Log message format |
datefmt | str | None | Date format string |
style | str | '%' | Format style (’%’, ’{’, ’$‘) |
handlers | list | None | List of handlers to attach |
Logger Objects
Loggers are the primary interface for application code. Always obtain them via getLogger() rather than instantiating directly.
Logger Methods
| Method | Description |
|---|---|
logger.setLevel(level) | Set the minimum logging level |
logger.getEffectiveLevel() | Get the effective level (walks up hierarchy if NOTSET) |
logger.isEnabledFor(level) | Check if a level would be processed |
logger.debug(msg, ...) | Log at DEBUG level |
logger.info(msg, ...) | Log at INFO level |
logger.warning(msg, ...) | Log at WARNING level |
logger.error(msg, ...) | Log at ERROR level |
logger.critical(msg, ...) | Log at CRITICAL level |
logger.exception(msg, ...) | Log at ERROR with traceback |
logger.log(level, msg, ...) | Log at a specific level |
logger.addHandler(hdlr) | Attach a handler to this logger |
logger.removeHandler(hdlr) | Remove a handler |
logger.addFilter(filter) | Add a filter object |
logger.hasHandlers() | Check if any handlers are configured |
Logger Attributes
| Attribute | Description |
|---|---|
logger.name | The logger’s name (passed to getLogger) |
logger.level | The threshold level set on this logger |
logger.parent | Parent logger in the hierarchy |
logger.propagate | If True, messages pass to parent handlers (default True) |
logger.handlers | List of handlers attached to this logger |
Log Levels
Logging levels are numeric values that determine message severity. The module defines these standard levels:
| Level | Value | When to Use |
|---|---|---|
NOTSET | 0 | Inherit from parent (default for loggers) |
DEBUG | 10 | Detailed diagnostic information |
INFO | 20 | Confirmation that things work as expected |
WARNING | 30 | Something unexpected, but the program continues |
ERROR | 40 | A serious problem prevented a function from working |
CRITICAL | 50 | The program itself may be unable to continue |
Messages below the configured threshold are ignored. The root logger defaults to WARNING, while child loggers default to NOTSET (inheriting their parent’s level).
Handlers
Handlers determine where log messages go. The module provides several built-in handler types:
| Handler | Description |
|---|---|
StreamHandler | Logs to a stream (default: stderr) |
FileHandler | Logs to a file |
RotatingFileHandler | Logs to a file with rotation |
TimedRotatingFileHandler | Logs to a file rotated at intervals |
SocketHandler | Logs to a network socket |
DatagramHandler | Logs to a UDP socket |
SysLogHandler | Logs to a Unix syslog |
HTTPHandler | Logs to an HTTP server |
Handler methods include setLevel(), setFormatter(), addFilter(), removeFilter(), and emit().
Examples
Basic Configuration with basicConfig
import logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)
logger.debug("Debug message - detailed info for troubleshooting")
logger.info("Info message - normal operation")
logger.warning("Warning message - something looks off")
logger.error("Error message - something failed")
logger.critical("Critical message - system may crash")
Output:
2026-03-09 10:30:45 - __main__ - DEBUG - Debug message - detailed info for troubleshooting
2026-03-09 10:30:45 - __main__ - INFO - Info message - normal operation
2026-03-09 10:30:45 - __main__ - WARNING - Warning message - something looks off
2026-03-09 10:30:45 - __main__ - ERROR - Error message - something failed
2026-03-09 10:30:45 - __main__ - CRITICAL - Critical message - system may crash
Logger Hierarchy and Propagation
import logging
# Configure root logger
logging.basicConfig(level=logging.INFO, format='%(name)s - %(levelname)s - %(message)s')
# Create child loggers
app_logger = logging.getLogger('myapp')
db_logger = logging.getLogger('myapp.database')
api_logger = logging.getLogger('myapp.api')
# Set different levels for different components
app_logger.setLevel(logging.WARNING)
db_logger.setLevel(logging.DEBUG)
api_logger.setLevel(logging.INFO)
# Emit messages at various levels
app_logger.warning("App warning - config file missing")
db_logger.debug("Database: Executing query")
db_logger.info("Database: Connection established")
api_logger.info("API: Request received")
api_logger.error("API: Request failed")
Output:
myapp - WARNING - App warning - config file missing
myapp.database - DEBUG - Database: Executing query
myapp.database - INFO - Database: Connection established
myapp.api - INFO - API: Request received
myapp.api - ERROR - API: Request failed
Custom Handlers and Formatters
import logging
# Create a custom logger
logger = logging.getLogger('myapp.worker')
logger.setLevel(logging.DEBUG)
# Create handlers
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)
# Create formatters
console_format = logging.Formatter('%(levelname)s: %(message)s')
file_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# Attach formatters to handlers
console_handler.setFormatter(console_format)
file_handler.setFormatter(file_format)
# Add handlers to logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# Log messages
logger.debug("Debug: Variable x = 42")
logger.info("Info: Task started")
logger.warning("Warning: Resource usage high")
Console output:
DEBUG: Debug: Variable x = 42
INFO: Info: Task started
WARNING: Warning: Resource usage high
File output (app.log):
2026-03-09 10:30:45 - myapp.worker - DEBUG - Debug: Variable x = 42
2026-03-09 10:30:45 - myapp.worker - INFO - Info: Task started
2026-03-09 10:30:45 - myapp.worker - WARNING - Warning: Resource usage high
Common Patterns
Per-Module Loggers
The recommended pattern is creating a logger per module using __name__:
# mymodule.py
import logging
logger = logging.getLogger(__name__)
def process_data(data):
logger.info("Processing started")
# ... processing logic
logger.info("Processing complete")
This creates a hierarchical logger structure matching your module hierarchy, allowing fine-grained control.
Disabling Logging Globally
You can disable all logging at or below a certain level:
import logging
# Disable all DEBUG and INFO messages
logging.disable(logging.WARNING)
logger = logging.getLogger('myapp')
logger.debug("This won't appear") # Ignored
logger.info("This won't appear") # Ignored
logger.warning("This will appear") # Shows
Exception Logging
Use logger.exception() to automatically include traceback information:
import logging
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.ERROR)
try:
result = 10 / 0
except ZeroDivisionError:
logger.exception("Division failed")
Output:
ERROR:__main__:Division failed
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
ZeroDivisionError: division by zero