对于具有指定编码的多个模块和多个处理程序,最具pythonic的日志记录方式是什么?

时间:2013-02-21 15:03:19

标签: python logging character-encoding module

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

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

这是我的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)

2 个答案:

答案 0 :(得分:9)

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

根据你的主要程序减少cut-n-paste,确保你的模块有一个有意义的层次结构,并且只有一个功能可以设置你的日志记录,然后任何主要的你都可以调用它愿望。

例如,创建一个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 多处理 cough )具有不同的行为,具体取决于子句是否存在。< / p>

答案 1 :(得分:4)

有点包装,这就是我所做的;我在每个模块/文件中都放了以下两行:

import logging
logger = logging.getLogger(__name__)

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

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

再次感谢@CaptainMurphy - 对不起,我不能以低声誉向你投票,但我已经勾选了答案。