在多个类中记录日志并包含模块名称

22 投票
2 回答
48638 浏览
提问于 2025-04-18 04:52

我想用日志模块来代替打印输出调试信息和文档。我的目标是在控制台上以DEBUG级别打印信息,同时以INFO级别记录到文件中。

我看了很多关于日志模块的文档、食谱和其他教程,但还是搞不清楚怎么按照我想要的方式使用它。(我用的是python25)

我希望在我的日志文件中能看到写入日志的模块名称。

文档上说我应该使用 logger = logging.getLogger(__name__),但是我该怎么在其他模块/包中的类里声明日志记录器,以便它们使用和主日志记录器相同的处理器呢?为了识别“父级”,我可以用 logger = logging.getLogger(parent.child),但我怎么知道是谁调用了这个类/方法呢?

下面的例子展示了我的问题,如果我运行这个,输出中只会有 __main__ 的日志,而忽略了 Class 中的日志。

这是我的 Mainfile:

# main.py

import logging
from module import Class

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# create file handler which logs info messages
fh = logging.FileHandler('foo.log', 'w', 'utf-8')
fh.setLevel(logging.INFO)

# create console handler with a debug log level
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# creating a formatter
formatter = logging.Formatter('- %(name)s - %(levelname)-8s: %(message)s')

# setting handler format
fh.setFormatter(formatter)
ch.setFormatter(formatter)

# add the handlers to the logger
logger.addHandler(fh)
logger.addHandler(ch)

if __name__ == '__main__':
    logger.info('Script starts')
    logger.info('calling class Class')
    c = Class()
    logger.info('calling c.do_something()')
    c.do_something()
    logger.info('calling c.try_something()')
    c.try_something()

模块:

# module.py

imnport logging

class Class:
    def __init__(self):
        self.logger = logging.getLogger(__name__) # What do I have to enter here?
        self.logger.info('creating an instance of Class')
        self.dict = {'a':'A'}
    def do_something(self):
        self.logger.debug('doing something')
        a = 1 + 1
        self.logger.debug('done doing something')
    def try_something(self):
        try:
            logging.debug(self.dict['b'])
        except KeyError, e:
            logging.exception(e)

控制台 的输出:

- __main__ - INFO    : Script starts
- __main__ - INFO    : calling class Class
- __main__ - INFO    : calling c.do_something()
- __main__ - INFO    : calling c.try_something()
No handlers could be found for logger "module"

另外:有没有办法在我的日志文件中获取发生日志的模块名称,而不需要像上面那样在每个类中声明一个新的日志记录器?而且这样的话,每次我想记录日志都得用 self.logger.info()。我更希望在整个代码中使用 logging.info()logger.info()

是不是全局日志记录器可能是这个问题的正确答案?但那样我就无法在日志中看到错误发生的模块了……

最后一个问题:这样做算不算符合Python的风格?或者有没有更好的建议来正确处理这些事情。

2 个回答

11

一般来说,推荐的日志记录设置是每个模块最多有一个日志记录器。

如果你的项目是正确打包的,__name__的值会是"mypackage.mymodule",但在你的主文件中,它的值会是"__main__"

如果你想要更详细的上下文信息,比如记录消息的代码部分,你可以用一个格式化字符串来设置你的格式器,比如%(funcName)s,这样就会把函数名加到所有的消息里。

如果你真的想要每个类都有自己的日志记录器,你可以这样做:

class MyClass:
    def __init__(self):
        self.logger = logging.getLogger(__name__+"."+self.__class__.__name__)
22

在你的主模块中,你正在设置一个名为 '__main__' 的日志记录器(或者在你的情况下,__name__ 代表的名字)。而在 module.py 里,你使用的是一个不同的日志记录器。你需要为每个模块单独配置日志记录器,或者你可以在主模块中配置根日志记录器(通过配置 logging.getLogger()),这样就会默认应用到你项目中的所有日志记录器。

我建议使用配置文件来设置日志记录器。这个链接可以让你了解一些好的实践: http://victorlin.me/posts/2012/08/26/good-logging-practice-in-python

补充:在你的格式化器中使用 %(module) 来在日志信息中包含模块名称。

撰写回答