日志系统logging

BG41

Posted by Blue Geek on October 27, 2019

日志系统logging

By 青衣极客 Blue Geek In 2019-10-27

在比较大型的项目中,没有日志系统简直是无法想象的。在出现不符合预期的问题之后,一般都是需要利用日志信息来定位问题点,毕竟不太可能逐行将项目代码查看。也就是说,掌握日志系统是构建大型系统的一种基础能力。在自己平时科研或者开发的小项目中,学会使用日志系统往往也能提高排错的效率。应该没有任何一个开发者会声称自己开发的程序完全没有bug,即使是一般科研项目中用到的小规模程序也是需要经常修复一些问题,或者由于一些简单错误而导致浪费大量时间。python提供了一个简单易用的日志系统,只需要稍加配置即可使用,即使不明白各其中理,比如四大组件、过滤器、消息传播等等,仍然可以很好地利用日志来提升自己的工作。事实上,我们也确实不需要对这个日志系统的原理深入剖析,在python中,我们只需要关注自己研究的问题,至于其他的模块只需会使用就行。本文就来讨论一下python中的logging模块,希望能够对想要构建日志系统的朋友有所帮助。

1. 尝试日志

我们先以最简单的方式调用一下logging模块中的接口尝试一下日志系统的运作。在python中,获取日志对象的接口称为Logger,它也是我们打印日志的基础对象。日志的打印途径的不同决定了需要使用不同的Handler,如果是打印到控制台就使用StreamHandler,如果是打印到文件就用FileHandler,当然也可以将日志循环依次在多个文件中打印以保证每个文件大小合适。以下演示说明了使用logging模块在控制台打印日志的方式。这种日志只是演示以下logging的基本用法,并不适合在实际系统中使用。

import logging
# 创建日志接口对象
logger = logging.getLogger('Test')
logger.setLevel(logging.DEBUG)
# 创建handler
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
logger.addHandler(ch)
# 打印日志
logger.debug('debug prompt')
logger.info('info prompt')
logger.warning('warning prompt')
logger.error('error prompt')
logger.critical('critical prompt')
debug prompt
info prompt
warning prompt
error prompt
critical prompt

2. 定制日志

通常,我们需要定制日志打印的格式、日志打印位置以及不同日志级别的过滤。日志格式既Formatter使用一些关键字定义日志中需要打印的信息以及表现形式。本文摘录一些关键字的意义如下:

格式控制字 含义描述
%(filename)s 当前执行文件的名字
%(lineno)d 当前执行的行号
%(asctime)s 当前时间
%(levelno)s 当前日志级别编号
%(levelname)s 当前日志级别名字

还有更多的格式控制关键字可以使用,不过以上列出的基本够用。

一般而言,我们在打印日志时希望有些日志打印到控制到,有些日志打印到文件,这是就可以添加两个Handler,并可以分别控制每个Handler的日志显示最低级别和格式。在以下演示中,两个Handler的最低级别和格式设置为相同的。由于本文编写时使用jupyter notebook环境,因此文件名与一般项目中显示地不太一样。

import logging
# 创建日志接口对象
logger = logging.getLogger('Test')
logger.setLevel(logging.DEBUG)
# 定制日志格式
fmt = logging.Formatter('%(asctime)s %(filename)s:%(lineno)d, %(levelno)s: %(message)s')
# 创建控制台handler
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(fmt)
logger.addHandler(ch)
# 创建文件handler
fh = logging.FileHandler('../../output/py_test.log')
fh.setLevel(logging.DEBUG)
fh.setFormatter(fmt)
logger.addHandler(fh)

# 打印日志
logger.debug('debug prompt')
logger.info('info prompt')
logger.warning('warning prompt')
logger.error('error prompt')
logger.critical('critical prompt')
debug prompt
2019-10-22 22:56:53,905 <ipython-input-5-e6a2c3f9a623>:19, 10: debug prompt
info prompt
2019-10-22 22:56:53,908 <ipython-input-5-e6a2c3f9a623>:20, 20: info prompt
warning prompt
2019-10-22 22:56:53,909 <ipython-input-5-e6a2c3f9a623>:21, 30: warning prompt
error prompt
2019-10-22 22:56:53,911 <ipython-input-5-e6a2c3f9a623>:22, 40: error prompt
critical prompt
2019-10-22 22:56:53,913 <ipython-input-5-e6a2c3f9a623>:23, 50: critical prompt
!cat ../../output/py_test.log
2019-10-22 22:56:53,905 <ipython-input-5-e6a2c3f9a623>:19, 10: debug prompt
2019-10-22 22:56:53,908 <ipython-input-5-e6a2c3f9a623>:20, 20: info prompt
2019-10-22 22:56:53,909 <ipython-input-5-e6a2c3f9a623>:21, 30: warning prompt
2019-10-22 22:56:53,911 <ipython-input-5-e6a2c3f9a623>:22, 40: error prompt
2019-10-22 22:56:53,913 <ipython-input-5-e6a2c3f9a623>:23, 50: critical prompt

3. 配置方式

除了以上使用python代码直接配置logging模块之外,还可以使用文件配置和词典配置来定制化logging模块。以下给出了文件指定配置的基本格式,其主要配置项还是Logger、Formatter和Handler,还有FIlter过滤器的使用可以使控制更加精细,不过一般系统中并不需要那么精细。

!cat ../../data/test_log.conf
[loggers]
keys=root,Test

[logger_root]
level=DEBUG
handlers=hand01, hand02

[logger_Test]
handlers=hand01, hand02
level=DEBUG
qualname=Test
propagate=0

[handlers]
keys=hand01, hand02

[handler_hand01]
class=StreamHandler
level=DEBUG
formatter=form01
args=(sys.stderr,)

[handler_hand02]
class = FileHandler
level=DEBUG
formatter=form01
args=('../../output/py_file.log', 'a')

[formatters]
keys=form01

[formatter_form01]
format=%(asctime)s-%(filename)s:%(lineno)d, %(levelno)s: %(message)s

配置logging的文件编写完成之后,可以在python程序中使用logging.config.fileConfig函数来进行载入,之后就跟普通使用日志没差别了。

import logging
import logging.config
logging.config.fileConfig('../../data/test_log.conf')
logger = logging.getLogger('Test')
logger.debug('debug prompt')
logger.info('info prompt')
logger.warning('warning prompt')
logger.error('error prompt')
logger.critical('critical prompt')
2019-10-22 23:38:07,876-<ipython-input-5-e7036f305f53>:5, 10: debug prompt
2019-10-22 23:38:07,877-<ipython-input-5-e7036f305f53>:6, 20: info prompt
2019-10-22 23:38:07,878-<ipython-input-5-e7036f305f53>:7, 30: warning prompt
2019-10-22 23:38:07,879-<ipython-input-5-e7036f305f53>:8, 40: error prompt
2019-10-22 23:38:07,881-<ipython-input-5-e7036f305f53>:9, 50: critical prompt

将打印到文件中的日志展示出来如下:

!cat ../../output/py_file.log
2019-10-22 23:38:07,876-<ipython-input-5-e7036f305f53>:5, 10: debug prompt
2019-10-22 23:38:07,877-<ipython-input-5-e7036f305f53>:6, 20: info prompt
2019-10-22 23:38:07,878-<ipython-input-5-e7036f305f53>:7, 30: warning prompt
2019-10-22 23:38:07,879-<ipython-input-5-e7036f305f53>:8, 40: error prompt
2019-10-22 23:38:07,881-<ipython-input-5-e7036f305f53>:9, 50: critical prompt

除此之外,还可以使用词典的方式来配置logging模块。基本的配置项与文件配置和python代码配置是一致的,格式在以下演示中展示。

import logging
import logging.config

log_dict = {
    'version':1,
    'disable_existing_loggers': False,
    'formatters':{
        'form01':{
            'format':'%(asctime)s:%(filename)s:%(lineno)d, %(levelno)s: %(message)s'
        }
    },
    'handlers':{
        'ch':{
            'class':'logging.StreamHandler',
            'level':'DEBUG',
            'formatter':'form01',
            'stream':'ext://sys.stderr'
        },
        'fh':{
            'class':'logging.FileHandler',
            'level':'DEBUG',
            'formatter':'form01',
            'filename':'../../output/py_dict.log',
            'mode':'w+'
        }
    },
    'Test':{
        'handlers':['ch', 'fh'],
        'level':'DEBUG',
        'propagate':False
    },
    'root':{
        'handlers':['ch', 'fh'],
        'level':'DEBUG',
    }
}
logging.config.dictConfig(log_dict)
logger = logging.getLogger('Test')
logger.debug('debug prompt')
logger.info('info prompt')
logger.warning('warning prompt')
logger.error('error prompt')
logger.critical('critical prompt')
2019-10-22 23:36:20,374:<ipython-input-1-730663d2f95e>:39, 10: debug prompt
2019-10-22 23:36:20,375:<ipython-input-1-730663d2f95e>:40, 20: info prompt
2019-10-22 23:36:20,376:<ipython-input-1-730663d2f95e>:41, 30: warning prompt
2019-10-22 23:36:20,377:<ipython-input-1-730663d2f95e>:42, 40: error prompt
2019-10-22 23:36:20,378:<ipython-input-1-730663d2f95e>:43, 50: critical prompt
!cat ../../output/py_dict.log
2019-10-22 23:36:20,374:<ipython-input-1-730663d2f95e>:39, 10: debug prompt
2019-10-22 23:36:20,375:<ipython-input-1-730663d2f95e>:40, 20: info prompt
2019-10-22 23:36:20,376:<ipython-input-1-730663d2f95e>:41, 30: warning prompt
2019-10-22 23:36:20,377:<ipython-input-1-730663d2f95e>:42, 40: error prompt
2019-10-22 23:36:20,378:<ipython-input-1-730663d2f95e>:43, 50: critical prompt

到此,关于python中的日志系统就讨论完毕,全文没有关于logging机制的深入探讨,只是说明了在python中使用日志和配置日志的基本方式。阅读本文的朋友应该就已经具备了在自己的系统中使用日志系统的能力。

【青衣极客】公众号



COMMENT

博客评论区功能由Github Issue提供,提交Issue时请以本文标题为话题

"BG41-日志系统logging"