Bootstrap

logging模块详谈

1. 简介

日志是一种可以追踪软件运行时所发生异常事件的方法。开发人员可以从此获悉代码哪里发生了问题,以此定位到问题点并进行修复。Python中有一个标准库模块logging,此模块定义的函数和类为应用程序和库的开发实现了一个灵活的事件日志系统。

2. logging模块的日志级别

logging默认有以下几个日志等级

  • DEBUG —— 最详细的日志信息,典型应用场景是问题诊断
  • INFO —— 信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的进行工作
  • WARNING —— 当某些不期望的事情发生时记录信息,但此时应用程序能正常运行的
  • ERROR —— 一个严重问题导致某些功能不能正常运行的信息
  • CRITICAL —— 发生严重错误时,导致应用不能继续运行的信息

日志的等级从上到下依次升高:DEBUG < INFO > WARNING > ERROR > CRITICAL,而日志的信息是依次减少的。当logging模块指定日志记录器的日志级别后,只有级别大于或等于该日志级别的日志记录才会被输出。

示例:

import logging

logging.debug("桃乃木香奈")
logging.info("枫花恋")
logging.warning("三上悠亚")
logging.error("桥本有菜")
logging.critical("神木丽")

输出结果:

WARNING:root:三上悠亚
ERROR:root:桥本有菜
CRITICAL:root:神木丽

以上的日志输出只有warning、error、critical的,是因为默认的日志级别是logging.warning

日志输出信息字段含义

上面输出结果的日志信息中有三个字段,含义分别是:

日志级别:日志器名称:日志内容

这样的输出格式是由日志记录函数所使用的日志器设置的日志格式默认为BASIC_FORMAT决定的:
%(levelname)s:%(name)s:%(message)
还可以设置其他的样式:
%(asctime)s - %(levelname)-10s - %(filename)s - %(funcName)s:%(lineno)d - %(message)s
以上的设置可以在logging.basicConifg()format参数中进行设置。该函数可接收的关键字参数如下:

  • filename:指定日志输出目标文件的文件名,指定该设置项后日志信心就不会被输出到控制台了
  • filemode:指定日志文件的打开模式,默认为’a’。需要注意的是,该选项要在filename指定时才有效
  • format:指定日志格式字符串,即指定日志输出时所包含的字段信息以及它们的顺序。logging模块定义的格式字段下面会列出
  • datefmt:指定日期/时间格式。需要注意的是,该选项要在format中包含时间字段%(asctime)s时才有效
  • level:指定日志器的日志级别
  • stream:指定日志输出目标stream,如sys.stdout、sys.stderr以及网络stream。需要说明的是,stream和filename不能同时提供,否则会引发 ValueError异常
  • style:Python 3.2中新添加的配置项。指定format格式字符串的风格,可取值为’%‘、’{‘和’$‘,默认为’%’
  • handlers:Python 3.3中新添加的配置项。该选项如果被指定,它应该是一个创建了多个Handler的可迭代对象,这些handler将会被添加到root logger。需要说明的是:filename、stream和handlers这三个配置项只能有一个存在,不能同时出现2个或3个,否则会引发ValueError异常
配置日志器级别

import logging

logging.basicConfig(level=logging.DEBUG)
logging.debug("桃乃木香奈")
logging.info("枫花恋")
logging.warning("三上悠亚")
logging.error("桥本有菜")
logging.critical("神木丽")

输出结果:

DEBUG:root:桃乃木香奈
INFO:root:枫花恋
WARNING:root:三上悠亚
ERROR:root:桥本有菜
CRITICAL:root:神木丽

所有的日志都输出了,说明以上代码的日志级别设置生效了。

配置日志输出目标文件及日志格式
import logging

log_format = '%(asctime)s - %(levelname)s: %(message)s'
logging.basicConfig(filename= 'mylog.log',level=logging.DEBUG,format=log_format)
logging.debug("桃乃木香奈")
logging.info("枫花恋")
logging.warning("三上悠亚")
logging.error("桥本有菜")
logging.critical("神木丽")

此时会发现控制台已经没有日志输出了,但根目录下生成了一个mylog.log的文件,文件内容如下:

2023-01-17 15:03:53,409 - DEBUG: 桃乃木香奈
2023-01-17 15:03:53,409 - INFO: 枫花恋
2023-01-17 15:03:53,409 - WARNING: 三上悠亚
2023-01-17 15:03:53,409 - ERROR: 桥本有菜
2023-01-17 15:03:53,410 - CRITICAL: 神木丽

继续,我们接着上面代码的基础,将日期时间格式设置一下:

import logging

log_format = '%(asctime)s - %(levelname)s: %(message)s'
date_format = '%m/%d/%Y %H:%M:%S %p'
logging.basicConfig(filename='D:\PyProject\loggingPY\logs\mylog.log',level=logging.DEBUG,format=log_format,encoding='utf-8',datefmt=date_format)
logging.debug("桃乃木香奈")
logging.info("枫花恋")
logging.warning("三上悠亚")
logging.error("桥本有菜")
logging.critical("神木丽")

文件内容:

01/17/2023 15:16:31 PM - DEBUG: 桃乃木香奈
01/17/2023 15:16:31 PM - INFO: 枫花恋
01/17/2023 15:16:31 PM - WARNING: 三上悠亚
01/17/2023 15:16:31 PM - ERROR: 桥本有菜
01/17/2023 15:16:31 PM - CRITICAL: 神木丽

3. logging日志模块四大组件

  • 日志器(Logger):提供了应用程序可一直使用的接口
  • 处理器(Handler):将looger创建的日志记录发送到合适的输出
  • 过滤器(Filter):提供了更细粒度的控制工具来决定输出那条日志记录
  • 格式器(Formatter):决定日志记录的最终输出格式

logging模块通过以上组件来完成日志的处理,其中日志器是入口,实际做处理的是处理器,处理器可以通过过滤器以及格式器对要输出的日志内容做过滤和格式化等处理操作。

3.1 组件相关类

四大组件相关的类:

  • Logger
  • Handler
  • Filter
  • Formatter
Logger类

常用配置方法:

  • Logger.setLevel():设置日志器将会处理的日志消息的最低严重级别
  • Logger.addHandler()Logger.removeHandler():为该logger对象添加和移除一个handler对象
  • Logger.addFilterLogger.removeFilter():为该logger对象添加和移除一个filter对象

创建日志记录方法:

  • Logger.debug():创建一个与它们的方法名对应等级的日志记录
  • Logger.info():创建一个与它们的方法名对应等级的日志记录
  • Logger.warning():创建一个与它们的方法名对应等级的日志记录
  • Logger.error():创建一个与它们的方法名对应等级的日志记录
  • Logger.critical():创建一个与它们的方法名对应等级的日志记录
  • Logger.exception():创建一个类似于Logger.error()的日志消息
  • Logger.log():需要获取一个明确的日志level参数来创建一个日志记录

获取Logger对象
方法一:通过Logger类实例化
方法二:logging.getLogger()方法获得。(此方法有一个可选参数name,该参数表示将要返回的日志器的名称标识,如果不提供则值为’root’)

Handler类

Handler对象是基于日志消息的级别将消息分发到handler指定的位置(文件、控制台、网络、邮件)。Logger对象可以通过addHandler()方法为自己添加多个handler对象。而多个handler对象可以用于发送不同级别的日志到不同位置。
handler配置方法:

  • Handler.setLevel():设置handler将会处理的日志消息的最低严重级别
  • Handler.setFormatter():为handler设置一个格式器对象
  • Handler.addFilter()和Handler.removeFilter():为handler添加或删除一个过滤器对象

子类可使用或覆盖的Handler行为(由于Handler是基类,所以不建议直接实例化Handler):

  • logging.StreamHandler: 将日志消息发送到输出到Stream,如std.out, std.err或任何file-like对象。
  • logging.FileHandler: 将日志消息发送到磁盘文件,默认情况下文件大小会无限增长
  • logging.handlers.RotatingFileHandler: 将日志消息发送到磁盘文件,并支持日志文件按大小切割
  • logging.hanlders.TimedRotatingFileHandler: 将日志消息发送到磁盘文件,并支持日志文件按时间切割
  • logging.handlers.HTTPHandler: 将日志消息以GET或POST的方式发送给一个HTTP服务器
  • logging.handlers.SMTPHandler: 将日志消息发送给一个指定的email地址
  • logging.NullHandler: 该Handler实例会忽略error messages,通常被想使用logging的library开发者使用来避免‘No handlers could be found for logger XXX’信息的出现。
Formatter类

Formater对象主要用于配置日志信息的最终顺序、结构以及内容。Formatter类可以直接实例化,但大多数情况下我们也还是会用logging.Formatter的方式去使用。

logging.Formatter.__init__(fmt=None, datefmt=None, style='%')

以上是Formatter类的构造方法,其由有3个参数:

  • fmt:指定消息格式化字符串,如果不指定该参数则默认使用 message 的原始值
  • datefmt:指定日期格式字符串,如果不指定该参数则默认使用 “%Y-%m-%d %H:%M:%S”
  • style:Python 3.2 新增的参数,可取值为 ‘%’, '{'和 ‘$’,如果不指定该参数则默认使用 ‘%’
Filter类

Filter可以被Handler和Logger用来做比level更细粒度的、更复杂的过滤功能。 Filter是一个过滤器基类,它只允许某个logger层级下的日志事件通过过滤。 该类定义如下:

class logging.Filter(name=''):
    filter(record)

比如,一个filter实例化时传递的name参数值为’A.B’,那么该filter实例将只允许名称为类似如下规则的 loggers 产生的日志记录通过过滤:‘A.B’,‘A.B.C’,‘A.B.C.D’,‘A.B.D’,而名称为’A.BB’, 'B.A.B’的loggers产生的日志则会被过滤掉。如果name的值为空字符串,则允许所有的日志事件通过过滤。
filter方法用于具体控制传递的record记录是否能通过过滤,如果该方法返回值为0表示不能通过过滤,返回值为非0表示可以通过过滤。

综合案例

实现不同级别日志写入不同文件

import logging
import logging.handlers
import datetime

# 创建logger
logger = logging.getLogger("MyLogger")

# 设置日志级别
logger.setLevel(logging.DEBUG)

f_handler = logging.handlers.TimedRotatingFileHandler(r'loggingPY\logs\full.log',when='midnight',interval=2,backupCount=7,atTime=datetime.time(0,0,0,0))
f_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))


e_handler = logging.FileHandler(r'loggingPY\logs\error.log')
e_handler.setLevel(logging.ERROR)
e_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(filename)s[:%(lineno)d] - %(message)s"))


# 添加到handler中
logger.addHandler(f_handler)
logger.addHandler(e_handler)


logger.debug('debug message')
logger.info('info message')
logger.warning('warning message')
logger.error('error message')
logger.critical('critical message')

TimedRotatingFileHandler函数,用于创建固定时间间隔的日志,以下为参数解释:

  • filename:输出日志的文件名
  • when:是一个字符串,定义了日志切分的间隔时间单位,这是一个枚举类,可选参数如下:
    • “S”:Second 秒
    • “M”:Minutes 分钟
    • “H”:Hour 小时
    • “D”:Days 天
    • “W”:Week day(0 = Monday)
    • “midnight”:Roll over at midnight
  • interval:是间隔时间单位的个数,指等待多少个 when 的时间后 Logger 会自动重建新闻继续进行日志记录。这里需要注意的一点是,如果创建的文件和已有文件存在重名的情况,则会对历史的文件进行覆盖操作,所以使用好 suffix 避免文件名称重复
  • backupCount:是保留日志的文件个数
    默认的参数是0,这种设置下是不会自动删除文件的。如果设置为 N(正整数),则会在创建新的日志文件时会检查日志文件个数是否到达 N,达到了的话就会从最先创建的开始删除,从而达到维持日志文件个数为 N 个的目标。
;