python logging模块_python中logging模块下篇

0c69d488c28b58aa267e4b09d79aa7d9.png

本文承接上一篇分为如下几个部分

  • 日志输出
  • 捕获异常
  • 配置共享
  • 配置到文件
  • 使用规范
  • 参考资料

日志输出

我们之前都是将日志输出到控制台,而实际项目中常常需要将日志存储为文件,我们直接看代码

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:
    do
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>
    do
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:
    do
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')

logging.info('continue')

第一种输出异常结果会是

Sun, 01 Jul 2018 21:58:52 +0000  There are something wrong: name 'do' is not defined

后两种输出异常结果会是

Sun, 01 Jul 2018 21:58:52 +0000  There are something wrong
Traceback (most recent call last):
  File "learn.py", line 8, in <module>
    do
NameError: name 'do' is not defined

3.日志级别设置

这里列出这篇文章中认为的日志级别使用方法

  • 最细微的信息记录到debug中,这个级别就是用来debug的,看程序在哪一次迭代中发生了错误,比如每次循环都输出一些东西用debug级别
  • info级别用于routines,也就是输出start finish 状态改变等信息
  • warn输出一些相对重要,但是不是程序bug的信息,比如输入了错误的密码,或者连接较慢
  • error输出程序bug,打印异常信息
  • critical用于处理一些非常糟糕的事情,比如内存溢出、磁盘已满,这个一般较少使用

4.建议使用RotatingFileHandler而不是FileHandler

因为日志文件写入是不断增加的过程,如果用FileHandler,久了日志文件会非常大,不利于读写和管理。

用RotatingFileHandler设置日志文件大小,比如10M。则如果日志文件达到10M,就会自动将这个文件重命名,然后创建一个新的日志文件继续写入

也可以使用TimedRotatingFileHandler,这是根据时间,每隔一段时间会自动创建新的日志文件

5.get_logger位置

在一个被import的文件中get_logger的代码要放在函数里面,因为如果放在函数外面,在主文件import这个文件时,就会运行这个初始化。而在import的时候,主文件一般还没有读入yaml文件,因此事先初始化的log就不会正常工作。

详情见这篇文章

参考资料

  • 进击的coder公众号
  • CSDN博客

专栏信息

专栏主页:python编程

专栏目录:目录

版本说明:软件及包版本说明

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值