4

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

我想将所有内容记录到文件中,并警告及以上到控制台。

这是我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.js 中似乎很直观。level2不是独立的,所以它只有:

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

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

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

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

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

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

4

2 回答 2

10

对于由许多部分组成的模块,我使用文档中推荐的方法,每个模块只有一行,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__"子句,某些模块(咳嗽多处理咳嗽)具有不同的行为,具体取决于该子句是否存在。

于 2013-02-21T20:13:23.550 回答
6

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

import logging
logger = logging.getLogger(__name__)

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

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

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

于 2013-02-25T14:59:57.683 回答