How do I change the format of a Python log message on a per-logger basis?
Asked Answered
O

3

75

After reading the documentation on logging, I know I can use code like this to perform simple logging:

import logging

def main():
    logging.basicConfig(filename="messages.log",
                        level=logging.WARNING,
                        format='%(filename)s: '    
                                '%(levelname)s: '
                                '%(funcName)s(): '
                                '%(lineno)d:\t'
                                '%(message)s')

    logging.debug("Only for debug purposes\n")
    logging.shutdown()

main()

However, I realised I don't know how to change the format of log messages on a per-logger basis, since basicConfig is a module-level function. This code works for creating different loggers with different levels, names, etc. but is there a way to change the format of those log messages on a per-logger basis as well, in a way similar to basicConfig?

import inspect
import logging

def function_logger(level=logging.DEBUG):
    function_name = inspect.stack()[1][3]
    logger = logging.getLogger(function_name)
    logger.setLevel(level)
    logger.addHandler(logging.FileHandler("{0}.log".format(function_name)))
    return logger

def f1():
    f1_logger = function_logger()
    f1_logger.debug("f1 Debug message")
    f1_logger.warning("f1 Warning message")
    f1_logger.critical("f1 Critical message")

def f2():
    f2_logger = function_logger(logging.WARNING)
    f2_logger.debug("f2 Debug message")
    f2_logger.warning("f2 Warning message")
    f2_logger.critical("f2 Critical message")

def main():
    f1()
    f2()
    logging.shutdown()

main()
Oberstone answered 20/7, 2012 at 14:55 Comment(2)
You just helped me to figure out how to add filename, function name, and line number to my logs. ThanksRefrain
@XanderYzWich See docs.python.org/3/library/logging.html#logrecord-attributes for the full list of available attributesUndertint
P
125

Try this

import logging

logger = logging.getLogger('simple_example')
logger.setLevel(logging.DEBUG)
# create file handler that logs debug and higher level messages
fh = logging.FileHandler('spam.log')
fh.setLevel(logging.DEBUG)
# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
# create formatter and add it to the handlers
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
fh.setFormatter(formatter)
# add the handlers to logger
logger.addHandler(ch)
logger.addHandler(fh)

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

See http://docs.python.org/howto/logging-cookbook.html#multiple-handlers-and-formatters for more information

Pilate answered 20/7, 2012 at 15:14 Comment(7)
+1, and +1 again (sadly no) for adding the code. I edited it into my previous answer to a logging question as well, and it works perfectly.Oberstone
I am always surprised it's not as simple as logger = logging.getLogger('mylogger') logger.basicConfig(level=..., format=...)...Valois
Only the logging module has the basicConfig functionActinoid
@Actinoid yes, that's the problem/criticism here.Cogitative
@Valois On the other hand, by setting the format on each handler, you can make one format for stdout and another for the file log. But it would be nice to simplify the way we set the format for formatting as well. Is it really necessary for us to create a Formatter object? Shouldn't we be able to just pass the format string to setFormatter(), and let it deal with any conversion inside the method?Natty
When will they change this madness? Who knowsChenopod
Sometimes I get double the logs, I use logger.propagate = False.Bobbysoxer
G
3

You have to create or use an existing subclass of logging.Handler and call the setformatter() method of an instance thereof with an instance of a custom subclass of logger.Formatter. If you set the formatter for a handler that was already attached to the logger you want to modify the output of, you are fine, otherwise you have to retrieve a logger object with logging.getLogger() and call its addHandler() method with the instance of your handler class that you set the formatter on as the argument.

Gutta answered 20/7, 2012 at 15:11 Comment(0)
E
0

logging.basicConfig(filename = file_path, level = logging.INFO, format = '%(message)s')

Euh answered 16/12, 2023 at 22:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.