Python模块之logging

作为一个程序员,个人认为一个好的日志记录习惯是能称得上优秀所必备的素养之一。而且随着在行业内阅历的增长,这个好习惯在心目中的比重越来越高。

0. 日志的重要性

为了凸显log的重要程度,笔者不惜打乱已有的行文风格——在开始本篇标题标识的内容前,用一个比较典型的场景来说明下日志为何如此重要。

碰到QA提的一个bug的时候,笔者见识过两种方式的答复:
a) 请给我重现步骤和重现数据;
b) 把当时的日志给我。

答复前者的,一般需要花很多时间去找问题出现在那里,如果是别人开发的模块的话,花费的时间更多。而答复后者的,一般能很快的找到出问题的点,然后就可以开始进入修复的流程。(希望读者你已经是,或者有志并愿意努力成为后者)

从概念上来说:日志是一个可运行的、可维护的软件的基础组成部分;通过日志,我们可以了解软件系统在运行中的实时状态,历史状态和异常状态等。一个没有良好日志的软件是所有人的噩梦

1. 概述

相比较Java,.NET等语言庞大的日志系统,第三方框架层出不穷的现象,python在这一方面表现得倒是出奇地统一,网上找到的资料讨论的都是Python内置的logging模块,这真是选择强迫症的救星。本文也无法免俗,本篇博客没有啥原理性和深入性的知识的探索,仅仅是记录在使用过程中的一些问题,提供一份快速上手的Demo Code。

2. 实现

2.1 配置文件

以下配置存储为 ogger_config.conf 文件,注意该配置文件中,笔者尽量减少了高级知识的引入,以期最大限度地减少初学者的心理和生理压力,读者可以在完成初步探索之后,在需求的驱动下自行进行扩展,学习是一个循序渐进的过程,慢慢来,不要着急。

在下面的配置中,我们只引入了一个logger,我们在程序中打印的日志都是通过它来进行输出的,并且提供了两种handler(console,file)。

[loggers]
keys=root

[logger_root]
level=DEBUG
# ,filehandler
handlers=consoleHandler

###############################################
[handlers]
keys=consoleHandler,filehandler

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[handler_filehandler]
class=FileHandler
level=DEBUG
formatter=simpleFormatter
args=('./ogs/debug.log', 'a')

###############################################
[formatters]
keys=simpleFormatter

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=%Y-%m-%d %H:%M:%S

2.2 代码实现
import logging
import logging.config
from os import path

def _init_logger():
    log_file_path = path.join(path.dirname(path.abspath(__file__)), 'logger_config.conf')
    logging.config.fileConfig(log_file_path)

def _log_test():
    logger = logging.getLogger(__name__)
    logger.debug("LQ")

if __name__ == '__main__':
	_init_logger()
	_log_test()

3. 最佳实践

任何一门知识点必然有一些最佳实践,提前知晓它们并努力去适应永远是一个利远大于弊的选择。

3.1 使用 __name__ 作为日志名称(logger name)

这里先举个例子,假设你在foo.bar.my_module模块中调用logger.getLogger(__name__),即是调用了logger.getLogger("foo.bar.my_module");这样做的好处至少有两个:

  1. 这样当你需要为logger添加配置时,你就只需要给"foo"添加配置,然后所有在"foo"里的模块都能共享相同的配置信息。
  2. 你在查看日志时,可以非常清晰地了解到某条日志信息是从哪个模块里输出的。
3.2 记录异常时记得带上堆栈信息

这一条不仅仅适用于Python;只要是有日志记录功能的地方,这一条都可以算得上是铁律了;我们这里重点看下Python中如何实现这一点:

    try:
        open('/path/to/does/not/exist', 'rb')
    except (SystemExit, KeyboardInterrupt):
        raise
    except Exception as e:
    	# 通过将exc_info参数设置为True调用logger的相关方法,traceback将会被记录进logger
        # 当然你也可以使用 logger.exception(msg, *args)方法
        logger.error('Failed to open file', exc_info=True)
3.3 在需要的时候实例化logger
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')

4. 总结

Python中的logging是被作为标准库的一部分的,灵活性也非常高。而且正如Python一直所宣称的,logging的使用上也是相当地简单。

但是log的难点永远不是在其使用上,如何制定符合业务发展需要并且被广大开发人员接受并执行的日志规范,以及后期日志信息的提取和分析才是有关log话题的精华所在。

5. Links

  1. 最佳实践 – 2013年的时候找到的,没想到还有再次打开的一天

你可能感兴趣的:(Python3)