Skip to content

Python内置模块 - 日志

日志模块

在程序开发中,日记记录是不可缺少的。Python处理日志的模式是logging,它是内置模块之一,提供了丰富的处理流,包括写入文件、发送到邮箱、发送到控制台等。Python中常用的异常处理信息输出如下表所示。

场景推荐措施
将结果显示在控制台print()
监控和记录程序运行状态使用logging.info()函数(当由诊断目的需要详细输出的使用logging.debug()函数)
发出一个警告信息使用logging.warning(),在控制台显示红色
对业务之外的错误处理抛出异常
只报告错误,不抛出异常使用logging.error()函数

日志的使用

使用import logging导入日志模块。

日志模块有以下该概念:

  • 记录器:Logger是日志的记录器,对外提供logging的接口,应用程序可以直接使用这个对象。
  • 处理器:Handler是日志的处理器,负责将适当的日志信息分派成处理程序的目标。
  • 格式化器:Formatter是日志的格式化器,负责将日志按照格式字符串输出。
  • 过滤器:Filter是日志的过滤器,提供了更加精细的附加功能。
  • 日志级别:用于划分错误的严重级别。
  1. 日志等级

日志等级分为6种,如下表。

级别数值备注
NOTSET0当logger是根logger时,将处理所有消息,若logger是非根logger,则所有消息会委派给父级
DEBUG10细节消息,仅当诊断问题时使用
INFO20确认程序按预期执行
WARNING30表明有已经或即将发生的意外,程序仍按预期执行
ERROR40由于严重的问题,程序的某些功能已经不能正常执行
CRITICAL50有严重的错误,程序不能继续执行
  1. 格式化器

格式化器对象用于配置日志信息的输出内容,由一些字符串定义。

字段含义
%(levelno)s打印日志级别的数值
%(levelname)s打印日志级别名称
%(pathname)s打印当前执行程序的路径,相当于sys.argv[0]
%(filename)打印当前执行程序名
%(funcName)s打印日志的当前函数
%(lineno)d打印日志的当前函数
%(asctime)s打印日志的时间
%(thread)d打印线程ID
%(threadName)s打印线程名称
%(process)d打印进程ID
%(message)s打印日志信息
%(msecs)d打印记录时的毫秒部分
%(created)f打印记录时的时间戳
%(name)s打印记录器的名称,默认为root
%(processName)s打印进程名
%(relativeCreated)d打印日志记录时相对于日志模块初始化的时间(毫秒)
%(module)s打印模块

Logger

记录器对外提供logging的接口。它具有以下属性。

  • propagate:判断日志是否向上传播。
  • setLevel(level):设置记录阈值,日志等级大于阈值的会被记录。默认为WARNING级别。
  • isEnabledFor(level):判断当前记录器是否处理级别为参数传入的level级别信息。
  • getEffectiveLevel():获取此记录器的有效级别。
  • getChild(suffix):用于返回由参数确定的记录器。
  • debug(msg, *args, **kwargs):用于DEBUG在此记录器上记录级别的消息,在关键字参数中制定了三个参数:
    • exc_info:若不为False,则将异常信息添加到日志信息中。
    • stack_info:若为True,则将堆栈信息添加到日志信息中。
    • extra:它不常用,可通过字典指定。
  • info(msg, *args, **kwargs):用于记录INFO级别的信息。
  • 以下函数都是记录对应级别的信息:warning(msg, *args, **kwargs)error(msg, *args, **kwargs)critical(msg, *args, **kwargs)log(msg, *args, **kwargs)exception(msg, *args, **kwargs)
  • addFilter(filter):将过滤器添加到记录器。
  • remove(filter):从记录器中移除过滤器。
  • filter(record):用过滤器检查记录事件对象record,若有一条不符合要求则不处理此事件。
  • addHandler(hdlr):将处理程序添加到记录器。
  • removeHandler(hdlr):从记录器中删除指定的处理程序。
  • findCaller(stack_info=False):以元组形式返回记录器所在的文件名、行号、函数名称和堆栈信息。
  • handle(record):将记录对象record传递给记录器或其祖先关联的所有处理程序。此方法用于从套接字接受的为选择记录以及本机创建记录。
  • makeRecord(name, lvl, fn, info, lno, msg, exe_info, func=None, extra=None, sinfo=None):用于重写或创建专门的LogRecord实例。
  • hasHandler:用于检查此记录是否配置了处理程序。
python
import logging

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)


class MyFilter(logging.Filter):
	def filter(self, record: object) -> bool:
		return "测试消息" not in record.msg


ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(filename)s[line: %(lineno)d] - %(levelname)s: %(message)s - %(name)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
filter = MyFilter()
logger.addFilter(filter)

if __name__ == '__main__':
	logger.info('这是日志消息')
	logger.info('过滤器将过滤掉含测试消息字段的日志')
	logger.removeFilter(filter)
	logger.info('移除测试消息过滤器')

Handler

Handler的作用是负责将适当的日志消息分派给对应的处理程序。一般不直接实例化Handler,它只是一个基类,它内部提供了很多处理程序,直接使用它们即可。

它的属性和方法如下:

  • __init__(level=NOTSET):初始化实例,传入处理日志级别。

  • createLock():初始化一个线程锁,确保对文件访问是安全的。

  • acquire():获取使用createLock()初始化的线程锁。

  • release():释放锁。

  • setLevel(level):设置处理阈值。

  • setFormatter(fmt):设置消息格式。

  • addFilter(filter):添加过滤器。

  • removeFilter(filter):移除过滤器。

  • filter(record):将此处理器的过滤器应用于记录,若要处理该条记录,则返回True。依次查询过滤器,直到其中返回一个False后处理器就不会处理记录。

  • flush():确保已清除所有日志输出记录。

  • close():关闭当前处理程序使用的所有资源,并删除处理程序。

  • handle(record):经处理器处理后将记录对象record发送给实际处理程序。

  • handleError(record):当Handle遇到异常时,将记录对象传递给emit()处理。若raiseExceptions属性为False,则异常将被忽略。

  • format(record):如果设置了格式化程序将会对记录格式化,否则使用默认的格式化程序。

  • emit(record):处理当处理程序出现异常时的记录对象。

    处理器一般很少自由使用。

Python日志模板内置数十种日志处理器,下面列举出常用的。

  • StreamHandler:将日志记录输出发送到流,如控制台。
  • FileHandler:将日志记录发送到磁盘文件。
  • NullHandler:不做任何的格式输出。
  • RotatingFileHandler:按照日志文件大小分割日志。
  • TimedRotatingFileHandler:按照日志文件记录时间分割日志。
  • STMPHandler:将日志文件发送到指定电子邮箱。
  • MemoryHandler:将日志记录缓冲到内存中。
  • HTTPHandler:将日志记录发送到Web服务器。
  1. 将日志写入磁盘文件。
python
import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)  # 输出到console的log等级的开关
fh = logging.FileHandler('log.log', mode='w')
fh.setLevel(logging.DEBUG)  # 输出到file的log等级的开关
fmt = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")  # 创建一个格式化器
ch.setFormatter(fmt)  # 格式化器添加到流处理器
fh.setFormatter(fmt)  # 格式化器添加到文件写入处理器
logger.addHandler(ch)  # 记录器添加流处理器
logger.addHandler(fh)  # 记录器添加文件写入处理器

if __name__ == '__main__':
	logger.info("这是一条常规日志信息1")

  1. 将日志文件按照时间分割
python
import time
import logging.handlers

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)  # 输出到console的log等级的开关
fh = logging.handlers.TimedRotatingFileHandler(filename='log.log', when="m", interval=1,
                                               backupCount=5, encoding='utf-8')  # 按照时间分割日志
fh.setLevel(logging.DEBUG)  # 输出到file的log等级的开关
fmt = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")  # 创建一个格式化器
ch.setFormatter(fmt)  # 格式化器添加到流处理器
fh.setFormatter(fmt)  # 格式化器添加到文件按时分割处理器
logger.addHandler(ch)  # 记录器添加流处理器
logger.addHandler(fh)  # 记录器添加文件按时分割处理器

if __name__ == '__main__':
    while True:
        logger.info(f"这是一条常规日志信息,时间戳{time.time()}")
        time.sleep(1)
  1. 将日志按照大小分割
python
import time
import logging.handlers

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)  # 输出到console的log等级的开关
fh = logging.handlers.RotatingFileHandler(filename='log.log', maxBytes=1024, backupCount=5)  # 按照日志文件大小分割日志
fh.setLevel(logging.DEBUG)  # 输出到file的log等级的开关
fmt = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")  # 创建一个格式化器
ch.setFormatter(fmt)  # 格式化器添加到流处理器
fh.setFormatter(fmt)  # 格式化器添加到文件按时分割处理器
logger.addHandler(ch)  # 记录器添加流处理器
logger.addHandler(fh)  # 记录器添加文件按大小割处理器

if __name__ == '__main__':
    while True:
        logger.info(f"这是一条常规日志信息,时间戳{time.time()}")
        time.sleep(1)
  1. 将日志发送到邮箱
python
import time
import logging.handlers

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)  # 输出到console的log等级的开关
fh = logging.handlers.SMTPHandler(
    mailhost=('smtp.qq.com', 587),  # SMTP邮件服务器地址和端口号
    fromaddr='255xxx277@qq.com',  # 发件人地址
    toaddrs='243xxx539@qq.com',  # 收件人地址
    subject='发生了一个错误',  # 邮件主题
    credentials=('255xxx77@qq.com', 'ouqpcxxxbab'))  # SMTP邮箱账号和SMTP服务授权码,不是邮箱登陆授权码
fh.setLevel(logging.ERROR)  # 输出到file的log等级的开关
fmt = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")  # 创建一个格式化器
ch.setFormatter(fmt)  # 格式化器添加到流处理器
fh.setFormatter(fmt)  # 格式化器添加到邮件发送处理器
logger.addHandler(ch)  # 记录器添加流处理器
logger.addHandler(fh)  # 记录器添加邮件发送处理器

if __name__ == '__main__':
    try:
        1 + "s"
    except BaseException as e:
        logger.exception(e)

Formatter

格式化器的作用就是将日志按照开发者想要的格式输出。它具有如下成员属性和方法。

  • __init__(fmt=None, datefmt=None, style='%'):初始化方法,参数fmt代表格式化字符串,默认为%(message)s,参数datefmt是时间格式化字符串,style参数确定如何将格式字符串与数字合并,可选值有“%”、“{”、“$”。
  • format(record):它是日志记录的属性字典,用作字符串格式化操作的数据,返回结果字符串。
  • formatTime(record, datefmt=None):用自定义的格式化时间字符串来处理记录对象record,默认为“%Y-%m-%d%H:%M:%S,uuu”格式的时间格式化字符串。
  • formatException(exc_info):将指定的异常信息格式化成字符串。
  • formatStack(stack_info):将指定的堆栈信息转换为字符串。

可选择的格式化字段及输出示例如下表所示。

字段含义输出示例
%(levelinfo)s打印日志级别的数值20
%(levelname)s打印日志级别名称INFO
%(pathname)s打印当前执行程序的路径C:/User/PythonProject/code/log.py
%(filename)s打印当前执行程序名log.py
%(funcName)s打印日志的当前函数<module>
%(lineno)d打印日志的当前行号35
%(asctime)s打印日志的时间2020-02-25 22:00:58,759
%(thread)d打印线程ID1772
%(threadName)s打印线程名称MainThread
%(process)d打印进程ID5732
%(message)s打印日志信息日志初步使用
%(msecs)d打印日志时的毫秒时分759
%(created)f打印记录日志时的时间戳15823412441.522445
%(name)s打印记录器的名称,默认为rootroot
(processName)s打印进程名MainProcess
(relativeCreated)d打印日志记录时相对于日志模块初始化的时间358
%(module)s打印模块log

Filters

过滤器提供了更加精细的附加功能,用于确定要输出的日志。过滤器可以单独添加到记录器和处理器,以便更为精准地输出日志记录。同时过滤器还可以负责记录日志的上下文处理,比如技术、附加消息等。

Filters有如下的成员属性和方法。

  • __init__(name=''):初始化过滤器,name代表传入记录器或处理器名字,处理记录对象时会对比对象的处理器或记录器的名字与传入的name。若name为空则代表允许经过所有过滤器。
  • filter(record):用于判断记录日志对象record是否需要记录。

过滤逻辑支持一个过滤器对象和过滤函数,执行逻辑为:检查filter对象是否有filter属性,若有假定其过滤函数为filter,并调用filter方法,否则假设它是可调用的单个函数,并以记录对象为单个参数进行调用。

下面示例如何自定义过滤器和过滤函数。

python
import logging


class WordFilter(logging.Filter):

	def __init__(self, word, name=''):
		"""
		初始化一个字符检测过滤器
		过滤日志消息中含有指定字符的消息
		:param word: 要过滤的字符串
		:param name: 记录器或处理器名字
		"""
		super().__init__(name)
		self.name = name
		self.nlen = len(name)
		self.word = word

	def filter(self, record: object) -> bool:
		if self.nlen == 0 or self.name != record.name:
			return True
		if self.word in record.msg:
			return False
		else:
			return True


logger_a = logging.getLogger('a')  # 获取一个名为a的记录器
logger_b = logging.getLogger('b')  # 获取一个名为b的记录器
logger_a.setLevel(logging.DEBUG)  # 给a记录器设置记录日志等级
logger_b.setLevel(logging.DEBUG)  # 给b记录器设置记录日志等级

ch = logging.StreamHandler()  # 创建一个处理器
ch.setLevel(logging.DEBUG)  # 给处理器设置日志等级
fmt = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s-%(name)s")  # 创建一个格式化器
ch.setFormatter(fmt)  # 将格式化器添加到处理器

logger_a.addHandler(ch)  # 给a记录器添加处理器
logger_b.addHandler(ch)  # 给b记录器添加处理器

wordfilter = WordFilter("名字", "a")
logger_a.addFilter(wordfilter)  # 给a记录器添加过滤器wordfilter
logger_b.addFilter(wordfilter)  # 给b记录器添加过滤器wordfilter

if __name__ == '__main__':
	logger_a.info("我是记录器,我的名字是a,我添加了wordfilter过滤器")
	logger_b.info("我是记录器,我的名字是b,我添加了wordfilter过滤器")
	logger_a.removeFilter(wordfilter)
	logger_a.info("我是记录器,我的名字是a,我移除了wordfilter过滤器")

上述结果中,三条日志只处理了两条是因为自定义过滤器在过滤目标处理器a中生效,检查是否含关键词时第一条日志记录被过滤掉了。

python
import logging

def wordfilter(record: object) -> bool:
    """
    自定义过滤器函数
    :param record:
    :return:
    """
    if "名字" in record.msg:
        return False
    else:
        return True

logger = logging.getLogger(__name__)  # 创建一个过滤器
logger.setLevel(logging.DEBUG)  # 设置处理的日志级别
ch = logging.StreamHandler()  # 创建一个处理器
ch.setLevel(logging.DEBUG)  # 设置处理器的日志级别
fmt = logging.Formatter(
    "%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s-%(name)s")  # 创建一个格式化器
ch.setFormatter(fmt)  # 将格式化器添加到处理器
logger.addHandler(ch)  # 将处理器添加到记录器
logger.addFilter(wordfilter)  # 将自定义过滤函数添加到记录器

if __name__ == '__main__':
    logger.info("我是记录器,我的名字是__main__,我添加了wordfilter过滤器函数")
    logger.removeFilter(wordfilter)
    logger.info("我是记录器,我的名字是__main__,我移除了wordfilter过滤器")

LogRecord对象

LogRecord对象在Logger每次记录时自动创建,并且可以通过makeLogRecord函数手动创建。它在logging四大组件之间流动,用于传递日志消息。它是连通日志记录四大组件的重要对象。

LogRecord对象的属性和方法如下。

  • __init__(name, level, pathname, lineno, msg, args, esc_info, func=None, sinfo=None):产生LogRecord的初始方法,相关参数解释如下。
    • name:产生LogRecord实例的事件记录器的名称。
    • level:日志记录事件的数字级别。该属性将转换为LogRecord的levelno数字值和levelname相应的级别名称。
    • pathname:日志记录时调用的源文件的完整路径。
    • lineno:记录日志时代码的行号。
    • msg:事件描述信息,可能是带有占位符的格式字符串。
    • args:合并到msg参数中的可变数据,可以此获得事件描述。
    • exc_info:具有当前异常信息的异常元组。
    • func:从中调用日志记录的函数或方法的名称。
    • sinfo:一个文本字符串,表示从当前线程中的堆栈底部到日志记录调用的堆栈信息。
  • getMessage():用于返回LogRecord对象实例化时的消息内容。

LogRecord实例化对象的全部属性见下表。

属性名称格式说明示例值
args不需要格式化将参数的元组合并到msg,生成消息()
asctime%(asctime)sLogRecird创建时的格式化可读时间2020-02-27 12:00:00, 054
created%(created)fLogRecord创建的时间戳2939848123.24241
exc_info不需要格式化异常信息元组或者NoneNone
filename%(filename)s路径名的文件名部分log.py
funcName%(funcName)s包含创建日志的函数名称<module>
levelname%(levelname)s文本日志记录消息的级别DEBUG
levelno%(levelno)s日志记录的级别表示数字20
lineno%(lineno)d发出日志记录调用的源码符号18
message%(message)s记录的消息。是调用Formatter.format函数填充args函数后得到的发送日志服务器
module%(module)s模块(filename的名称部分)log
msecs%(msecs)dLogRecord创建时间的毫秒部分53.01134490866797
msg不需要格式化原始日志记录调用中传递的格式字符串发送日志服务器
name%(name)s对应记录器的名称__main__
pathname%(pathname)s调用日志记录的源文件的完整路径A:/log/log.py
process%(process)d进程ID16011
processName%(processName)s进程名MainProcess
relativeCreated%(relativeCreated)d创建LogRecord时相对于加载日志模块的时间3005.11234123231
stack_info不需要格式化当前线程中堆栈底部的堆栈帧信息,直至导致创建此纪录的日志调用的堆栈帧None
thread%(thread)d线程ID844
threadName%(threadName)s线程名MainThread

构造LogRecord不能直接使用上方的属性作为传入参数实例化LogRecord类,一般使用logging.makeRecord({})创建一个没有任何属性的LogRecord对象,然后使用update函数更新这个示例中__dict__的全部参数,即可实现恢复一个LogRecord实例化对象,但是要注意这个实例的字段的数据类型不能改变。

日志的配置

日志配置有以下方法:

  • 使用配置方法显式创建记录器,处理程序和格式化程序;
  • 使用fileConfig()函数读取日志配置文件。
  • 使用dictConfig()函数读取配置信息字典。

显式配置

该方法指直接使用logging四大组件的相关设置函数来配置。在之前的例子中都是在代码中重复使用配置,如使用logger=logging.getLogger()获取记录器,这种代码放在业务代码中很不整洁,因此常用的方法是将这些配置代码放到单独的文件中,需要时导入即可。多次导入记录器不会重复实例化配置,它们的导入都是第一次配置的记录器。

通过fileConfig()配置

该函数可以从.conf文件中读取日志配置,该函数位于logging.config模块。conf文件必须包含名为[loggers]、[handlers]、[formatters]的节点,这些节点通过名称标识定义出每种类型的组件,其节点下的项以keys=value1,value2等格式组成。

新建一个log.conf文件,写入下方配置项。

ini
[loggers]  # 声明是loggers节点
keys = root,log02
# 配置两个记录器分别是root和log02
[handlers]  # 声明是handlers节点
keys = hand01
# 配置一个处理器
[formatters]  # 声明是formatters节点
keys = fmt
# 配置一个格式化器fmt
[logger_root]  # 对root记录器配置
level = NOTSET
handlers = hand01

[logger_log02]  # 对log02记录器配置
level = DEBUG
handlers = hand01
propagate = 1
qualname = compiler.parser


[handler_hand01]  # 对hand01处理器配置
class = StreamHandler
level = DEBUG
formatter = fmt
args = (sys.stdout,)

[formatter_fmt]  # 对格式化器fmt进行配置
format = %(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s-%(name)s
datefmt =
class = logging.Formatter

然后创建一个读取该配置文件的log.py。

python
import logging.config

logging.config.fileConfig('log.conf')
logger = logging.getLogger('log02')

if __name__ == '__main__':
    logger.info("读取配置")
运行结果
2023-07-07 23:06:12,181 - 3-13-2 fileConfig读取配置.py[line:7] - INFO: 读取配置-log02

通过dictConfig读取

dictConfig可以从字典中获取日志记录配置,该函数位于logging.config中。dictConfig同时支持在dict中导入外部对象。在配置处理器、过滤器、格式化器中使用自定义对象用特殊键“()”,兹定于i对象所需的构造参数以及键值对在配置字典中列出。当引用外部对象时使用“ext://”+自定义对象导入路径的方式,配置系统会自动分割字符串,并使用常规导入机制处理值的剩余部分。dictConfig还支持引用配置文件内部的对象。对于日志系统内部的对象通过提供ID引用或隐式转换,如DEBUG会自动换成logging.DEBUG。若为用户自定义的对象就要通过“dfg://”+相对配置文件来引用,该位置即字典获取值的路径。如“cfg://handler.email”指的是config_dict[]'handlers']['email']。

dictConfig所需的配置字典应包含下列键。

  1. version指版本号。

  2. formatters:格式化器,其值为dict类型,其中键为ID,值为含有format、datefmt的配置字典。

json
"fmt": {
      "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    }
  1. filters:过滤器,值为dict类型,键位ID,值位含有name固定字段的配置字典。
json
"filters": {
    "wordfilter": {
      "name": "WordFilter"
    }
  }
  1. handlers:处理器,值位dict类型,键为ID,值为含有clas、level、formatter、filters等字段的配置字典。
json
"handlers": {
    "hand01": {
      "class": "logging.StreamHandler",
      "level": "INFO",
      "filters": [
        "wordfilter"
      ],
      "formatter": "fmt"
    }
  }
  1. loggers:记录器,值为dict类型,其中键为记录器名称,值为含有propagate、level、filters、handlers等字段的配置字典。
json
"loggers": {
    "log02": {
      "propagate": 1,
      "level": "DEBUG",
      "handlers": ["hand01"]
    },
    "root": {
      "level": "DEBUG",
      "handlers": ["hand01"]
    }
  }
  1. root:根记录器,配置和logger相同,但是配置字典不能使用propagate属性。

  2. incremental:用来决定是否替换现有配置,默认为False。

  3. disable_existing_logger:用于判断是否禁用现有的记录器,默认为True。

python
from logging import getLogger, config


data = {
    "word": "去除",  # 一个过滤关键字
    "version": 1,
    "disable_existing_loggers": True,
    "formatters": {
        "fmt": {
            "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
        }
    },
    "filters": {
        "wordfilter": {
            "()": "ext://自定义类过滤器.WordFilter", "word": "名字", "name": "log02"
        },
        "wordfilter2": {
            "()": "ext://自定义类过滤器.WordFilter", "word": "cfg://word", "name": "log02"
        }
    },
    "handlers": {
        "hand01": {
            "class": "logging.StreamHandler",
            "level": "DEBUG",
            "formatter": "fmt",
            "filters": [
                "wordfilter", "wordfilter2"
            ],
        }
    },
    "loggers": {
        "log02": {
            "propagate": 1,
            "level": "DEBUG",
            "handlers": ["hand01"],
        },
        "root": {
            "level": "DEBUG",
            "handlers": ["hand01"]
        }
    }
}
config.dictConfig(data)
logger = getLogger('log02')

if __name__ == '__main__':
    logger.info("本条将不被过滤,不含过滤关键字")
    logger.info("本条将过滤,含过滤关键字:名字")
    logger.info("本条将过滤,含过滤关键字:去除")
运行结果
2020-03-01 10:02:29,968 - log02 - INFO - 本条将不被过滤,不含过滤关键字

为方便开发而创建的常用库指南