在Python项目开发中,默认的日志输出通常比较简单,无法满足不同业务场景下的日志管理需求,比如需要同时输出到控制台和文件、自定义日志的时间格式和字段、区分不同模块的日志级别等,这时候就需要实现自定义日志。Python内置的logging模块提供了完整的日志功能扩展能力,支持开发者灵活配置日志的各个环节。

自定义日志的核心组件
要实现自定义日志,首先需要了解logging模块的四个核心组件,所有自定义配置都是围绕这些组件展开的:
- Logger:日志器,是日志系统的入口,负责接收日志消息并传递给对应的处理器
- Handler:处理器,决定日志消息的输出位置,比如控制台、文件、网络等
- Formatter:格式化器,定义日志消息的最终输出格式,包括时间、级别、内容等字段
- Filter:过滤器,用于对日志消息做更精细的过滤,比如只输出特定模块的日志
基础自定义日志实现步骤
1. 创建并配置日志器
首先创建日志器实例,设置全局的日志级别,日志级别从低到高分为DEBUG、INFO、WARNING、ERROR、CRITICAL,只有大于等于设置级别的日志才会被处理。
import logging
# 创建日志器,指定日志器名称
logger = logging.getLogger("my_custom_logger")
# 设置日志级别为DEBUG,所有级别大于等于DEBUG的日志都会被处理
logger.setLevel(logging.DEBUG)
2. 自定义格式化器
默认的日志格式比较简单,我们可以自定义格式化器,添加时间、日志级别、模块名、行号、日志内容等信息,时间格式也可以按需调整。
# 定义日志输出格式 log_format = "%(asctime)s - %(name)s - %(levelname)s - %(module)s:%(lineno)d - %(message)s" # 创建格式化器,指定时间格式为年-月-日 时:分:秒 formatter = logging.Formatter(log_format, datefmt="%Y-%m-%d %H:%M:%S")
3. 添加处理器
处理器决定日志的输出位置,常用的有控制台处理器StreamHandler和文件处理器FileHandler,我们可以同时添加多个处理器,实现日志多位置输出。
# 创建控制台处理器,输出到控制台
console_handler = logging.StreamHandler()
# 设置控制台处理器的日志级别为INFO,只输出INFO及以上级别的日志到控制台
console_handler.setLevel(logging.INFO)
# 给控制台处理器绑定格式化器
console_handler.setFormatter(formatter)
# 创建文件处理器,输出到指定文件,encoding指定编码避免中文乱码
file_handler = logging.FileHandler("app.log", encoding="utf-8")
# 设置文件处理器的日志级别为DEBUG,所有DEBUG及以上级别的日志都写入文件
file_handler.setLevel(logging.DEBUG)
# 给文件处理器绑定格式化器
file_handler.setFormatter(formatter)
# 将处理器添加到日志器
logger.addHandler(console_handler)
logger.addHandler(file_handler)
4. 测试自定义日志
配置完成后,就可以使用日志器输出不同级别的日志,验证自定义配置是否生效。
# 输出不同级别的日志
logger.debug("这是一条调试级别的日志")
logger.info("这是一条信息级别的日志")
logger.warning("这是一条警告级别的日志")
logger.error("这是一条错误级别的日志")
logger.critical("这是一条严重错误级别的日志")
运行上述代码后,控制台会输出INFO及以上级别的日志,而app.log文件中会记录所有DEBUG及以上级别的日志,日志格式和我们自定义的一致。
进阶自定义:添加过滤器
如果需要更精细的日志过滤,比如只输出某个模块或者包含特定关键词的日志,可以自定义过滤器。过滤器需要实现filter方法,返回True表示日志会被处理,返回False则会被过滤掉。
class CustomFilter(logging.Filter):
def filter(self, record):
# 只处理日志内容中包含"业务"关键词的日志
if "业务" in record.getMessage():
return True
return False
# 创建过滤器实例
custom_filter = CustomFilter()
# 给文件处理器添加过滤器
file_handler.addFilter(custom_filter)
添加过滤器后,只有内容包含"业务"的日志才会被写入app.log文件,其他日志即使级别符合要求也会被过滤。
自定义日志的常见问题
避免日志重复输出
如果多次调用logging.getLogger获取同一个名称的日志器,并且重复添加处理器,会导致日志重复输出。可以在添加处理器前先清空已有的处理器:
# 清空日志器已有的处理器,避免重复添加 logger.handlers.clear() # 再添加新的处理器 logger.addHandler(console_handler) logger.addHandler(file_handler)
日志文件的滚动切割
如果项目运行时间较长,单个日志文件会变得很大,不方便查看。可以使用RotatingFileHandler实现按文件大小切割,或者使用TimedRotatingFileHandler实现按时间切割。
from logging.handlers import RotatingFileHandler
# 创建按大小切割的文件处理器,最大文件大小10MB,最多保留5个备份文件
rotate_handler = RotatingFileHandler(
"app_rotate.log",
maxBytes=10*1024*1024, # 10MB
backupCount=5,
encoding="utf-8"
)
rotate_handler.setLevel(logging.DEBUG)
rotate_handler.setFormatter(formatter)
logger.addHandler(rotate_handler)
| 处理器类型 | 作用 | 适用场景 |
|---|---|---|
| StreamHandler | 输出日志到控制台、文件流等 | 开发调试时输出日志到控制台 |
| FileHandler | 输出日志到指定文件 | 生产环境记录日志到文件 |
| RotatingFileHandler | 按文件大小切割日志文件 | 日志文件大小可控的场景 |
| TimedRotatingFileHandler | 按时间切割日志文件 | 需要按天、按小时归档日志的场景 |
注意:不要在多个模块中重复创建同名的日志器并添加处理器,建议将自定义日志的配置封装成函数或者类,在项目的入口处统一初始化,其他模块直接获取日志器使用即可。
# 封装自定义日志初始化函数
def init_custom_logger():
logger = logging.getLogger("my_custom_logger")
logger.setLevel(logging.DEBUG)
# 避免重复添加处理器
if not logger.handlers:
formatter = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)
file_handler = logging.FileHandler("app.log", encoding="utf-8")
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
logger.addHandler(console_handler)
logger.addHandler(file_handler)
return logger
# 其他模块中直接使用
logger = logging.getLogger("my_custom_logger")
logger.info("其他模块的日志信息")