1、概念
Python logging 模块定义了为应用程序和库实现灵活的事件日志记录的函数和类。
程序开发过程中,很多程序都有记录日志的需求,并且日志包含的信息有正常的程序访问日志还可能有错误、警告等信息输出,Python 的 logging 模块提供了标准的日志接口,可以通过它存储各种格式的日志,日志记录提供了一组便利功能,用于简单的日志记录用法。
- 使用 Python Logging 模块的主要好处是所有 Python 模块都可以参与日志记录
- Logging 模块提供了大量具有灵活性的功能
日志记录函数以它们用来跟踪的事件的级别或严重性命名。下面描述了标准级别及其适用性(从高到低的顺序):
日志等级(level) 描述
DEBUG 最详细的日志信息,典型应用场景是 问题诊断
INFO 信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作
WARNING 当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的
ERROR 由于一个更严重的问题导致某些功能不能正常运行时记录的信息
CRITICAL 当发生严重错误,导致应用程序不能继续运行时记录的信息
日志级别等级排序:critical > error > warning > info > debug
级别越高打印的日志越少,反之亦然,即
debug : 打印全部的日志( notset 等同于 debug )
info : 打印 info, warning, error, critical 级别的日志
warning : 打印 warning, error, critical 级别的日志
error : 打印 error, critical 级别的日志
critical : 打印 critical 级别
一、 Logging 模块日志记录方式
Logging 模块提供了两种日志记录方式:
一种方式是使用 Logging 提供的模块级别的函数
另一种方式是使用 Logging 日志系统的四大组件记录
1、Logging 定义的模块级别函数
函数 说明
logging.debug(msg, *args, **kwargs) 创建一条严重级别为DEBUG的日志记录
logging.info(msg, *args, **kwargs) 创建一条严重级别为INFO的日志记录
logging.warning(msg, *args, **kwargs) 创建一条严重级别为WARNING的日志记录
logging.error(msg, *args, **kwargs) 创建一条严重级别为ERROR的日志记录
logging.critical(msg, *args, **kwargs) 创建一条严重级别为CRITICAL的日志记录
logging.log(level, *args, **kwargs) 创建一条严重级别为level的日志记录
logging.basicConfig(**kwargs) 对root logger进行一次性配置
简单打印日志:
import logging
# 打印日志级别
def test_logging():
logging.debug('Python debug')
logging.info('Python info')
logging.warning('Python warning')
logging.error('Python Error')
logging.critical('Python critical')
test_logging()
输出结果:
WARNING:root:Python warning
ERROR:root:Python Error
CRITICAL:root:Python critical
当指定一个日志级别之后,会记录大于或等于这个日志级别的日志信息,小于的将会被丢弃,
默认情况下日志打印只显示大于等于 WARNING 级别的日志。
1.1 设置日志显示级别
通过 logging.basicConfig() 可以设置 root 的日志级别,和日志输出格式。
logging.basicConfig() 关键字参数:
关键字 描述
filename 创建一个 FileHandler,使用指定的文件名,而不是使用 StreamHandler。
filemode 如果指明了文件名,指明打开文件的模式(如果没有指明 filemode,默认为 ‘a’)。
format handler 使用指明的格式化字符串。
datefmt handler 使用指明的格式化字符串。
level 指明根 logger 的级别。
stream 使用指明的流来初始化 StreamHandler。该参数与 ‘filename’ 不兼容,如果两个都有,‘stream’ 被忽略。
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 打印日志信息
注意:Logging.basicConfig() 需要在开头就设置,在中间设置并无作用
实例
import logging
# 打印日志级别
def test():
logging.basicConfig(level=logging.DEBUG)
logging.debug('Python debug')
logging.info('Python info')
logging.warning('Python warning')
logging.error('Python Error')
logging.critical('Python critical')
logging.log(2,'test')
test()
输出:
DEBUG:root:Python debug
INFO:root:Python info
WARNING:root:Python warning
ERROR:root:Python Error
CRITICAL:root:Python critical
1.2 将日志信息记录到文件
日志信息记录到文件
logging.basicConfig(filename='F:/example.log', level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
在相应的路径下会有 example.log 日志文件,内容如下:
DEBUG:root:This message should go to the log file
INFO:root:So should this
WARNING:root:And this, too
1.3 多个模块记录日志信息
如果程序包含多个模块,则用以下实例来显示日志信息:
实例中有两个模块,一个模块通过导入另一个模块的方式用日志显示另一个模块的信息:
myapp.py 模块
import logging
import mylib
def main():
logging.basicConfig(filename='myapp.log',level=logging.DEBUG)
logging.info('Started')
mylib.do_something()
logging.info('Finished')
if __name__ == '__main__':
main()
mylib.py 模块
import logging
def do_something():
logging.info('Doing something')
执行 myapp.py 模块会打印相应日志,在文件 myapp.log 中显示信息如下:
INFO:root:Started
INFO:root:Doing something
INFO:root:Finishe
1.4 显示信息的日期及更改显示消息格式
显示消息日期
import logging
#显示消息时间
logging.basicConfig(format='%(asctime)s %(message)s')
logging.warning('is when this event was logged.')
logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning('is when this event was logged.')
结果:
2019-10-16 18:57:45,988 is when this event was logged.
2019-10-16 18:57:45,988 is when this event was logged.
更改显示消息格式
import logging
# 更改显示消息的格式
logging.basicConfig(format='%(levelname)s:%(message)s',level=logging.DEBUG)
logging.debug('Python message format Debug')
logging.info('Python message format Info')
logging.warning('Python message format Warning')
结果:
DEBUG:Python message format Debug
INFO:Python message format Info
WARNING:Python message format Warning
注意:显示结果只显示级别和具体信息,之前显示的 “根” 已经消失,重新定义的格式修改了默认输出方式。
2、logging 模块四大组件
组件名称 对应类名 功能描述
日志器 Logger 暴露函数给应用程序,基于日志记录器和过滤器级别决定哪些日志有效
处理器 Handler 将 logger 创建的日志记录发送到合适的目的输出
过滤器 Filter 提供了更细粒度的控制工具来决定输出哪条日志记录,丢弃哪条日志记录
格式器 Formatter 决定日志记录的最终输出格式
2.1 日志器- Logger
Logger 持有日志记录器的方法,日志记录器不直接实例化,而是通过模块级函数 logger.getlogger (name) 来实例化,使用相同的名称多次调用 getLogger() 总是会返回对相同 Logger 对象的引用。
- 应用程序代码能直接调用日志接口。
- Logger最常用的操作有两类:配置和发送日志消息。
- 初始化 logger = logging.getLogger(“endlesscode”),获取 logger
对象,getLogger() 方法后面最好加上所要日志记录的模块名字,配置文件和打印日志格式中的 %(name)s
对应的是这里的模块名字,如果不指定name则返回root对象。 - logger.setLevel(logging.DEBUG),Logging 中有 NOTSET < DEBUG < INFO <
WARNING < ERROR < CRITICAL这几种级别,日志会记录设置级别以上的日志 - 多次使用相同的name调用 getLogger 方法返回同一个 looger 对象; Logger是一个树形层级结构,在使用接口
debug,info,warn,error,critical 之前必须创建 Logger 实例:
创建方法: logger = logging.getLogger(logger_name)
创建Logger实例后,可以使用以下方法进行日志级别设置,增加处理器 Handler:
logger.setLevel(logging.ERROR) # 设置日志级别为 ERROR,即只有日志级别大于等于 ERROR 的日志才会输出
logger.addHandler(handler_name) # 为 Logger 实例增加一个处理器
logger.removeHandler(handler_name) # 为 Logger 实例删除一个处理器
2.2 处理器- Handler
Handler 处理器类型有很多种,比较常用的有三个,StreamHandler,FileHandler,NullHandler
StreamHandler
创建方法:sh = logging.StreamHandler(stream=None)
创建 StreamHandler 之后,可以通过使用以下方法设置日志级别,设置格式化器 Formatter,增加或删除过滤器 Filter:
ch.setLevel(logging.WARN) # 指定日志级别,低于WARN级别的日志将被忽略
ch.setFormatter(formatter_name) # 设置一个格式化器formatter
ch.addFilter(filter_name) # 增加一个过滤器,可以增加多个
ch.removeFilter(filter_name) # 删除一个过滤器
2.3 过滤器- Filter
Handlers 和 Loggers 可以使用 Filters 来完成比级别更复杂的过滤。
Filter 基类只允许特定 Logger 层次以下的事件。
例如用 ‘A.B’ 初始化的 Filter 允许Logger ‘A.B’, ‘A.B.C’, ‘A.B.C.D’, ‘A.B.D’ 等记录的事件,logger‘A.BB’, ‘B.A.B’ 等就不行。 如果用空字符串来初始化,所有的事件都接受。
创建方法: filter = logging.Filter(name=‘’)
2.4 格式器- Formatter
使用Formatter对象设置日志信息最后的规则、结构和内容,默认的时间格式为%Y-%m-%d %H:%M:%S。
创建方法: formatter = logging.Formatter(fmt=None, datefmt=None)
其中,fmt 是消息的格式化字符串,datefmt 是日期字符串。如果不指明 fmt,将使用 ‘%(message)s’ 。如果不指明 datefmt,将使用 ISO8601 日期格式。
2.5 组件之间的关联关系
- 日志器(logger)需要通过处理器(handler)将日志信息输出到目标位置,不同的处理器(handler)可以将日志输出到不同的位置;
- 日志器(logger)可以设置多个处理器(handler)将同一条日志记录输出到不同的位置;
- 每个处理器(handler)都可以设置自己的过滤器(filter)实现日志过滤,从而只保留感兴趣的日志;
- 每个处理器(handler)都可以设置自己的格式器(formatter)实现同一条日志以不同的格式输出到不同的地方。
简明了说就是:日志器(logger)是入口,真正干活儿的是处理器(handler),处理器(handler)还可以通过过滤器(filter)和格式器(formatter)对要输出的日志内容做过滤和格式化等处理操作。
- Logger 可以包含一个或多个 Handler 和 Filter
- Logger 与 Handler 或 Fitler 是一对多的关系
- 一个 Logger 实例可以新增多 个 Handler,一个 Handler 可以新增多个格式化器或多个过滤器,而且日志级别将会继承。
二、Logging 日志工作流程
1、Logging 模块使用过程
1)第一次导入 logging 模块或使用 reload 函数重新导入 logging 模块,logging 模块中的代码将被执行,这个过程中将产生 logging 日志系统的默认配置。
2)自定义配置(可选),logging标准模块支持三种配置方式: dictConfig,fileConfig,listen。其中,dictConfig 是通过一个字典进行配置 Logger,Handler,Filter,Formatter;fileConfig 则是通过一个文件进行配置;而 listen 则监听一个网络端口,通过接收网络数据来进行配置。当然,除了以上集体化配置外,也可以直接调用 Logger,Handler 等对象中的方法在代码中来显式配置。
3)使用 logging 模块的全局作用域中的 getLogger 函数来得到一个 Logger 对象实例(其参数即是一个字符串,表示 Logger 对象实例的名字,即通过该名字来得到相应的 Logger 对象实例)。
4)使用 Logger 对象中的 debug,info,error,warn,critical 等方法记录日志信息。
ogging 模块处理流程
流程描述:
- 判断日志的等级是否大于 Logger 对象的等级,如果大于,则往下执行,否则,流程结束。
- 产生日志:第一步,判断是否有异常,如果有,则添加异常信息。 第二步,处理日志记录方法(如 debug,info
等)中的占位符,即一般的字符串格式化处理。 - 使用注册到 Logger 对象中的 Filters
进行过滤。如果有多个过滤器,则依次过滤;只要有一个过滤器返回假,则过滤结束,且该日志信息将丢弃,不再处理,而处理流程也至此结束。否则,处理流程往下执行。 - 在当前 Logger 对象中查找 Handlers,如果找不到任何 Handler,则往上到该 Logger 对象的父 Logger
中查找;如果找到一个或多个 Handler,则依次用 Handler 来处理日志信息。但在每个 Handler
处理日志信息过程中,会首先判断日志信息的等级是否大于该 Handler 的等级,如果大于,则往下执行(由 Logger 对象进入
Handler 对象中),否则,处理流程结束。 - 执行 Handler 对象中的 filter 方法,该方法会依次执行注册到该 Handler 对象中的 Filter。如果有一个
Filter 判断该日志信息为假,则此后的所有 Filter 都不再执行,而直接将该日志信息丢弃,处理流程结束。 - 使用 Formatter 类格式化最终的输出结果。 注:Formatter 同上述第 2
步的字符串格式化不同,它会添加额外的信息,比如日志产生的时间,产生日志的源代码所在的源文件的路径等等。 - 真正地输出日志信息(到网络,文件,终端,邮件等)。至于输出到哪个目的地,由 Handler 的种类来决定。
三、配置日志
程序员可以通过三种方式配置日志记录:
1、基础配置
logging.basicConfig(filename="config.log",
filemode="w",
format="%(asctime)s-%(name)s-%(levelname)s-%(message)s",
level=logging.INFO)
2、使用配置文件的方式
fileConfig(filename,defaults=None,disable_existing_loggers=Ture )
3、使用一个字典方式来写配置信息
使用dictConfig(dict,defaults=None, disable_existing_loggers=Ture )函数
2、示例
日志的封装
实际开发情况中,我们会将logging单独写成一个模块封装成类,然后在其他的功能模块比如通讯协议模块,数据库模块,业务层,API调用模块等等,中进行调用。
在实际情况中, logging.getLogger(" “) 传入无名称适用于只需要小范围系统使用,一旦涉及到多个模块,需要系统划分的情况,建议把class设置一个传入参数,并且将这个参数传递给 logging.getLogger(” ") ,以规范日志的划分
import logging
import logging.handlers
import os
import time
class logs(object):
def __init__(self):
# 获取模块名称,测试的时候直接控模块即可,但是在实际使用的情况下需要针对不同需要进行日志撰写的模块进行命名
# 列如:通讯协议模块,测试模块,数据库模块,业务层模块,API调用模块
# 可以考虑 __init__(self,model_name) 这样传入,然后再用一个list规定一下模块名称
self.logger = logging.getLogger("")
# 设置输出的等级
LEVELS = {'NOSET': logging.NOTSET,
'DEBUG': logging.DEBUG,
'INFO': logging.INFO,
'WARNING': logging.WARNING,
'ERROR': logging.ERROR,
'CRITICAL': logging.CRITICAL}
# 创建文件目录
logs_dir = "logs"
if os.path.exists(logs_dir) and os.path.isdir(logs_dir):
pass
else:
os.mkdir(logs_dir)
# 修改log保存位置
timestamp = time.strftime("%Y-%m-%d", time.localtime())
logfilename = '%s.txt' % timestamp
logfilepath = os.path.join(logs_dir, logfilename)
rotatingFileHandler = logging.handlers.RotatingFileHandler(filename=logfilepath,
maxBytes=1024 * 1024 * 50,
backupCount=5)
# 设置输出格式
formatter = logging.Formatter('[%(asctime)s] [%(levelname)s] %(message)s', '%Y-%m-%d %H:%M:%S')
rotatingFileHandler.setFormatter(formatter)
# 控制台句柄
console = logging.StreamHandler()
console.setLevel(logging.NOTSET)
console.setFormatter(formatter)
# 添加内容到日志句柄中
self.logger.addHandler(rotatingFileHandler)
self.logger.addHandler(console)
self.logger.setLevel(logging.NOTSET)
def info(self, message):
self.logger.info(message)
def debug(self, message):
self.logger.debug(message)
def warning(self, message):
self.logger.warning(message)
def error(self, message):
self.logger.error(message)
例子
日志输出-控制台
import logging # 引入logging模块
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s') # logging.basicConfig函数对日志的输出格式及方式做相关配置
# 由于日志基本配置中级别设置为DEBUG,所以一下打印信息将会全部显示在控制台上
logging.info('this is a loggging info message')
logging.debug('this is a loggging debug message')
logging.warning('this is loggging a warning message')
logging.error('this is an loggging error message')
logging.critical('this is a loggging critical message')
日志输出-文件
import logging
import os.path
import time
# 第一步,创建一个logger
logger = logging.getLogger()
logger.setLevel(logging.INFO) # 设置Log等级
# 第二步,创建一个handler,用于写入日志文件
rq = time.strftime('%Y%m%d%H%M', time.localtime(time.time()))
log_path = os.path.dirname(os.getcwd()) + '/Logs/'
log_name = log_path + rq + '.log'
logfile = log_name
fh = logging.FileHandler(logfile, mode='w')
fh.setLevel(logging.DEBUG) # 输出到file的log等级的开关
# 第三步,定义handler的输出格式
formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
fh.setFormatter(formatter)
# 第四步,将logger添加到handler里面
logger.addHandler(fh)
# 日志
logger.debug('this is a logger debug message')
logger.info('this is a logger info message')
logger.warning('this is a logger warning message')
logger.error('this is a logger error message')
logger.critical('this is a logger critical message')
日志输出-控制台和文件
ch = logging.StreamHandler() # 只要在输入到日志中的第二步和第三步插入一个handler输出到控制台
ch.setLevel(logging.WARNING) # 输出到console的log等级的开关
# 在第四步和第五步分别加入以下代码即可
ch.setFormatter(formatter)
logger.addHandler(ch)
6、format常用格式说明
%(levelno)s: 打印日志级别的数值
%(levelname)s: 打印日志级别名称
%(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0]
%(filename)s: 打印当前执行程序名
%(funcName)s: 打印日志的当前函数
%(lineno)d: 打印日志的当前行号
%(asctime)s: 打印日志的时间
%(thread)d: 打印线程ID
%(threadName)s: 打印线程名称
%(process)d: 打印进程ID
%(message)s: 打印日志信息
import os.path
import time
import logging
# 创建一个logger
logger = logging.getLogger()
logger.setLevel(logging.INFO) # Log等级总开关
# 创建一个handler,用于写入日志文件
rq = time.strftime('%Y%m%d%H%M', time.localtime(time.time()))
log_path = os.path.dirname(os.getcwd()) + '/Logs/'
log_name = log_path + rq + '.log'
logfile = log_name
fh = logging.FileHandler(logfile, mode='w')
fh.setLevel(logging.DEBUG) # 输出到file的log等级的开关
# 定义handler的输出格式
formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
fh.setFormatter(formatter)
logger.addHandler(fh)
# 使用logger.XX来记录错误,这里的"error"可以根据所需要的级别进行修改
try:
open('/path/to/does/not/exist', 'rb')
except (SystemExit, KeyboardInterrupt):
raise
except Exception, e:
logger.error('Failed to open file', exc_info=True)
日志滚动和过期删除(按时间)
# 字段解释
filename:日志文件名的prefix;
when:是一个字符串,用于描述滚动周期的基本单位,字符串的值及意义如下:
“S”: Seconds
“M”: Minutes
“H”: Hours
“D”: Days
“W”: Week day (0=Monday)
“midnight”: Roll over at midnight
interval: 滚动周期,单位有when指定,比如:when=’D’,interval=1,表示每天产生一个日志文件
backupCount: 表示日志文件的保留个数
# coding:utf-8
import logging
import time
import re
from logging.handlers import TimedRotatingFileHandler
from logging.handlers import RotatingFileHandler
def backroll():
#日志打印格式
log_fmt = '%(asctime)s\tFile \"%(filename)s\",line %(lineno)s\t%(levelname)s: %(message)s'
formatter = logging.Formatter(log_fmt)
#创建TimedRotatingFileHandler对象
log_file_handler = TimedRotatingFileHandler(filename="ds_update", when="M", interval=2, backupCount=2)
#log_file_handler.suffix = "%Y-%m-%d_%H-%M.log"
#log_file_handler.extMatch = re.compile(r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}.log$")
log_file_handler.setFormatter(formatter)
logging.basicConfig(level=logging.INFO)
log = logging.getLogger()
log.addHandler(log_file_handler)
#循环打印日志
log_content = "test log"
count = 0
while count < 30:
log.error(log_content)
time.sleep(20)
count = count + 1
log.removeHandler(log_file_handler)
if __name__ == "__main__":
backroll()
3、日志配置详解
1. logging.basicConfig()
"""
logging.basicConfig(
filename,
filemode,
format,
datefmt,
style,
level,
stream,
handlers
)
"""
import logging
_format = ('%(asctime)s '
'%(name)s ' # the name of logger
'%(levelname)s '
'%(filename)s '
'%(threadName)s '
'%(lineno)d: '
'%(message)s')
logging.basicConfig(
level=logging.DEBUG, # 默认 level 是 WARNING
filename='test.log',
format=_format,
)
logging.debug('logging debug')
logging.info('logging info')
2、自定义 logger
将 logging.basicConfig 的配置项进行拆分, 自定义 logger, 然后逐步配置.
自定义 一个 logger 的步骤:
- 获取一个 logger: logging.getLogger(…) 设置 level: logger.setLevel(…)
- 定义一个/多个 formatter: logging.Formatter(…) 定义一个/多个 handler
- logging.XXXHandler(…) 为一个 handler 设置一个 fomatter:
- hd.setFormatter(…) 为一个 handler 设置 level: hd.setLevel(…) 添加所有
- handler 到一个 logger: logger.addHandler(…), … 定义一个/多个 filter: class
- MyFilter(logging.Filter): … 添加所有 filter 到 logger:
- logger.addFilter(…), …
import logging
# 获取一个 logger
logger = logging.getLogger(__name__)
# 为 logger 设置 level
logger.setLevel(logging.DEBUG)
# 定义一个 formatter
formatter = logging.Formatter(
'%(levelname)s:%(name)s %(asctime)s %(funcName)s: %(message)s'
)
# 定义一个 handler, 存储 INFO 及 INFO 以上级别的日志
# 为了看方便测试结果, 将 mode 设为 w, 不追加
info_hd = logging.FileHandler('log1_info.log', mode='w')
info_hd.setFormatter(formatter) # 为 handler 设置 formatter
info_hd.setLevel(logging.INFO) # 为 handler 设置 level
# 定义另一个 handler, 存储 ERROR 及 ERROR 以上级别的日志
err_hd = logging.FileHandler('log1_error.log', mode='w')
err_hd.setFormatter(formatter)
err_hd.setLevel(logging.ERROR)
# 定义一个 handler, 用于 在控制台中打印 所有级别的日志
stream_hd = logging.StreamHandler()
stream_hd.setFormatter(formatter)
stream_hd.setLevel(logging.DEBUG)
# 添加所有 handler 到 logger
logger.addHandler(info_hd)
logger.addHandler(err_hd)
logger.addHandler(stream_hd)
# 实际定义 logger 时, 一般定义不同的 formatter, 用于存储/打印不同细节的日志
def foo():
# 使用自定义 logger 打印日志
logger.debug('test user defined logger DEBUG')
logger.info('test user defined logger INFO')
logger.warning('test user defined logger INFO')
logger.error('test user defined logger ERROR')
logger.critical('test user defined logger ERROR')
# 测试 logger.exception(), 记录异常(ERROR级别)的堆栈信息
def log_exc():
try:
1 / 0
except ZeroDivisionError:
logger.exception('tried to divide by 0')
if __name__ == '__main__':
foo()
log_exc()
3、logging.config.XXX
这里记录一下logging.config.dictConfig.
"""
顶级配置项:
version
formatters
filters
handlers
loggers
root
incremental
disable_existing_loggers
"""
LOG_CONF = {
'version': 1,
'formatters': {
'formatter_1': {
'format': '%(levelname)s:%(name)s %(asctime)s: %(message)s',
'datefmt': None
},
'formatter_2': {
'format': '%(levelname)s - %(name)s - %(message)s',
'datefmt': None
}
},
'filters': {
'filter_1': {
'()': myFilter1, # 使用 `()` 指定用哪个类来实现过滤功能
'name': 'name1' # name 的值会在 Filter 实例化时传入
}
},
'handlers': {
'handler_1': { # 指定一个文件 handler, 仅保存 ERROR 及其以上级别的日志
'class': 'logging.FileHandler',
'level': logging.ERROR,
'formatter': 'formatter_1',
'filters': ['filter_1'],
'filename': LOG_DIR / 'test_dict_config_err.log', # 需要先创建目录
},
'handler_2': { # 指定一个 stream handler, 打印 DEBUG 及其以上级别的日志
'class': 'logging.StreamHandler',
'level': logging.DEBUG,
'formatter': 'formatter_2',
# 'filters': ['filter_1']
}
},
'loggers': {
'my_logger1': {
'level': logging.WARNING, # 在这里指定的 level, 优先级比 handler 中的 level 更高
# Logger 对象被设计为一个树形结构. print(logger.parent)
# 子logger 在完成对日志消息的处理后,默认会将日志消息传递给它的 parent logger
'propagate': False, # 1/0/True/False, 是否传递给父节点的 logger 来处理
'filters': ['filter_1'], # 在这里指定的 filter, 优先级比 handler 中的 filter 更高
'handlers': ['handler_1', 'handler_2']
}
},
'root': { # 专门配置 root logger
'level': logging.DEBUG,
# 'propagate': 1, # 对于 root logger 来说, propagate 零作用
'filters': [],
'handlers': ['handler_2'] # 为了测试 propagate, 需要指定一个 handler
},
'incremental': False,
'disable_existing_loggers': True
}
myFilter1 和 LOG_DIR 为
import logging
from pathlib import Path
LOG_DIR = Path('logs')
LOG_DIR.mkdir(exist_ok=True)
class myFilter1(logging.Filter):
def filter(self, record: logging.LogRecord) -> bool:
"""
返回 True 时, 此条日志过滤, 不会打印和保存
返回 False 时, 不过滤, 会打印或保存
"""
# 如果日志 message 中带有`test filter`, 则忽略此条日志
if 'test filter' in record.getMessage():
return False
return True
test
import logging
from logging import config
from config.dict_config import LOG_CONF
logger = logging.getLogger('my_logger1')
# Logger 对象被设计为一个树形结构
print(logger.parent)
# 使配置生效
config.dictConfig(LOG_CONF)
def foo():
logger.debug('dictConfig DEBUG')
logger.info('dictConfig INFO')
logger.warning('dictConfig WARNING')
logger.error('dictConfig ERROR')
logger.critical('dictConfig CRITICAL')
try:
1 / 0
except ZeroDivisionError:
logger.exception('dictConfig logger.exception')
logger.error('xxxx test filter xxx')
foo()