日志logging
一、日志概述
日志作用
不管是在项目开发还是测试过程中,项目运行一旦出现问题日志信息就非常重要了。日志是定位问题的重要手段,就像侦探人员要根据现场留下的线索来推断案情。
总的来说,日志的作用:
1、程序调试
2、了解软件运行的情况,看软件是否正常
3、软件的故障分析和问题定位
二、logging模块简介
1. logging模块的日志级别
logging模块默认定义了以下几个日志等级,它允许开发人员自定义其他日志级别,但是这是不被推荐的,尤其是在开发供别人使用的库时,因为这会导致日志级别的混乱。
| 日志等级(level) | 描述 |
|---|---|
| DEBUG | 最详细的日志信息,典型应用场景是 问题诊断 |
| INFO | 信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作 |
| WARNING | 当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的 |
| ERROR | 由于一个更严重的问题导致某些功能不能正常运行时记录的信息 |
| CRITICAL | 当发生严重错误,导致应用程序不能继续运行时记录的信息 |
上述日志等级从上往下依次升高,即DEBUG<NFO<WARNING<ERROR<CRITICAL,一般在应用过程中,DEBUG和INFO级别的一般在软件的开发和调试过程中使用,在应用软件上线,用户使用中
用WARNING,ERROR,CRITICAL这三个级别,这样可以减少日志的输出,提高从日志查找定位原因的效率
三、使用logging提供的模块级别输出日志
1.最简单的日志输出
#!/usr/bin/env python
# encoding: utf-8
#@author: 张新礼
import logging
logging.debug('用来调试代码')
logging.info('用来记录节点')
logging.warning('可能将会发生问题')
logging.error('局部有问题')
logging.critical('我要停止运行了')
输入结果:
WARNING:root:可能将会发生问题
ERROR:root:局部有问题
CRITICAL:root:我要停止运行了
从上边的代码可能有一下几个问题
问题1:为什么前两条日志没有被打印出来?
这是因为logging模块提供的日志记录函数默认日志级别是WARNING,因此只有等于和大于WARNING级别的日志记录被输出了,而小于日志记录被丢弃了。
问题2:打印出来的日志信息中各字段表示什么意思?
上边输出的日志字段代表的意思
日志级别:日志器:日志信息
问题3:为什么这样输出?
因为logging模块提供的日志记录函数所使用的日志器设置的日志格式默认是BASIC_FORMAT,其值为:

问题4:我想输入特定格式的日志信息该怎么修改?
其实很简单,在我们调用上面这些日志记录函数之前,手动调用一下basicConfig()方法,把我们想设置的内容以参数的形式传递进去就可以了。
2. logging.basicConfig()函数说明
该方法用于为logging日志系统做一些基本配置,方法定义如下:
logging.basicConfig(**kwargs)
该函数支持的参数如下:
| 参数名称 | 描述 |
|---|---|
| filename | 指定日志输出目标文件的文件名,指定该设置项后日志信心就不会被输出到控制台了 |
| filemode | 指定日志文件的打开模式,默认为'a'。需要注意的是,该选项要在filename指定时才有效 |
| format | 指定日志格式字符串,即指定日志输出时所包含的字段信息以及它们的顺序。logging模块定义的格式字段下面会列出。 |
| datefmt | 指定日期/时间格式。需要注意的是,该选项要在format中包含时间字段%(asctime)s时才有效 |
| level | 指定日志器的日志级别 |
| stream | 指定日志输出目标stream,如sys.stdout、sys.stderr以及网络stream。需要说明的是,stream和filename不能同时提供,否则会引发 ValueError异常 |
对与logging模块中定义好的可以用于format格式字符串中字段有哪些
|
格式 |
描述 |
|
%(levelno)s |
打印日志级别的数值 |
|
%(levelname)s |
打印日志级别名称 |
|
%(pathname)s |
打印当前执行程序的路径 |
|
%(filename)s |
打印当前执行程序名称 |
|
%(funcName)s |
打印日志的当前函数 |
|
%(lineno)d |
打印日志的当前行号 |
|
%(asctime)s |
打印日志的时间 |
|
%(thread)d |
打印线程id |
|
%(threadName)s |
打印线程名称 |
|
%(process)d |
打印进程ID |
|
%(message)s |
打印日志信息 |
3.经过配置的日志输出
先简单配置下日志器的日志级别
#!/usr/bin/env python # encoding: utf-8 #@author: 张新礼 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:我要停止运行了
所有等级的日志信息都被输出了,说明配置生效了。
在配置日志器日志级别的基础上,在配置下日志输出目标文件和日志格式
#!/usr/bin/env python # encoding: utf-8 #@author: 张新礼 import logging logging.basicConfig(level=logging.DEBUG,filename='runlog.log', format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s') logging.debug('用来调试代码') logging.info('用来记录节点') logging.warning('可能将会发生问题') logging.error('局部有问题') logging.critical('我要停止运行了')
此时会发现控制台中已经没有输出日志内容了,输出结果会在runlog.log的文件里
2022-04-19 17:35:46,504 log01.py[line:9] DEBUG 用来调试代码 2022-04-19 17:35:46,504 log01.py[line:10] INFO 用来记录节点 2022-04-19 17:35:46,504 log01.py[line:11] WARNING 可能将会发生问题 2022-04-19 17:35:46,504 log01.py[line:12] ERROR 局部有问题 2022-04-19 17:35:46,504 log01.py[line:13] CRITICAL 我要停止运行了
特别说明:
logging.basicConfig()函数是一个一次性的简单配置工具使,也就是说只有在第一次调用该函数时会起作用,后续再次调用该函数时完全不会产生任何操作的,多次调用的设置并不是累加操作。- 日志器(Logger)是有层级关系的,上面调用的logging模块级别的函数所使用的日志器是
RootLogger类的实例,其名称为'root',它是处于日志器层级关系最顶层的日志器,且该实例是以单例模式存在的。
四、logging模块日志流处理流程
在介绍logging模块的高级用法之前,很有必要对logging模块所包含的重要组件以及其工作流程做个全面、简要的介绍,这有助于我们更好的理解我们所写的代码。
1. logging日志模块四大组件
在介绍logging模块的日志流处理流程之前,我们先来介绍下logging模块的四大组件:
| 组件名称 | 对应类名 | 功能描述 |
|---|---|---|
| 日志器 | Logger | 提供了应用程序可一直使用的接口 |
| 处理器 | Handler | 将logger创建的日志记录发送到合适的目的输出 |
| 过滤器 | Filter | 提供了更细粒度的控制工具来决定输出哪条日志记录,丢弃哪条日志记录 |
| 格式器 | Formatter | 决定日志记录的最终输出格式 |
logging模块就是通过这些组件来完成日志处理的,上面所使用的logging模块级别的函数也是通过这些组件对应的类来实现的。
这些组件之间的关系描述:
- 日志器(logger)需要通过处理器(handler)将日志信息输出到目标位置,如:文件、sys.stdout、网络等;
- 不同的处理器(handler)可以将日志输出到不同的位置;
- 日志器(logger)可以设置多个处理器(handler)将同一条日志记录输出到不同的位置;
- 每个处理器(handler)都可以设置自己的过滤器(filter)实现日志过滤,从而只保留感兴趣的日志;
- 每个处理器(handler)都可以设置自己的格式器(formatter)实现同一条日志以不同的格式输出到不同的地方。
简单点说就是:日志器(logger)是入口,真正干活儿的是处理器(handler),处理器(handler)还可以通过过滤器(filter)和格式器(formatter)对要输出的日志内容做过滤和格式化等处理操作。
2. logging日志模块相关类及其常用方法介绍
下面介绍下与logging四大组件相关的类:Logger, Handler, Filter, Formatter。
Logger类
Logger对象有3个任务要做:
- 1)向应用程序代码暴露几个方法,使应用程序可以在运行时记录日志消息;
- 2)基于日志严重等级(默认的过滤设施)或filter对象来决定要对哪些日志进行后续处理;
- 3)将日志消息传送给所有感兴趣的日志handlers。
Logger对象最常用的方法分为两类:配置方法 和 消息发送方法
最常用的配置方法如下:
| 方法 | 描述 |
|---|---|
| Logger.setLevel() | 设置日志器将会处理的日志消息的最低严重级别 |
| Logger.addHandler() 和 Logger.removeHandler() | 为该logger对象添加 和 移除一个handler对象 |
| Logger.addFilter() 和 Logger.removeFilter() | 为该logger对象添加 和 移除一个filter对象 |
关于Logger.setLevel()方法的说明:
内建等级中,级别最低的是DEBUG,级别最高的是CRITICAL。例如setLevel(logging.INFO),此时函数参数为INFO,那么该logger将只会处
理INFO、WARNING、ERROR和CRITICAL级别的日志,而DEBUG级别的消息将会被忽略/丢弃。
Handler类
Handler对象的作用是(基于日志消息的level)将消息分发到handler指定的位置(文件、网络、邮件等)。Logger对象可以通过addHandler()方法为自己添加0个或者更多个handler对象。比如,一个应用程序可能想要实现以下几个日志需求:
- 1)把所有日志都发送到一个日志文件中;
- 2)把所有严重级别大于等于error的日志发送到stdout(标准输出);
- 3)把所有严重级别为critical的日志发送到一个email邮件地址。
这种场景就需要3个不同的handlers,每个handler复杂发送一个特定严重级别的日志到一个特定的位置。一个handler中只有非常少数的方法是需要应用开发人员去关心的。对于使用内建handler对象的应用开发人员来说,似乎唯一相关的handler方法就是下面这几个配置方法:
方法 描述 Handler.setLevel() 设置handler将会处理的日志消息的最低严重级别 Handler.setFormatter() 为handler设置一个格式器对象 Handler.addFilter() 和 Handler.removeFilter() 为handler添加 和 删除一个过滤器对象 需要说明的是,应用程序代码不应该直接实例化和使用Handler实例。因为Handler是一个基类,它只定义了素有handlers都应该有的接口,同时提供了一些子类可以直接使用或覆盖的默认行为。下面是一些常用的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'信息的出现。 Formater类
Formater对象用于配置日志信息的最终顺序、结构和内容。与logging.Handler基类不同的是,应用代码可以直接实例化Formatter类。另外,如果你的应用程序需要一些特殊的处理行为,也可以实现一个Formatter的子类来完成。
Formatter类的构造方法定义如下:
logging.Formatter.__init__(fmt=None, datefmt=None, style='%')可见,该构造方法接收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表示可以通过过滤。
说明:
- 如果有需要,也可以在filter(record)方法内部改变该record,比如添加、删除或修改一些属性。
- 我们还可以通过filter做一些统计工作,比如可以计算下被一个特殊的logger或handler所处理的record数量等。
3. logging日志流处理流程
简单的日志流处理流程,复杂的自己学

五、配置日志的几种方式
我们可以通过以下3种方式来配置logging:
●1)使用Python代码显式的创建loggers, handlers和formatters并分别调用它们的配置函数;
●2)创建一个日志配置文件,然后使用fileConfig()函数来读取该文件的内容;
●3)创建一个包含配置信息的dict,然后把它传递个dictConfig()函数,一般这个方式不常用
需要说明的是,logging.basicConfig()也属于第一种方式,它只是对loggers, handlers和formatters的配置函数进行了封装。另外,第二种配置方式相对于第一种配置方式的优点在于,它将配置信息和代码进行了分离,这一方面降低了日志的维护成本,同时还使得非开发人员也能够去很容易地修改日志配置。
1、使用Python代码实现日志配置
需求
现在有以下几个日志记录的需求:
●1)要求将所有级别的所有日志都控制台输出
●2)控制台记录所有的日志信息,日志格式为:日期和时间 - 日志器名称 - 文件名 - 日志级别 - 日志信息
●3)runlog.log文件中单独记录warning及以上级别的日志信息,日志格式为:日期和时间 - 日志器名称 - 日志级别 - 文件名[:行号] - 日志信息
浙公网安备 33010602011771号