记录格式右对齐级别名称

时间:2014-02-21 01:14:57

标签: python string logging formatting

我正在尝试格式化logging的输出,以便始终在终端的右侧显示levelname。我目前有一个看起来像这样的脚本:

import logging, os, time

fn = 'FN'
start = time.time()

def getTerminalSize():
    import os
    env = os.environ
    def ioctl_GWINSZ(fd):
        try:
            import fcntl, termios, struct, os
            cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,
        '1234'))
        except:
            return
        return cr
    cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
    if not cr:
        try:
            fd = os.open(os.ctermid(), os.O_RDONLY)
            cr = ioctl_GWINSZ(fd)
            os.close(fd)
        except:
            pass
    if not cr:
        cr = (env.get('LINES', 25), env.get('COLUMNS', 80))
    return int(cr[1]), int(cr[0])


(width, _) = getTerminalSize()
level_width = 8
message_width = width - level_width - 4
FORMAT = '%(message)-{len1:{width1}d}s [%(levelname){len2:{width2}d}s]'.format(
    len1 = message_width,_
    len2 = level_width,_
    width1 = len(str(message_width)),_
    width2 = len(str(level_width)))

logging.basicConfig(format=FORMAT, level="DEBUG")

logging.debug("Debug Message")
logging.info("Info Message")
logging.warning("Warning Message")
logging.error("Error Message")
logging.critical("Critical Message")
logging.info("Starting File: " + os.path.basename(fn) + "\n-----------------------------------------")
logging.info("\tTo read data: %s"%(time.time() - start))

输出如下:

Debug Message                                                        [   DEBUG]
Info Message                                                         [    INFO]
Warning Message                                                      [ WARNING]
Error Message                                                        [   ERROR]
Critical Message                                                     [CRITICAL]
Starting File: Channel209.Raw32
----------------------------------------- [    INFO]
        To read data: 0.281999826431                                        [
 INFO]

我希望输出看起来像这样,并且无法弄清楚:

Debug Message                                                        [   DEBUG]
Info Message                                                         [    INFO]
Warning Message                                                      [ WARNING]
Error Message                                                        [   ERROR]
Critical Message                                                     [CRITICAL]
Starting File: Channel209.Raw32
-----------------------------------------                            [    INFO]
        To read data: 0.281999826431                                 [    INFO]

2 个答案:

答案 0 :(得分:2)

正如@Carpetsmoker所说,要做我真正想要的事情,需要创建一个覆盖默认值的新格式化程序。

以下课程适用于此过程:

import logging
import textwrap
import itertools
'''
MyFormatter class
Adapted from: https://stackoverflow.com/questions/6847862/how-to-change-the-format-of-logged-messages-temporarily-in-python
              https://stackoverflow.com/questions/3096402/python-string-formatter-for-paragraphs
Authors: Vinay Sajip, unutbu
'''
class MyFormatter(logging.Formatter):

    #This function overwrites logging.Formatter.format
    #We conver the msg into the overall format we want to see
    def format(self,record):


        widths=[getTerminalSize()[0] - 12 ,10]
        form='{row[0]:<{width[0]}} {row[1]:<{width[1]}}'

        #Instead of formatting...rewrite message as desired here
        record.msg = self.Create_Columns(form,widths,[record.msg],["[%8s]"%record.levelname])

        #Return basic formatter
        return super(MyFormatter,self).format(record)

    def Create_Columns(self,format_str,widths,*columns):
        '''
        format_str describes the format of the report.
        {row[i]} is replaced by data from the ith element of columns.

        widths is expected to be a list of integers.
        {width[i]} is replaced by the ith element of the list widths.

        All the power of Python's string format spec is available for you to use
        in format_str. You can use it to define fill characters, alignment, width, type, etc.

        formatter takes an arbitrary number of arguments.
        Every argument after format_str and widths should be a list of strings.
        Each list contains the data for one column of the report.

        formatter returns the report as one big string.
        '''
        result=[]
        for row in zip(*columns):

            #Create a indents for each row...
            sub = []

            #Loop through
            for r in row:
                #Expand tabs to spaces to make our lives easier
                r = r.expandtabs()

                #Find the leading spaces and create indend character
                if r.find(" ") == 0:
                    i = 0
                    for letters in r:
                        if not letters == " ":
                            break
                        i += 1
                    sub.append(" "*i)
                else:
                    sub.append("")

            #Actually wrap and creat the string to return...stolen from internet
            lines=[textwrap.wrap(elt, width=num, subsequent_indent=ind) for elt,num,ind in zip(row,widths,sub)]
            for line in itertools.izip_longest(*lines,fillvalue=''):
                result.append(format_str.format(width=widths,row=line))
        return '\n'.join(result)

它依赖于在名为getTerminalSize的某个函数中获取终端大小。我使用了Harco Kuppens' Method,我不会在这里重新发布。

示例驱动程序如下,其中MyFormattergetTerminalSize位于Colorer

import logging
import Colorer

logger = logging.getLogger()
logger_handler = logging.StreamHandler()
logger.addHandler(logger_handler)
logger_handler.setFormatter(Colorer.MyFormatter("%(message)s"))
logger.setLevel("DEBUG")

logging.debug("\t\tTHIS IS A REALY long DEBUG Message that works and wraps around great........")
logging.info("   THIS IS A REALY long INFO Message that works and wraps around great........")
logging.warning("THIS IS A REALY long WARNING Message that works and wraps around great........")
logging.error("\tTHIS IS A REALY long ERROR Message that works and wraps around great........")
logging.critical("THIS IS A REALY long CRITICAL Message that works and wraps around great........")

输出为(为便于阅读而评论):

#                 THIS IS A REALY long DEBUG Message that works and    [   DEBUG]
#                 wraps around great........
#    THIS IS A REALY long INFO Message that works and wraps around     [    INFO]
#    great........
# THIS IS A REALY long WARNING Message that works and wraps around     [ WARNING]
# great........
#         THIS IS A REALY long ERROR Message that works and wraps      [   ERROR]
#         around great........
# THIS IS A REALY long CRITICAL Message that works and wraps around    [CRITICAL]
# great........

答案 1 :(得分:1)

我将最后一行修改为:

logging.info("Starting File: %s" % os.path.basename(fn))
logging.info("%s" % ('-' * 15))
logging.info("        To read data: %s" % (time.time() - start))

您的错误是使用换行符(\n)和标签符(\t)。

或者,如果你必须保留换行符(这对我来说似乎很奇怪),你可以手动添加空格,如下所示:

logging.info("Starting File: %s\n%s%s" % (
    os.path.basename(fn),
    ('-' * 15),
    ' ' * (width - 15 - 12)))

其他说明

您应该创建一个Minimal, Complete, Tested and Readable。你的代码没有用,我需要修改一些东西才能让例子运行。有关我必须编辑的内容,请参阅your messages edit history

从Python 3.3开始,有os.get_terminal_size。如果没有,那么subprocess.call(['tput cols'], shell=True)似乎对我来说简单得多......