Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to log number with commas as thousands separators?

The time has come to convert all prints into logging calls in a library I'm maintaining. Some of the print calls are using str.format, like this (simplified):

>>> n = 4000000
>>> print(f"this bird wouldn't voom if you put {n:,} volts through it!")
this bird wouldn't voom if you put 4,000,000 volts through it!

When I try to log it:

>>> log.warning("this bird wouldn't voom if you put %d, volts through it!", n)
WARNING:root:this bird wouldn't voom if you put 4000000, volts through it!

Seems this is not specifying the thousands separator correctly. How do you specify the thousands separator when using the %-formatting syntax which Python's stdlib logging module necessitates?

Currently using this workaround, which does give the desired output, but seems wrong since the variable is formatted first using str.format and then formatted as a string again, instead of logging as a number directly:

>>> log.warning("this bird wouldn't voom if you put %s volts through it!", format(n, ','))
WARNING:root:this bird wouldn't voom if you put 4,000,000 volts through it!
like image 355
wim Avatar asked Sep 01 '25 05:09

wim


1 Answers

That's a limitation with logging and it's actually mentioned in (at least one place in) the documentation:

logging.debug(msg, *args, **kwargs)

Logs a message with level DEBUG on the root logger. The msg is the message format string, and the args are the arguments which are merged into msg using the string formatting operator. (Note that this means that you can use keywords in the format string, together with a single dictionary argument.)

(emphasis mine)

But the string formatting operator % doesn't support thousand seperators.

You could however adapt a recipe from the official cookbook:

import logging

class Message(object):
    def __init__(self, fmt, args):
        self.fmt = fmt
        self.args = args

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

class StyleAdapter(logging.LoggerAdapter):
    def __init__(self, logger, extra=None):
        super(StyleAdapter, self).__init__(logger, extra or {})

    def log(self, level, msg, *args, **kwargs):
        if self.isEnabledFor(level):
            msg, kwargs = self.process(msg, kwargs)
            self.logger._log(level, Message(msg, args), (), **kwargs)

logger = StyleAdapter(logging.getLogger(__name__))

def main():
    # this changed
    logger.warning("this bird wouldn't voom if you put {:,} volts through it!", 4000000)

if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG)
    main()

WARNING:__main__: this bird wouldn't voom if you put 4,000,000 volts through it!

This is actually copied verbatim (I just changed the message) from the last example in the "Use of alternative formatting styles" section.


Personally I would just go with your format(n, ',') solution. It may not be perfect but it doesn't require setting up a custom LoggerAdapter to print a different thousand seperator.

like image 135
MSeifert Avatar answered Sep 02 '25 20:09

MSeifert