1# Copyright 2004-2008 Roman Yakovenko. 2# Distributed under the Boost Software License, Version 1.0. (See 3# accompanying file LICENSE_1_0.txt or copy at 4# http://www.boost.org/LICENSE_1_0.txt) 5 6# Initial version by Matthias Baas (baas@ira.uka.de). 7 8"""defines a class that helps to format user messages""" 9 10import os, logging, textwrap 11 12class multi_line_formatter_t(logging.Formatter): 13 """Custom log formatter to split long message into several lines. 14 15 This formatter is used for the default stream handler that outputs 16 its messages to `stdout`. 17 """ 18 19 def __init__(self, fmt=None, datefmt=None, width=None): 20 """Constructor. 21 22 See the Python standard library reference for a documentation 23 of fmt and datefmt. 24 width is the maximum width of the generated text blocks. 25 """ 26 logging.Formatter.__init__(self, fmt, datefmt) 27 if None == width: 28 try: 29 import curses 30 curses.setupterm() 31 self._width = curses.tigetnum('cols') 32 # Leave a border of two blanks to prevent that full lines 33 # wrap to the next line 34 self._width -= 2 35 except: 36 self._width = 70 # default to 70 37 38 def format(self, record): 39 """This method overwrites the original one. 40 41 The first thing, that is done in the original format() method, is the 42 creation of the record.message attribute: 43 44 record.message = record.getMessage() 45 46 Now this method temporarily replaces the getMessage() method of 47 the record by a version that returns a generated message that 48 spans several lines. Then the original format() method is called 49 which will invoke the 'fake' method. 50 """ 51 # Get the original single-line message 52 message = record.getMessage() 53 # Distribute the message among several lines... 54 message = self.formatMessage(message, width=self._width) 55 # ...and temporarily replace the getMessage() method so that the 56 # reformatted message is used 57 mth = record.getMessage 58 record.getMessage = lambda x=message: x 59 # Invoke the inherited format() method 60 res = logging.Formatter.format(self, record) 61 # Restore the original method 62 record.getMessage = mth 63 return res 64 65 @staticmethod 66 def formatMessage(msgline, width=70): 67 """Format a long single line message so that it is easier to read. 68 69 `msgline` is a string containing a single message. It can either be 70 a plain message string which is reformatted using the :mod:`textwrap` 71 module or it can be of the form <declaration>;<message> where <declaration> 72 is the declaration string and <message> an arbitrary message. Lines of this 73 form will be separated so that the declaration and the message appear in 74 individual text blocks, where every line of message will start 75 with '>' character. 76 77 width is the maximum width of any text blocks (without indentation). 78 """ 79 if isinstance(msgline, logging.LogRecord): 80 msgline = msgline.getMessage() 81 txts = msgline.split(";") 82 # Ensure that there are no more than two items in txts 83 if len( txts ) != 2: 84 #If message is not in format we expected, just return it 85 return os.linesep.join( textwrap.wrap( msgline, width ) ) 86 87 lines = [ txts[0] ] #I don't want to break declaration string to few lines 88 89 # Insert a separator if there are two parts (=decl and msg) 90 # Apply the text wrapper to shorten the maximum line length 91 wrapped_lines = textwrap.wrap( txts[1], width ) 92 lines.extend( ["> " + s.strip() for s in wrapped_lines] ) 93 94 return os.linesep.join(lines) 95 96