8

I have the following logging class, which works fine when assigned as a formatter in code. It extends an existing formatter by prepending a string to the start of the message to be logged, to help show the importance of the message. (I don't just use %(levelname)s in the format string because I don't want to show DEBUG or INFO prefixes.)

class PrependErrorLevelFormatter(logging.Formatter):
    def __init__(self, default):
        self._default_formatter = default
    def format(self, record):
        if record.levelno == logging.WARNING:
            record.msg = "[WARNING] " + record.msg
        elif record.levelno == logging.ERROR:
            record.msg = "[ERROR] " + record.msg
        elif record.levelno == logging.CRITICAL:
            record.msg = "[CRITICAL] " + record.msg
        return self._default_formatter.format(record)

Now I want to be able to assign it via a configuration file loaded in by logging.config.fileConfig(). I have tried syntax like this:

[formatter_PrependErrorLevelFormatter]
format=%(asctime)s  %(message)s
datefmt=%X
class=PrependErrorLevelFormatter

Unfortunately I get errors resolving this class:

  File "C:\Python27\lib\logging\config.py", line 70, in fileConfig
    formatters = _create_formatters(cp)
  File "C:\Python27\lib\logging\config.py", line 127, in _create_formatters
    c = _resolve(class_name)
  File "C:\Python27\lib\logging\config.py", line 88, in _resolve
    found = __import__(used)
ImportError: No module named PrependErrorLevelFormatter

I have tried prefixing the class name with the name of the module it is in, but get the same error. And even if it could resolve the class, it will probably not work due to the extra default formatter argument I need to provide.

How can I achieve the result I want using the logging.config system?

4
  • You can use %(levelname)s in the format definition (docs.python.org/library/logging.html#logrecord-attributes). Commented Feb 9, 2012 at 14:36
  • I commented on this in the first paragraph. Commented Feb 9, 2012 at 14:48
  • So, is your question answered now? Commented Feb 12, 2012 at 14:21
  • Yes, sorry - I meant to click answer as well as upvote! Thanks for your help. Commented Feb 13, 2012 at 10:53

1 Answer 1

13

As you are using Python 2.7, you could use dictionary-based configuration using dictConfig(): this is more flexible than fileConfig(), as it allows use of arbitrary callables as factories to return e.g. handlers, formatters, or filters.

If you have to use fileConfig(), you'll have to construct a callable which takes format and datefmt string values and returns an instance of your class. The class value just needs to resolve to a callable, not an actual class. Here's a setup that works: In this gist, I have a file custfmt.py which contains the formatter definition, and a script fcfgtest.py which uses it via fileConfig(). Just get the files into a scratch directory and run fcfgtest.py - you should see output like this:

20:17:59 debug message
20:17:59 info message
20:17:59 [WARNING] warning message
20:17:59 [ERROR] error message
20:17:59 [CRITICAL] critical message

which appears to be what you need.

Note that you can use an alternative design for your formatter, which should do the same job:

class AltCustomFormatter(logging.Formatter):
    def format(self, record):
        if record.levelno in (logging.WARNING,
                              logging.ERROR,
                              logging.CRITICAL):
            record.msg = '[%s] %s' % (record.levelname, record.msg)
        return super(AltCustomFormatter , self).format(record)

To use this, you don't need a separate factory function, so you can just use

class=custfmt.AltCustomFormatter

instead of

class=custfmt.factory

and it should work - it did for me when I tested just now using Python 2.7.1 :-)

Sign up to request clarification or add additional context in comments.

1 Comment

Ok, I think it's working. I had to remember to prefix the class name with the name of the file it's in, but it seems to find it now. (Whereas this didn't work for my previous solution - strange!)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.