title: 【logging】Python多层级日志输出
tags: python,日志
updatedAt: 2024-06-08T19:41:21.291Z
createdAt: 2024-06-08T19:41:21.291Z
【logging】Python 多层级日志输出
1. 简介
在应用的开发过程中,我们常常需要去记录应用的状态,事件,结果。而 Python 最基础的 Print 很难满足我们的需求,这种情况下我们就需要使用 python 的另一个标准库:logging。
这是一个专门用于记录日志的模块。相对于 Print 来说,logging提供了日志信息的分级,格式化,过滤等功能。如果在程序中定义了丰富而有条理的 log 信息,那么可以非常方便的去分析程序的运行状况,在有问题时也能够方便的去定位问题,分析问题。
以下是具体的一些应用场景。
| 执行的任务 | 这项任务的最佳工具 |
|---|---|
| 显示控制台输出 | print() |
| 报告在程序正常运行期间发生的事件 | logging.info()或 logging.debug() |
| 发出有关特定运行时事件的警告 | logging.warning() |
| 报告有关特定运行时事件的错误 | 抛出异常 |
| 报告错误但不抛出异常 | logging.error(), logging.exception()或 logging.critical() |
2. 基础用法
以下是一些logging最基础的使用方法,如果不需要深入的去定制 log 的话,那么只需要使用最基础的部分即可。
In [1]: import logging
In [2]: logging.info('hello world')
In [3]: logging.warning('good luck')
WARNING:root:good luck可以看到,logging.info()的日志信息没有被输出,而logging.warning()的日志信息被输出了,这就是因为logging的日志信息分为几个不同的重要性级别,而默认输出的级别则是warning,也就是说,重要性大于等于warning的信息才会被输出。
以下是logging模块中信息的五个级别,重要性从上往下递增。
| 等级 | 什么时候使用 |
|---|---|
DEBUG | 详细信息,通常仅在 Debug 时使用。 |
INFO | 程序正常运行时输出的信息。 |
WARNING | 表示有些预期之外的情况发生,或者在将来可能发生什么情况。程序依然能按照预期运行。 |
ERROR | 因为一些严重的问题,程序的某些功能无法使用了。 |
CRITICAL | 发生了严重的错误,程序已经无法运行。 |
我们也可以通过设置来设定输出日志的级别:
In [1]: import logging
In [2]: logging.basicConfig(level=logging.DEBUG)
In [3]: logging.info('hello world')
INFO:root:hello world可以看到,在设定了level参数为logging.DEBUG后,logging.info()的日志信息就正常输出了。
2.1. basicConfig
logging.basicConfig(**kwargs)通过basicConfig()方法可以为logging做一些简单的配置。此方法可以传递一些关键字参数。
filename
文件名参数,如果指定了这个参数,那么
logging会把日志信息输入到指定的文件之中。import logging logging.basicConfig(filename='example.log') logging.warning('Hello world')filemode
如果指定了
filename来输出日志到文件,那么filemode就是打开文件的模式,默认为'a',追加模式。当然也可以设置为'w',则每一次输入都会丢弃掉之前日志文件中的内容。format
指定输出的 log 信息的格式。
In [1]: import logging In [2]: logging.basicConfig(format='%(asctime)s %(message)s') In [3]: logging.warning('hello world') 2018-07-06 16:28:12,074 hello worlddatefmt
如果在
format中使用了asctime输出时间,那么可以使用此参数控制输出日期的格式,使用方式与time.strftime()相同。level
设置输出的日志的级别,只有高出此级别的日志信息才会被输出。
In [1]: import logging In [2]: logging.basicConfig(level=logging.INFO) In [3]: logging.info('hi') INFO:root:hi In [4]: logging.debug('byebye')
注:需要注意的是,basicConfig()方法是一个一次性的方法,只能用来做简单的配置,多次的调用basicConfig()是无效的。
3. 模块化定制 logging
在深度使用logging来定制日志信息之前,我们需要先来了解一下logging的结构。logging的主要逻辑结构主要由以下几个组件构成:
- Logger:提供应用程序直接使用的接口。
- Handler:将 log 信息发送到目标位置。
- Filter:提供更加细粒度的 log 信息过滤。
- Formatter:格式化 log 信息。
这四个组件是logging模块的基础,在基础用法中的使用方式,其实也是这四大组件的封装结果。
这四个组件的关系如下所示:

image.png
logger主要为外部提供使用的 api 接口,而每个logger下可以设置多个Handler,来将 log 信息输出到多个位置,而每一个Handler下又可以设置一个Formatter和多个Filter来定制输出的信息。
3.1. Logger
Logger这个对象主要有三个任务要做:
- 向外部提供使用接口。
- 基于日志严重等级(默认的过滤组件)或 filter 对象来决定要对哪些日志进行后续处理。
- 将日志消息传送给所有符合输出级别的
Handlers。
logging.getLogger(name=None)
首先,我们需要通过getLogger()方法来生成一个Logger,这个方法中有一个参数 name,则是生成的Logger的名称,如果不传或者传入一个空值的话,Logger的名称默认为 root。
In [1]: import logging
In [2]: logger = logging.getLogger('nanbei')需要注意的是,只要在同一个解释器的进程中,那么相同的Logger名称,使用getLogger()方法将会指向同一个Logger对象。
而使用logger的一个好习惯,是生成一个模块级别的Logger对象:
In [1]: logger = logging.getLogger(__name__)通过这种方式,我们可以让logger清楚的记录下事件发生的模块位置。
除此之外,logger对象是有层级结构的:
Logger的名称可以是一个以.分割的层级结构,每个.后面的Logger都是.前面的logger的子辈。例如,有一个名称为nanbei的
logger,其它名称分别为nanbei.a,nanbei.b和nanbei.a.c都是nanbei的后代。子
Logger在完成对日志消息的处理后,默认会将 log 日志消息传递给它们的父辈Logger相关的Handler。因此,我们不不需要去配置每一个的
Logger,只需要将程序中一个顶层的Logger配置好,然后按照需要创建子Logger就好了。也可以通过将一个logger的propagate属性设置为 False 来关闭这种传递机制。
例如:
In [1]: import logging
# 生成一个名称为nanbei的Logger
In [2]: logger = logging.getLogger('nanbei')
# 生成一个StreamHandler,这个Handler可以将日志输出到console中
In [3]: sh = logging.StreamHandler()
# 生成一个Formatter对象,使输出日志时只显示Logger名称和日志信息
In [4]: fmt = logging.Formatter(fmt='%(name)s - %(message)s')
# 设置Formatter到StreamHandler中
In [5]: sh.setFormatter(fmt)
# 将Handler添加到Logger中
In [6]: logger.addHandler(sh)
# 生成一个nanbei的子Logger:nanbei.child
In [7]: child_logger = logging.getLogger('nanbei.child')
# 可以看到两个Logger输出的日志信息都使用了相同的日志格式
In [8]: logger.warning('hello')
nanbei - hello
In [9]: child_logger.warning('hello')
nanbei.child - hello在Logger对象中,主要提供了以下方法:
| 方法 | 描述 |
|---|---|
| Logger.setLevel() | 设置日志器将会处理的日志消息的最低输出级别 |
| Logger.addHandler() 和 Logger.removeHandler() | 为该 logger 对象添加、移除一个 handler 对象 |
| Logger.addFilter() 和 Logger.removeFilter() | 为该 logger 对象添加、移除一个 filter 对象 |
| Logger.debug(),Logger.info(),Logger.warning(),Logger.error(),Logger.critical() | 输出一条与方法名对应等级的日志 |
| Logger.exception() | 输出一条与 Logger.error()类似的日志,包含异常信息 |
| Logger.log() | 可以传入一个明确的日志 level 参数来输出一条日志 |
3.2. Handler
Handler的作用主要是把 log 信息输出到我们希望的目标位置,其提供了如下的方法以供使用:
| 方法 | 描述 |
|---|---|
| Handler.setLevel() | 设置 handler 处理日志消息的最低级别 |
| Handler.setFormatter() | 为 handler 设置一个格式器对象 |
| Handler.addFilter() 和 Handler.removeFilter() | 为 handler 添加、删除一个过滤器对象 |
我们可以通过这几个方法,给每一个Handler设置一个Formatter和多个Filter,来定制不同的输出 log 信息的策略。
而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'信息的出现。 |
3.3. Filter
Filter可以被Handler和Logger用来做比 level 分级更细粒度的、更复杂的过滤功能。
Filter是一个过滤器基类,它可以通过 name 参数,来使这个logger下的日志通过过滤。
class logging.Filter(name='')比如,一个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 的值为空字符串,则允许所有的日志事件通过过滤。
In [1]: import logging
In [2]: logger = logging.getLogger('nanbei')
In [3]: filt = logging.Filter(name='nanbei.a')
In [4]: sh = logging.StreamHandler()
In [5]: sh.setLevel(logging.DEBUG)
In [6]: sh.addFilter(filt)
In [7]: logger.addHandler(sh)
In [8]: logging.getLogger('nanbei.a.b').warning('i am nanbei.a.b')
i am nanbei.a.b
In [9]: logging.getLogger('nanbei.b.b').warning('i am nanbei.a.b')可以看到,名称为nanbei.b.b的Logger的日志没有被输出。
3.4. Formatter
Formater对象用于配置日志信息的最终顺序、结构和内容。
Formatter类的构造方法定义如下:
logging.Formatter.__init__(fmt=None, datefmt=None, style='%')fmt
这个参数主要用于格式化 log 信息整体的输出。
以下是可以用来格式化的字段:
字段/属性名称 使用格式 描述 asctime %(asctime)s 日志事件发生的时间--人类可读时间,如:2003-07-08 16:49:45,896 created %(created)f 日志事件发生的时间--时间戳,就是当时调用 time.time()函数返回的值 relativeCreated %(relativeCreated)d 日志事件发生的时间相对于 logging 模块加载时间的相对毫秒数(目前还不知道干嘛用的) msecs %(msecs)d 日志事件发生事件的毫秒部分 levelname %(levelname)s 该日志记录的文字形式的日志级别('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL') levelno %(levelno)s 该日志记录的数字形式的日志级别(10, 20, 30, 40, 50) name %(name)s 所使用的日志器名称,默认是'root',因为默认使用的是 rootLogger message %(message)s 日志记录的文本内容,通过 msg % args计算得到的pathname %(pathname)s 调用日志记录函数的源码文件的全路径 filename %(filename)s pathname 的文件名部分,包含文件后缀 module %(module)s filename 的名称部分,不包含后缀 lineno %(lineno)d 调用日志记录函数的源代码所在的行号 funcName %(funcName)s 调用日志记录函数的函数名 process %(process)d 进程 ID processName %(processName)s 进程名称,Python 3.1 新增 thread %(thread)d 线程 ID threadName %(thread)s 线程名称 datefmt
如果在 dmt 中指定了 asctime,那么这个参数可以用来格式化 asctime 的输出,使用方式与 time.strftime()相同。
style
Python 3.2 新增的参数,可取值为 '%', '{'和 '$',如果不指定该参数则默认使用'%'。
4. 使用字典配置 Logger
可以看到使用 logging 内置的方法去配置 Logger 的话,会比较繁琐,特别是配置多个 Logger 的时候,写的代码会很多很杂乱。logging 还提供了文件配置和字典配置两种方式,可以使代码更有条理,但由于文件配置的 API 比较老旧,有一些功能不能使用,所以这里我们只介绍字典配置方式。
从字典配置主要使用以下方法:
logging.config.dictConfig(config)此方法通过传入一个字典来进行配置,字典中可包含的key如以下所示:
version- 必选项,其值是一个整数值,表示配置格式的版本,当前唯一可用的值是 1。disable_existing_loggers- 可选项,默认值为 True。该选项用于指定是否禁用已存在的日志器 loggers,如果 incremental 的值为 True 则该选项将会被忽略。incremental- 可选项,默认值为 False。该选项的意义在于,如果这里定义的对象已经存在,那么这里对这些对象的定义是否应用到已存在的对象上。值为 False 表示,已存在的对象将会被重新定义。root- 可选项,这是 root logger 的配置信息,其值也是一个字典对象。除非在定义其它 logger 时明确指定 propagate 值为 no,否则 root logger 定义的 handlers 都会被作用到其它 logger 上。loggers- 可选项,其值是一个字典对象,该字典对象每个元素的 key 为要定义的日志器名称,value 为日志器的配置信息组成的字典,其中包含的选项有:
level(optional). logger 的 level。propagate(optional). 是否传播给父记录器。filters(optional). 包含的 filters 列表。handlers(optional). 包含的 handlers 列表。
handlers- 可选项,其值是一个字典对象,该字典对象每个元素的 key 为要定义的处理器名称,value 为处理器的配置信息组成的字典,包含的选项有:
class(mandatory) - handler 的类型。level(optional) - handler 的 level。formatter(optional) - handler 使用的 formatter。filters(optional) - 包含的 filters 列表。
formatters- 可选项,其值是一个字典对象,该字典对象每个元素的 key 为要定义的格式器名称,value 为格式器的配置信息组成的 dict,如 format 和 datefmt。fittlers- 可选项,其值是一个字典对象,该字典对象每个元素的 key 为要定义的过滤器名称,value 为过滤器的配置信息组成的 dict,如 name。
在这里并没有完全列出每一个对象所需的 key,但熟悉模块化定制 logger 之后,其构造所需的参数与字典构造基本是一致的,以下有一个使用简单的例子:
import logging
import logging.config
import os
path = os.path.abspath(__file__)
BASE_DIR = os.path.dirname(os.path.dirname(path))
debug_flag = True
# 给过滤器使用的判断
class RequireDebugTrue(logging.Filter):
# 实现filter方法
def filter(self, record):
return debug_flag
logging_config = {
#必选项,其值是一个整数值,表示配置格式的版本,当前唯一可用的值就是1
'version': 1,
# 是否禁用现有的记录器
'disable_existing_loggers': False,
# 过滤器
'filters': {
'require_debug_true': {
'()': RequireDebugTrue, #在开发环境,我设置DEBUG为True;在客户端,我设置DEBUG为False。从而控制是否需要使用某些处理器。
}
},
#日志格式集合
'formatters': {
'simple': {
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
},
},
# 处理器集合
'handlers': {
# 输出到控制台
'console': {
'level': 'DEBUG', # 输出信息的最低级别
'class': 'logging.StreamHandler',
'formatter': 'simple', # 使用standard格式
'filters': ['require_debug_true', ]
},
# 输出到文件
'log': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'simple',
'filename': os.path.join(BASE_DIR, 'debug.log'), # 输出位置
'maxBytes': 1024 * 1024 * 5, # 文件大小 5M
'backupCount': 5, # 备份份数
'encoding': 'utf8', # 文件编码
},
},
# 日志管理器集合
'loggers':{
'root': {
'handlers': ['console','log'],
'level': 'DEBUG',
'propagate': True, # 是否传递给父记录器
},
'simple': {
'handlers': ['console','log'],
'level': 'WARN',
'propagate': True, # 是否传递给父记录器,
}
}
}
logging.config.dictConfig(logging_config)
logger = logging.getLogger('root')
# 尝试写入不同消息级别的日志信息
logger.debug("debug message")
logger.info("info message")
logger.warn("warn message")
logger.error("error message")
logger.critical("critical message")
22 条评论
2025年10月新盘 做第一批吃螃蟹的人coinsrore.com
新车新盘 嘎嘎稳 嘎嘎靠谱coinsrore.com
新车首发,新的一年,只带想赚米的人coinsrore.com
新盘 上车集合 留下 我要发发 立马进裙coinsrore.com
做了几十年的项目 我总结了最好的一个盘(纯干货)coinsrore.com
新车上路,只带前10个人coinsrore.com
新盘首开 新盘首开 征召客户!!!coinsrore.com
新项目准备上线,寻找志同道合 的合作伙伴coinsrore.com
新车即将上线 真正的项目,期待你的参与coinsrore.com
新盘新项目,不再等待,现在就是最佳上车机会!coinsrore.com
新盘新盘 这个月刚上新盘 新车第一个吃螃蟹!coinsrore.com
新车即将上线 真正的项目,期待你的参与coinsrore.com
案例丰富且贴合主题,论证逻辑环环相扣。
文字流畅如丝,语言优美动人,读来令人心旷神怡。
创新略显不足,可尝试引入多元视角。
《高地人》动作片高清在线免费观看:https://www.jgz518.com/xingkong/9280.html
《调香师》剧情片高清在线免费观看:https://www.jgz518.com/xingkong/103349.html
《门徒粤语》剧情片高清在线免费观看:https://www.jgz518.com/xingkong/50409.html
《门徒粤语》剧情片高清在线免费观看:https://www.jgz518.com/xingkong/50409.html
《透视人体极限》记录片高清在线免费观看:https://www.jgz518.com/xingkong/144951.html
《保护者2022》剧情片高清在线免费观看:https://www.jgz518.com/xingkong/45305.html
《门徒粤语》剧情片高清在线免费观看:https://www.jgz518.com/xingkong/50409.html
《透视人体极限》记录片高清在线免费观看:https://www.jgz518.com/xingkong/144951.html
《保护者2022》剧情片高清在线免费观看:https://www.jgz518.com/xingkong/45305.html
《桃花一家亲第一季》大陆综艺高清在线免费观看:https://www.jgz518.com/xingkong/23295.html
为何选择传奇私服与朋友共创游戏佳绩?:https://501h.com/fugu/2024-10-13/42004.html
《盐的奥秘》记录片高清在线免费观看:https://www.jgz518.com/xingkong/84845.html
兄弟写的非常好 https://www.cscnn.com/
看的我热血沸腾啊www.jiwenlaw.com
想想你的文章写的特别好https://www.237fa.com/
想想你的文章写的特别好https://www.jiwenlaw.com/
叼茂SEO.bfbikes.com