How can I configure Python's logging module to log to both a file and print to stdout?

How can I configure Python’s logging module to log to both a file and print to stdout?

I’m using Python’s logging module to log debug strings to a file, and that works great. Now, I want to log the same messages to the console (stdout) in addition to the file. Here’s the code I’m currently using to log to a file:

import logging
import logging.handlers

logger = logging.getLogger("")
logger.setLevel(logging.DEBUG)
handler = logging.handlers.RotatingFileHandler(
    LOGFILE, maxBytes=(1048576*5), backupCount=7
)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)

Then, I call a logger function like:

logger.debug("I am written to the file")

Can you help me with how to also log these messages to the console (stdout)?

To log messages to both a file and the console in Python, you can use the StreamHandler to output logs to stdout and a FileHandler to write logs to a file. Here’s a basic approach:

import logging
import sys

# Create the logger
logFormatter = logging.Formatter("%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s]  %(message)s")
rootLogger = logging.getLogger()

# FileHandler for logging to file
fileHandler = logging.FileHandler("your_logfile.log")
fileHandler.setFormatter(logFormatter)
rootLogger.addHandler(fileHandler)

# StreamHandler for logging to console
consoleHandler = logging.StreamHandler(sys.stdout)
consoleHandler.setFormatter(logFormatter)
rootLogger.addHandler(consoleHandler)

# Set logging level
rootLogger.setLevel(logging.DEBUG)

This will output logs both to the file and to stdout, with the messages formatted with the timestamp, thread name, and log level.

If you prefer a more concise approach without creating a custom logger, you can directly add handlers to the root logger like so:

import logging

# Add a stream handler to print logs to stdout
logging.getLogger().addHandler(logging.StreamHandler())

# Add a file handler to log messages to a file
logging.getLogger().addHandler(logging.FileHandler('your_logfile.log'))

# Set the logging level
logging.basicConfig(level=logging.DEBUG)

You can also configure logging using the logging.config module, which allows for a more flexible and structured configuration. Here’s an example:

import logging.config

logging.config.dictConfig({
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': 'your_logfile.log',
            'formatter': 'detailed'
        },
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'stream': 'ext://sys.stdout',
            'formatter': 'detailed'
        },
    },
    'formatters': {
        'detailed': {
            'format': '%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s]  %(message)s'
        },
    },
    'loggers': {
        '': {
            'handlers': ['file', 'console'],
            'level': 'DEBUG',
            'propagate': True,
        },
    }
})

This method gives you more control over the configuration and allows for easy modification or expansion in the future.