日志记录格式右对齐级别名称



我正在尝试格式化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]

正如@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的方法,我不会在这里转发。

驱动程序示例如下,其中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("ttTHIS 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........

我修改了最后几行,如下所示:

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: %sn%s%s" % (
    os.path.basename(fn),
    ('-' * 15),
    ' ' * (width - 15 - 12)))

其他注意事项

您应该创建一个最小、完整、经过测试和可读的。您的代码不起作用,我需要修改一些内容才能运行示例。查看您的消息编辑历史记录,了解我必须编辑的内容。

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

最新更新