【问题标题】:What is the most pythonic way of logging for multiple modules and multiple handlers with specified encoding?使用指定编码记录多个模块和多个处理程序的最 Pythonic 方式是什么?
【发布时间】:2013-02-21 15:03:18
【问题描述】:

我正在寻找有关如何完成多模块和多处理程序日志记录的具体建议。我在这里添加了我的简化代码,但我不想让答案产生偏见 - 告诉我最佳做法是什么。

我想将所有内容记录到一个文件中,然后向控制台发出警告。

这是我的level0.py,我希望它记录到指定的文件:

import logging
from flask import Flask
from level1 import function1

app = Flask(__name__)

logger = logging.getLogger('logger0')
logger.setLevel(logging.DEBUG)

file_handler = logging.FileHandler('../logs/logger0','w','utf-8')
file_handler.setLevel(logging.DEBUG)
file_format = logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d in %(funcName)s]')
file_handler.setFormatter(file_format)
logger.addHandler(file_handler)

console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_format = logging.Formatter('%(message)s')
console_handler.setFormatter(console_format)
logger.addHandler(console_handler)

@app.route('/', methods=['GET', 'POST'])
def function0(foo):
    bar = function1(foo)
    logger.debug('function0')
    ...

另外,level1 在作为脚本调用时可以是一个独立的模块。在这种情况下,我希望它记录到另一个文件中。下面是level1.py(有重复的日志行):

import logging
logger = logging.getLogger('level0.level1')

from level2 import function2

def function1(foo):
    bar = function2(foo)
    logger.debug('function1')
    ...

if __name__ == "__main__":
    logger = logging.getLogger('logger0')
    logger.setLevel(logging.DEBUG)

    file_handler = logging.FileHandler('../logs/logger1','w','utf-8')
    file_handler.setLevel(logging.DEBUG)
    file_format = logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d in %(funcName)s]')
    file_handler.setFormatter(file_format)
    logger.addHandler(file_handler)

    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    console_format = logging.Formatter('%(message)s')
    console_handler.setFormatter(console_format)
    logger.addHandler(console_handler)

    bar = function1('foo')
    logger.info('level1 main')
    ...

我从level0 复制了这个日志记录块,因为我想要相同的日志记录,而且将它放在 main 中似乎很直观。 level2 不是独立的,所以它只有:

import logging
logger = logging.getLogger('level0.level1.level2')

def function2(foo):
    logger.info('function2')
    ....

我从 logging.basicSetup 开始,但无法为文件设置编码并在尝试记录非 ascii 字符串时不断收到 UnicodeEncodeError

logger.warn(u'foo bar {}'.format(NON_ASCII_STR))

(当记录器将消息传递给其父级时,我仍然收到错误消息)

那么,对于这种情况或更一般而言,最好的日志设计是什么 - 多个模块(带有编码选择 - 我想要 utf-8)

【问题讨论】:

    标签: python logging character-encoding module


    【解决方案1】:

    对于由许多部分组成的模块,我使用documentation 中推荐的方法,每个模块只有一行,logger = logging.getLogger(__name__)。正如您所指出的,模块不应该知道或关心它的消息如何或去哪里,它只是将它传递给应该由主程序设置的记录器。

    要根据您的主程序来减少剪切粘贴,请确保您的模块具有合理的层次结构,并且在某处只有一个函数可以设置您的日志记录,然后可以由您的任何主程序调用希望。

    例如,制作一个logsetup.py:

    import logging
    
    def configure_log(level=None,name=None):
        logger = logging.getLogger(name)
        logger.setLevel(level)
    
        file_handler = logging.FileHandler('../logs/%s' % name,'w','utf-8')
        file_handler.setLevel(logging.DEBUG)
        file_format = logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d in %(funcName)s]')
        file_handler.setFormatter(file_format)
        logger.addHandler(file_handler)
    
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.INFO)
        console_format = logging.Formatter('%(message)s')
        console_handler.setFormatter(console_format)
        logger.addHandler(console_handler)
    

    要让您的各个模块有一种模式,它们在其中充当主模块,请定义一个单独的函数,例如 main

    在 level0.py 和/或 level1.py 中:

    def main():
      # do whatever
    

    在最顶层的程序中,调用该函数:

    import logging
    from logsetup import configure_log
    configure_log(logging.DEBUG,'level0') # or 'level1'
    from level0 import main # or level1
    
    if __name__ == "__main__"
      main()
    

    你应该仍然有 __name__ == "__main__" 子句,一些模块(cough multiprocessing cough)根据子句是否存在而具有不同的行为。

    【讨论】:

    • 我想我明白事情应该如何表现;每个模块都会创建一个子记录器,它将所有内容传递给它的父记录器,并且最上面的记录器有两个处理程序 - 所以所有记录都到同一个地方。正如@CaptainMurphy 所说,我创建了一个新的dummy_top_level.py。带有两个处理程序的 level0 记录器是可以的。当我在level0 中导入level1 函数时,这会创建一个level1 记录器,其父级是root,而不是level0。这意味着我只能正确记录来自level0 的内容。有什么建议么? PS:谢谢@CaptainMurphy,你帮助我更多地了解了日志记录。
    • 是的,为了让它像宣传的那样工作,你的模块组最终应该都在一个名称下,然后在里面有子模块。因此,例如,创建一个文件夹 all_the_levels,将 level0.py 和 level1.py 放入其中,并创建一个名为 init.py 的空文件(请参阅here 了解原因)。然后像这样导入:import all_the_levels.level0。如果您想从 all_the_levels 导入 level0,则必须将“import level0”添加到您的 init.py。
    • 我已经玩了一段时间了。我已将__init__.py 添加到所有文件夹并稍微移动了文件。包__init__.py 获得了记录器配置。现在我得到了记录器level0.level1level0.level2。只是包层次结构,而不是我想要的,这将是导入顺序。就像您说的那样 - 我需要很多文件夹才能获得所需的日志记录,但外部包仍然无法访问它。我得出的结论是包之间的日志记录不起作用。我说的对吗?
    • 好吧,您可以随时将处理程序添加到根记录器。我不确定您所说的“介于”包之间是什么意思。您可以通过将它们添加到__init__.py 来使您希望从顶层访问的任何功能。在您的tippy-top 主程序中,您必须为每个包配置日志记录。您能否将您的文件夹层次结构放入问题中,以便我查看您的排列方式?
    • 继续使用这些名称:我现在有一个文件夹/包 level0 和日志配置 __init__.py 以及所有三个模块 - level0.pylevel1.pylevel2.py。我想我现在知道我想要什么了 :) 我会尝试一些东西。谢谢@CaptainMurphy
    【解决方案2】:

    总结一下,这就是我所做的;我在每个模块/文件中添加了以下两行:

    import logging
    logger = logging.getLogger(__name__)
    

    这会设置日志记录,但不会添加任何处理程序。然后,我将处理程序添加到主文件中的根记录器中,我正在运行导入的模块,因此将它们的记录传递给根记录器,所有内容都会被保存和显示。我像 CaptainMurphy 建议的那样做,但使用 logger = logging.getLogger('') 与根记录器一起工作

    解决编码问题 - 我在将非 ascii 字符串保存到文件时遇到问题。所以我只是在根记录器中添加了FileHandler,可以在其中指定编码。 logging.basicConfig 做不到。

    再次感谢@CaptainMurphy - 很抱歉,我的声望很低,我无法为你投票,但我已经勾选了答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-26
      • 1970-01-01
      • 1970-01-01
      • 2014-04-26
      相关资源
      最近更新 更多