繁体   English   中英

在 Python 中使用 root logger 还是 named logger 更好

[英]Is it better to use root logger or named logger in Python

我正在尝试找出跨多个模块使用 python 中的日志记录的最佳实践。 我在这里看到: http://docs.python.org/2/howto/logging#logging-from-multiple-modules关于如何使用根记录器跨多个模块进行记录。 正如链接所指出的那样,您无法分辨您的应用程序中您的消息来自何处,因为它们都显示名称“root”。

在我看来有两个选项(假设我的模块不在 package 结构中,而只是同一文件夹中的一堆模块):

1) 使用示例中的根记录器,但更改日志格式以包含文件名:

# myapp.py
import logging
import mylib

def main():
    logging.basicConfig(format='%(asctime)s %(filename)s %(levelname)s: %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p', level=logging.INFO)  #filename='myapp.log', 
    logging.info('Started')
    mylib.do_something()
    logging.info('Finished')

if __name__ == '__main__':
    main()

#mylib.py
import logging

def do_something():
    logging.info('Do something')




In [4]: import myapp

In [5]: myapp.main()
03/06/2014 12:22:07 PM myapp.py INFO: Started
03/06/2014 12:22:07 PM mylib.py INFO: Do something
03/06/2014 12:22:07 PM myapp.py INFO: Finished

2)在主应用程序中使用根记录器,但在子模块中使用命名记录器,并在日志格式中使用“名称”而不是“文件名”:

# myapp.py
import logging
import mylib

def main():
    #logging.basicConfig(format='%(asctime)s %(filename)s %(levelname)s: %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p', level=logging.INFO)  #filename='myapp.log', 
    logging.basicConfig(format='%(asctime)s %(name)s %(levelname)s: %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p', level=logging.INFO)  #filename='myapp.log', 
    logging.info('Started')
    mylib.do_something()
    logging.info('Finished')

if __name__ == '__main__':
    main()

#mylib.py
import logging

def do_something():
    #logging.info('Do something')
    logger = logging.getLogger(__name__)
    logger.info('Do something')



In [3]: import myapp

In [4]: myapp.main()
03/06/2014 12:27:29 PM root INFO: Started
03/06/2014 12:27:29 PM mylib INFO: Do something
03/06/2014 12:27:29 PM root INFO: Finished

Vinay Sajip(日志记录模块的维护者) 在这里提出了一个类似于你的选项#2的建议,除了你可以在任何地方使用一个命名的记录器,即使在myapp中:

import logging
import mylib
logger = logging.getLogger(__name__)

def main():
    logging.basicConfig(format='%(asctime)s %(module)s %(levelname)s: %(message)s',
                        datefmt='%m/%d/%Y %I:%M:%S %p', level=logging.INFO)  
    logger.info('Started')
    mylib.do_something()
    logger.info('Finished')

if __name__ == '__main__':
    main()

产量

03/06/2014 12:59:25 PM myapp INFO: Started
03/06/2014 12:59:25 PM mylib INFO: Do something
03/06/2014 12:59:25 PM myapp INFO: Finished

请注意,如果您使用%(module)s而不是%(name)s ,那么在获得rootmyapp.py__main__之前,您将获得myapp

如果myapp有时被称为脚本并且有时作为模块导入,那么这种对称 - 总是使用logger - 可能特别有用。

似乎 OP 的主要目标是指示消息来自何处,IMO 是任何日志记录设置的重要组成部分。

我发现logging模块目前需要一些调整来完成一些实用的事情:因为虽然LogRecord 属性之一lineno ,但识别文件有点困难:你有modulefilenamepathname 但后者是整个路径,而您真正想要的是来自 CWD 的路径。

因此,我建议这样做:

cwd_path = pathlib.Path.cwd()

class TweakedFormatter(logging.Formatter):
    def formatMessage(self, record):
        # tweak: curtail the pathname to show only the path elements from the CWD
        path = pathlib.Path(record.pathname)
        # NB following evaluates to False if pathname of log event is not under CWD (rare occurrence)
        if path.is_relative_to(cwd_path):
            record.pathname = str(path.relative_to(cwd_path))
        return self._style.format(record) # copied from source

def get_logger():
    logger = logging.Logger(project_name)
    console_handler = logging.StreamHandler(sys.stdout)
    console_formatter = TweakedFormatter(
        fmt='%(asctime)s %(levelname)s [%(pathname)s %(lineno)d]: %(message)s', datefmt='%a %H:%M:%S')
    console_handler.setFormatter(console_formatter)
    logger.addHandler(console_handler)
    return logger

我举例说明了典型控制台记录器的情况:您不需要特别需要打印出完整日期,并且屏幕空间非常宝贵(防杂乱)。

注意我实际上只调用get_logger一次,并将结果import到所有文件中。 我从来没有真正找到记录器层次结构的用途(尽管我确信如果不需要它,这个想法永远不会出现......)。

暂无
暂无

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

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