68

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()
vvvvv
  • 25,404
  • 19
  • 49
  • 81
Ricardo Altamirano
  • 14,650
  • 21
  • 72
  • 105
  • 1
    You just helped me to figure out how to add filename, function name, and line number to my logs. Thanks – Xander YzWich Nov 19 '19 at 20:26
  • 1
    @XanderYzWich See https://docs.python.org/3/library/logging.html#logrecord-attributes for the full list of available attributes – Harm Sep 14 '20 at 10:43

2 Answers2

112

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

0 _
  • 10,524
  • 11
  • 77
  • 109
Pavel Reznikov
  • 2,968
  • 1
  • 18
  • 17
  • +1, and +1 again (sadly no) for adding the code. I edited it into my [previous answer](http://stackoverflow.com/a/11581118/869912) to a logging question as well, and it works perfectly. – Ricardo Altamirano Jul 20 '12 at 15:50
  • 88
    I am always surprised it's not as simple as `logger = logging.getLogger('mylogger') logger.basicConfig(level=..., format=...)`... – theartofrain Feb 18 '15 at 21:58
  • 3
    Only the `logging` module has the [`basicConfig` function](https://docs.python.org/3/library/logging.html#logging.basicConfig) – tread Aug 10 '22 at 17:09
  • 1
    @tread yes, that's the problem/criticism here. – moooeeeep Jan 18 '23 at 10:11
  • @theartofrain 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? – Vinícius Queiroz Aug 30 '23 at 19:51
2

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.

Silas Ray
  • 25,682
  • 5
  • 48
  • 63