【Free Style】华为云之Python实践(三)
使用 __name__ 作为 logger 的名称
虽然不是非得将 logger 的名称设置为 __name__ ,但是这样做会给我们带来诸多益处。在 python 中,变量 __name__ 的名称就是当前模块的名称。比如,在模块 “foo.bar.my_module” 中调用 logger.getLogger(__name__) 等价于调用logger.getLogger(“foo.bar.my_module”) 。当你需要配置 logger 时,你可以配置到 “foo” 中,这样包 foo 中的所有模块都会使用相同的配置。当你在读日志文件的时候,你就能够明白消息到底来自于哪一个模块。
捕捉异常并使用 traceback 记录它
出问题的时候记录下来是个好习惯,但是如果没有 traceback ,那么它一点儿用也没有。你应该捕获异常并用 traceback 把它们记录下来。比如下面这个例子:
Python
1 2 3 4 5 6 | try: open('/path/to/does/not/exist', 'rb') except (SystemExit, KeyboardInterrupt): raise except Exception, e: logger.error('Failed to open file', exc_info=True) |
使用参数 exc_info=true 调用 logger 方法, traceback 会输出到 logger 中。你可以看到下面的结果:
Python
1 2 3 4 5 | ERROR:__main__:Failed to open file Traceback (most recent call last): File "example.py", line 6, in <module> open('/path/to/does/not/exist', 'rb') IOError: [Errno 2] No such file or directory: '/path/to/does/not/exist' |
你也可以调用 logger.exception(msg, _args),它等价于 logger.error(msg, exc_info=True, _args)。
千万不要在模块层次获取 Logger,除非 disable_existing_loggers 被设置为 False
你可以看到很多在模块层次获取 logger 的例子(在这篇文章我也使用了很多,但这仅仅为了让示例更短一些)。它们看上去没什么坏处,但事实上,这儿是有陷阱的 – 如果你像这样在模块中使用 Logger,Python 会保留从文件中读入配置前所有创建的所有 logger。
my_module.py
Python
1 2 3 4 5 6 7 8 9 10 | import logging
logger = logging.getLogger(__name__)
def foo(): logger.info('Hi, foo')
class Bar(object): def bar(self): logger.info('Hi, bar') |
main.py
Python
1 2 3 4 5 6 7 8 9 10 | import logging
logger = logging.getLogger(__name__)
def foo(): logger.info('Hi, foo')
class Bar(object): def bar(self): logger.info('Hi, bar') |
logging.ini
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | [loggers] keys=root
[handlers] keys=consoleHandler
[formatters] keys=simpleFormatter
[logger_root] level=DEBUG handlers=consoleHandler
[handler_consoleHandler] class=StreamHandler level=DEBUG formatter=simpleFormatter args=(sys.stdout,)
[formatter_simpleFormatter] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s datefmt= |
本应该在日志中看到记录,但是你却什么也没有看到。为什么呢?这就是因为你在模块层次创建了 logger,然后你又在加载日志配置文件之前就导入了模块。logging.fileConfig 与 logging.dictConfig 默认情况下会使得已经存在的 logger 失效。所以,这些配置信息不会应用到你的 Logger 上。你最好只在你需要 logger 的时候才获得它。反正创建或者取得 logger 的成本很低。你可以这样写你的代码:
Python
1 2 3 4 5 6 7 8 9 10 11 12 | import logging
def foo(): logger = logging.getLogger(__name__) logger.info('Hi, foo')
class Bar(object): def __init__(self, logger=None): self.logger = logger or logging.getLogger(__name__)
def bar(self): self.logger.info('Hi, bar') |
这样,logger 就会在你加载配置后才会被创建。这样配置信息就可以正常应用。
python2.7 之后的版本中 fileConfg 与 dictConfig 都新添加了 “disable_existing_loggers” 参数,将其设置为 False,上面提到的问题就可以解决了。例如:
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | import logging import logging.config
logger = logging.getLogger(__name__)
# load config from file
# logging.config.fileConfig('logging.ini', disable_existing_loggers=False)
# or, for dictConfig
logging.config.dictConfig({ 'version': 1, 'disable_existing_loggers': False, # this fixes the problem
'formatters': { 'standard': { 'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s' }, }, 'handlers': { 'default': { 'level':'INFO', 'class':'logging.StreamHandler', }, }, 'loggers': { '': { 'handlers': ['default'], 'level': 'INFO', 'propagate': True } } })
logger.info('It works!') |
- 点赞
- 收藏
- 关注作者
评论(0)