python中logging模块下篇
本文承接 上一篇 分为如下几个部分
- 日志输出
- 捕获异常
- 配置共享
- 配置到文件
- 使用规范
- 参考资料
日志输出
我们之前都是将日志输出到控制台,而实际项目中常常需要将日志存储为文件,我们直接看代码
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(message)s',
datefmt='%a, %d %b %Y %H:%M:%S +0000',
filename='my.log')
logging.info('this is a info')
可以看到,只要在
logging.basicConfig
中加入
filename
参数,就不会再在控制台中输出日志,而是会将所有日志存入
my.log
文件中。
需要注意的一点是:上面日志存储方式是追加的,也就是说,上面这个代码连续运行两次,文件中是会有两行日志的。
如果需要改变输出形式,需要调整参数如下
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(message)s',
datefmt='%a, %d %b %Y %H:%M:%S +0000',
filename='my.log', filemode='w')
logging.info('this is a info')
其他读写格式可以参考 文件读写格式 。
指定日志输出也可以不在
logging.basicConfig
中配置,而是单独设置
import logging
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
handler = logging.FileHandler('my.log')
formatter = logging.Formatter('%(asctime)s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.critical('Critical 50')
logger.error('Error 40')
logger.warning('Warning 30')
logger.info('Info 20')
logger.debug('Debug 10')
这可以达到和刚才相同的效果。我们可以看到,不仅输出方式可以单独配置,而且format也可以单独配置。
配置它们的流程是
- 配置好的format设置到handler中
- 配置好的handler添加到logger中
添加多个输出端,且不同输出端输出内容不一样(level不同,format不同)
import logging
import sys
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.DEBUG) # 设置基本level
# 输出到控制台,不设置format
handler_stream = logging.StreamHandler(sys.stdout)
handler_stream.setLevel(level=logging.WARN) # 更改level
logger.addHandler(handler_stream)
# 输出到文件,继承基础level
handler_file = logging.FileHandler('my.log', 'w')
formatter = logging.Formatter('%(asctime)s %(message)s')
handler_file.setFormatter(formatter) # 设置format
logger.addHandler(handler_file)
logger.critical('Critical 50')
logger.error('Error 40')
logger.warning('Warning 30')
logger.info('Info 20')
logger.debug('Debug 10')
控制台输出结果为
Critical 50
Error 40
Warning 30
my.log文件输出结果为
2018-07-01 21:03:27,164 Critical 50
2018-07-01 21:03:27,165 Error 40
2018-07-01 21:03:27,167 Warning 30
2018-07-01 21:03:27,171 Info 20
2018-07-01 21:03:27,172 Debug 10
这里需要注意一点,基础level要设置比较低一些,后面handler设置的level只有比基础高才有效。
logging模块还提供了许多其他的日志输出形式,详情可以见 官网
捕获异常
我们希望将程序的报错信息记录到log文件中
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(message)s',
datefmt='%a, %d %b %Y %H:%M:%S +0000',
filename='my.log')
logging.info('this is a info')
try:
except Exception:
logging.error('There are something wrong', exc_info=True)
logging.info('continue')
输出到文件的结果如下
Sun, 01 Jul 2018 21:10:43 +0000 this is a info
Sun, 01 Jul 2018 21:10:53 +0000 this is a info
Sun, 01 Jul 2018 21:10:53 +0000 There are something wrong
Traceback (most recent call last):
File "learn.py", line 9, in <module>
NameError: name 'do' is not defined
Sun, 01 Jul 2018 21:10:53 +0000 continue
其中
exc_info
的作用就是在日志中包含具体的报错信息,默认是
False
不包含。
配置共享
在写一个项目时,需要编写多个文件,每个文件内都要设置相同格式的日志输出,如果每个文件都重新配置一遍就太麻烦了,logging模块为我们提供了一个简单的方法。
比如在main.py文件中编写如下代码
import logging
import a
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(message)s',
datefmt='%a, %d %b %Y %H:%M:%S +0000')
logger = logging.getLogger('mainlogger')
logger.info('main file log')
a.run()
在a.py文件中编写如下代码
import logging
logger = logging.getLogger('mainlogger.a')
def run():
logger.info('a file log')
运行main.py文件,输出结果如下
Sun, 01 Jul 2018 21:19:57 +0000 main file log
Sun, 01 Jul 2018 21:19:57 +0000 a file log
我们可以看到,在a.py文件中只是将logger名称设置为以main.py文件中的logger名称开头,就可以继承main.py文件中的配置了,甚至不需要在a.py文件中导入main.py文件。
配置到文件
我们不仅可以通过python代码进行logging配置,而且可以通过写一个yaml文件进行配置,每次需要用logging时只要调用这个文件就配置完成。
config.yaml文件内容如下
version: 1
formatters:
simple:
format: "%(message)s"
more:
format: "%(asctime)s - %(levelname)s - %(message)s"
handlers:
console:
class : logging.StreamHandler
formatter: simple
level: INFO
stream: ext://sys.stdout
file:
class: logging.FileHandler
formatter: more
level: DEBUG
filename: debug.log
loggers:
mainlogger:
level: DEBUG
handlers: [console, file]
root:
level: DEBUG
handlers: [console]
main.py文件中编写代码如下
import logging
import logging.config
import yaml
import a
with open('config.yaml', 'r', encoding='utf-8') as f:
config = yaml.load(f)
logging.config.dictConfig(config)
logging.info('main file log')
a.run()
a.py文件中编写代码如下
import logging
logger = logging.getLogger('mainlogger')
def run():
logger.info('a file log')
运行main.py结果在控制台中输出如下
a file log
INFO:mainlogger:a file log
在debug.log文件中输出如下
2018-07-01 21:45:37,673 - INFO - a file log
对于这样的输出结果,我们回到yaml文件理一下思路,对这个文件从下往上看
-
首先对于不同的logger名称,如果名称是mainlogger,则使用
[console, file]
这两个handler,如果未指定(使用logging.info
进行输出)则对应root只按照console
输出 -
对于不同的handler,名称为console的handler使用
logging.StreamHandler
输出到控制台,调用simple
的format,而file则输出到文件,使用more
的format -
对于不同的format格式则在最上面的
formatters
中定义
通过文件配置的更多内容可以参考 官网
使用规范
1.输出字符串的规范
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(levelname)s %(message)s')
info = 'apple'
# bad
logging.info('this is a {}'.format(info))
# good
logging.info('this is a %s', info)
二者输出皆为
2018-07-01 21:56:12,969 INFO this is a apple
2.异常处理规范
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(message)s',
datefmt='%a, %d %b %Y %H:%M:%S +0000')
logging.info('this is a info')
try:
except Exception as e:
# bad
logging.error('There are something wrong: %s', e)
# good
logging.error('There are something wrong', exc_info=True)
# good
logging.exception('There are something wrong')