12

I am using python's log formatter to format log records and i have a fmt value of

fmt = "[%(filename)s:%(lineno)s] %(message)s"

What i would like is that "[file.py:20]" to be stretched to 10 characters wide (for example). If it was one value that would have been easy but is there any way to stretch this entire structure to a specified length? I want something like:

tmp = "[%(filename)s:%(lineno)s]"
fmt = "%(tmp)10s %(message)s"

I would like to know if this is possible using string formatting or if I can trick python's formatter somehow to get what i want..

5
  • 2
    No. It's not possible without subclassing the Formatter to create a new kind of formatter. How important is this to you? Commented Jul 14, 2011 at 11:12
  • I would spend about 1-2 hours to get it working (although i'm a bit newbish). I assume i would have to override the format method and inject a custom attribute in the LogRecord object? Commented Jul 14, 2011 at 11:19
  • Is 10 chars a minimum width? i.e. if the string is longer than 10 chars do you want to truncate it? Commented Jul 14, 2011 at 11:31
  • not really important but lets say don't truncate it Commented Jul 14, 2011 at 11:46
  • I find myself surprised that I cannot use .ljust() in this case. Did the string stop being a string? Commented May 30, 2014 at 0:54

3 Answers 3

13

As an example, this Formatter ensures a fixed width "[%(filename)s:%(lineno)s]" by either truncating the filename, or right-padding (after the line number) with spaces.

class MyFormatter(logging.Formatter):
    width = 10

    def format(self, record):
        max_filename_width = self.width - 3 - len(str(record.lineno))
        filename = record.filename
        if len(record.filename) > max_filename_width:
            filename = record.filename[:max_filename_width]
        a = "%s:%s" % (filename, record.lineno)
        return "[%s] %s" % (a.ljust(self.width), record.msg)

if __name__ == '__main__':
    logger = logging.getLogger('simple_example')
    logger.setLevel(logging.DEBUG)
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    formatter = MyFormatter()
    ch.setFormatter(formatter)
    logger.addHandler(ch)

    logger.debug('No one expects the spammish repetition')

EDIT:

If you want to ensure a minimum width of 10 characters, ditch the filename stuff.

def format(self, record):
    a = "%s:%s" % (record.filename, record.lineno)
    return "[%s] %s" % (a.ljust(self.width), record.msg)
Sign up to request clarification or add additional context in comments.

Comments

9

Option 1

Start here: http://docs.python.org/library/logging.html#formatter-objects

You'll create your own customized subclass of Formatter that provides it's own unique format method.

Then you must be sure to call setFormatter() in each of your Handlers so that they use your new formatter.

Option 2

Create your own subclass of LogRecord with the additional property.

Subclass Logger and override makeRecord to create your new subclass of LogRecord.

Provide a customized format that uses this new property value.

Comments

2

Using @rob-cowie's answer as a basis, I've found the following useful:

class MyFormatter(logging.Formatter):
    width = 24
    datefmt='%Y-%m-%d %H:%M:%S'

    def format(self, record):
        cpath = '%s:%s:%s' % (record.module, record.funcName, record.lineno)
        cpath = cpath[-self.width:].ljust(self.width)
        record.message = record.getMessage()
        s = "%-7s %s %s : %s" % (record.levelname, self.formatTime(record, self.datefmt), cpath, record.getMessage())
        if record.exc_info:
            # Cache the traceback text to avoid converting it multiple times
            # (it's constant anyway)
            if not record.exc_text:
                record.exc_text = self.formatException(record.exc_info)
        if record.exc_text:
            if s[-1:] != "\n":
                s = s + "\n"
            s = s + record.exc_text
        #if record.stack_info:
        #    if s[-1:] != "\n":
        #        s = s + "\n"
        #    s = s + self.formatStack(record.stack_info)
        return s

logFormatter = MyFormatter()
logger = logging.getLogger("example")
logger.setFormatter(logFormatter)

Which gives output like:

WARNING 2014-03-28 16:05:09 module:function:31       : Message
WARNING 2014-03-28 16:05:09 dule:longerfunctions:140 : Message

1 Comment

Exception occurred when logger.setFormatter(logFormatter): AttributeError: 'Logger' object has no attribute 'setFormatter'

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.