Python: Logging TypeError: not all arguments converted during string formatting

PythonLoggingString Formatting

Python Problem Overview


Here is what I am doing

>>> import logging
>>> logging.getLogger().setLevel(logging.INFO)
>>> from datetime import date
>>> date = date.today()
>>> logging.info('date={}', date)
Traceback (most recent call last):
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 846, in emit
    msg = self.format(record)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 723, in format
    return fmt.format(record)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 464, in format
    record.message = record.getMessage()
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 328, in getMessage
    msg = msg % self.args
TypeError: not all arguments converted during string formatting
Logged from file <stdin>, line 1
>>> 

My python version is

$ python --version
Python 2.7.3

How do I make it work?

Python Solutions


Solution 1 - Python

You cannot use new-style formatting when using the logging module; use %s instead of {}.

logging.info('date=%s', date)

The logging module uses the old-style % operator to format the log string. See the debug method for more detail.

If you really want to use str.format() string formatting, consider using custom objects that apply the formatting 'late', when actually converted to a string:

class BraceMessage(object):
    def __init__(self, fmt, *args, **kwargs):
        self.fmt = fmt
        self.args = args
        self.kwargs = kwargs

    def __str__(self):
        return self.fmt.format(*self.args, **self.kwargs)

__ = BraceMessage

logging.info(__('date={}', date))

This is an approach the Python 3 logging module documentation proposes, and it happens to work on Python 2 too.

Solution 2 - Python

You could do the formatting yourself:

logging.info('date={}'.format(date))

As was pointed out by Martijn Pieters, this will always run the string formatting, while using the logging module would cause the formatting to only be performed if the message is actually logged.

Solution 3 - Python

Martijn's answer is correct, but if you prefer to use new style formatting with logging, it can be accomplished by subclassing Logger.

import logging

class LogRecord(logging.LogRecord):
    def getMessage(self):
        msg = self.msg
        if self.args:
            if isinstance(self.args, dict):
                msg = msg.format(**self.args)
            else:
                msg = msg.format(*self.args)
        return msg

class Logger(logging.Logger):
    def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
        rv = LogRecord(name, level, fn, lno, msg, args, exc_info, func)
        if extra is not None:
            for key in extra:
                rv.__dict__[key] = extra[key]
        return rv

Then just set the logging class:

logging.setLoggerClass(Logger)

Solution 4 - Python

You could do also (Python 3);

logging.info(f'date={date}')

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondaydreamerView Question on Stackoverflow
Solution 1 - PythonMartijn PietersView Answer on Stackoverflow
Solution 2 - PythonMattView Answer on Stackoverflow
Solution 3 - PythonJeff-MeadowsView Answer on Stackoverflow
Solution 4 - PythonyusufView Answer on Stackoverflow