简体   繁体   English

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

[英]What is the most pythonic way of logging for multiple modules and multiple handlers with specified encoding?

I'm looking for concrete advice about how the multiple module and multiple handler logging should be done. 我正在寻找有关如何完成多模块和多处理程序日志记录的具体建议。 I have added my simplified code here, but I don't want to bias the answers - tell me what the best practice is. 我在这里添加了我的简化代码,但我不想偏袒答案 - 告诉我最佳实践是什么。

I would like to log everything to a file, and warn and above to console. 我想将所有内容记录到一个文件中,然后向控制台发出警告。

This is my level0.py which I want it to log to the specified file: 这是我的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')
    ...

Also, level1 can be a standalone module, when called as a script. 此外,当作为脚本调用时, level1可以是独立模块。 In that case I want it logged to another file. 在那种情况下,我希望它记录到另一个文件。 Below is the level1.py (has duplicated logging lines): 下面是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')
    ...

I've copied this logging chunk from level0 , because I wanted the same logging and it seemed intuitive to put it in main. 我已经从level0复制了这个日志块,因为我想要相同的日志记录,将它放在main中似乎很直观。 level2 is not standalone, so it only has: level2不是独立的,因此它只有:

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

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

I started with logging.basicSetup , but couldn't set encoding for the file and kept getting UnicodeEncodeError when trying to log non-ascii strings: 我开始使用logging.basicSetup ,但无法为该文件设置编码并在尝试记录非ascii字符串时不断获取UnicodeEncodeError

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

(I still get the error as the logger passes the message to its parents) (当记录器将消息传递给其父项时,我仍然收到错误)

So, what is the best log design for this case or more generally - multiple modules (with encoding selection - I want utf-8) 那么,这个案例的最佳日志设计是什么,或者更普遍的 - 多个模块(带编码选择 - 我想要utf-8)

For modules consisting of many parts, I use the method recommended in the documentation , which just has one line per module, logger = logging.getLogger(__name__) . 对于由许多部分组成的模块,我使用文档中推荐的方法,每个模块只有一行, logger = logging.getLogger(__name__) As you point out, the module shouldn't know or care how or where its messages are going, it just passes it on to the logger that should have been set up by the main program. 正如您所指出的,模块不应该知道或关心其消息的方式或位置,它只是将其传递给应该由主程序设置的记录器。

To reduce cut-n-paste depending on which is your main program, make sure your modules have a hierarchy that makes sense, and have just one function somewhere that sets up your logging, which can then be called by any main you wish. 为了减少cut-n-paste,取决于你的主程序,确保你的模块有一个有意义的层次结构,并且只有一个功能可以设置你的日志记录,然后可以通过你想要的任何主要调用。

For example, make a logsetup.py: 例如,创建一个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)

To have your individual modules have a mode in which they behave as main, define a separate function, such as main . 要使单个模块具有其主要行为模式,请定义单独的功能,例如main

In level0.py and/or level1.py: 在level0.py和/或level1.py中:

def main():
  # do whatever

And in the very top level program, call that function: 在最顶级的程序中,调用该函数:

import logging
from logsetup import configure_log
configure_log(logging.DEBUG,'level0') # or 'level1'
from level0 import main # or level1

if __name__ == "__main__"
  main()

You should still have the __name__ == "__main__" clause there, some modules ( cough multiprocessing cough ) have different behavior depending on whether or not the clause exists. 你应该仍然有__name__ == "__main__"子句,一些模块( 咳嗽多处理咳嗽 )具有不同的行为,具体取决于该子句是否存在。

To kinda wrap this up, here is what I did; 有点包装,这就是我所做的; I've put the following two lines in every module/file: 我在每个模块/文件中放了以下两行:

import logging
logger = logging.getLogger(__name__)

This sets up the logging, but doesn't add any handlers. 这会设置日志记录,但不会添加任何处理程序。 I then add handlers to the root logger in the main file I'm running the imported modules therefore pass their records to the root logger and everything gets saved and displayed. 然后我将处理程序添加到主文件中的根记录器我正在运行导入的模块,因此将它们的记录传递给根记录器,一切都得到保存和显示。 I do it like CaptainMurphy suggested, but with logger = logging.getLogger('') to work with the root logger 我像CaptainMurphy建议的那样做,但用logger = logging.getLogger('')来处理根记录器

To address encoding - I had problems saving non-ascii strings to file. 为了解决编码问题 - 我在将非ascii字符串保存到文件时遇到了问题。 So I just added FileHandler to the root logger, where encoding can be specified. 所以我只是将FileHandler添加到根记录器中,可以指定编码。 I couldn't do it with logging.basicConfig . 我无法用logging.basicConfig做到这一点。

Thank you again @CaptainMurphy - I'm sorry I can't upvote you with my low reputation, but I've checkmarked the answer. 再次感谢@CaptainMurphy - 对不起,我不能以低声望向你投票,但我已经勾选了答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM