Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does python logging package not support printing variable length args?

Tags:

python

logging

When I first learned Python, I got used to doing this:

  print "text", lineNumber, "some dictionary", my_dict

When I wrote my own logging facility, I naturally wanted to be able to hand it an arbitrarily-sized list of items, so I did this:

def error(*args):
   print ERR_PREFIX,
   for _x in args:
      print _x,
   print "\r\n",

error("text", lineNumber, "some dictionary", my_dict)

Now I want to start using the logging package because it has a lot more goodies and I don't feel like replicating their effort. Overall it looks like a clean design that can do a lot. But I'm stymied by the fact that you can no longer present it with the same list of items for it to print. Instead, I must change all my calls to something more like this:

error("text %d some dictionary %s" % (lineNumber, my_dict))

Or, I could do something really silly like this:

error(' '.join(map, str(("text", lineNumber, "some dictionary", my_dict))))

The question is, why omit such an obvious usage case? If you want to go from the typical 'print' statement straight to the new-fangled logging facility, shouldn't this be easier?

As a follow-up question, can you think of a way to override the Logger class to perform this?

like image 686
Brian Avatar asked Dec 08 '25 12:12

Brian


2 Answers

I would suggest that it would be better to update the existing logging messages to the style that the logging module expects as it will be easier for other people looking at your code as the logging module will not longer function as they expect.

That out of the way, the following code will make the logging module behave as you desire.

import logging
import types

class ExtendedLogRecord(logging.LogRecord):

    def getMessage(self):
        """
        Return the message for this LogRecord.

        Return the message for this LogRecord after merging any user-supplied
        arguments with the message.
        """
        if not hasattr(types, "UnicodeType"): #if no unicode support...
            msg = str(self.msg)
        else:
            try:
                msg = str(self.msg)
            except UnicodeError:
                msg = self.msg      #Defer encoding till later
        if self.args:
            msg +=' '+' '.join(map(str,self.args))
        return msg

#Patch the logging default logging class
logging.RootLogger.makeRecord=lambda self,*args: ExtendedLogRecord(*args)

some_dict={'foo':14,'bar':15}
logging.error('text',15,'some dictionary',some_dict)

Output:

ERROR:root:text 15 some dictionary {'foo': 14, 'bar': 15}
like image 138
Mark Roddy Avatar answered Dec 10 '25 03:12

Mark Roddy


Patching the logging package (as one answer recommended) is actually a bad idea, because it means that other code (that you didn't write, e.g. stuff in the standard library) that calls logging.error() would no longer work correctly.

Instead, you can change your existing error() function to call logging.error() instead or print:

def error(*args):
  logging.error('%s', ' '.join(map(str, args)))

(If there might be unicode objects you'd have to be a bit more careful, but you get the idea.)

like image 25
user95975 Avatar answered Dec 10 '25 01:12

user95975