Python的logging模块是标准库提供的专业日志解决方案,相比print语句,它支持分级日志、多输出目标、灵活格式化等特性,能够满足从开发调试到生产运维的全场景日志需求。合理的配置可以让日志既不会冗余也不会缺失关键信息,有效提升问题排查效率。

logging模块核心组件
logging模块的运行依赖四个核心组件,理解它们的作用和关系是正确配置的基础:
- Logger:日志的记录器,是开发者直接调用的对象,负责产生日志消息,支持设置日志级别过滤消息。
- Handler:日志的处理器,负责将Logger产生的日志消息输出到指定目标,比如控制台、文件、网络等,一个Logger可以绑定多个Handler。
- Formatter:日志的格式化器,负责定义日志消息的最终输出格式,比如是否包含时间、日志级别、模块信息等。
- Filter:日志的过滤器,提供更细粒度的日志过滤能力,可以决定哪些日志消息可以被处理。
基础配置方法
对于简单的脚本场景,使用logging.basicConfig方法就可以快速完成配置,该方法会为根Logger设置默认的Handler和Formatter。
import logging
# 基础配置:设置日志级别为INFO,输出格式包含时间、级别、消息内容,输出到控制台
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# 测试不同级别的日志
logging.debug('这是调试级别的日志,不会被输出')
logging.info('这是信息级别的日志')
logging.warning('这是警告级别的日志')
logging.error('这是错误级别的日志')
上述代码中,日志级别设置为INFO后,DEBUG级别的日志会被过滤,不会输出到控制台。format参数中的占位符含义如下:
- %(asctime)s:日志记录的时间
- %(levelname)s:日志的级别名称
- %(message)s:日志的具体内容
- %(filename)s:产生日志的文件名
- %(lineno)d:产生日志的代码行号
高级配置:多Handler与文件输出
实际项目中通常需要同时把日志输出到控制台和文件,并且不同场景的日志级别可以分开设置,这时候就需要手动创建Logger、Handler和Formatter进行配置。
import logging
import os
def setup_logger():
# 创建Logger对象,设置全局最低日志级别为DEBUG
logger = logging.getLogger('my_app')
logger.setLevel(logging.DEBUG)
# 避免日志重复处理
logger.propagate = False
# 创建控制台Handler,设置级别为INFO
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# 创建文件Handler,设置级别为DEBUG,日志文件按天轮转
log_dir = 'logs'
if not os.path.exists(log_dir):
os.makedirs(log_dir)
file_handler = logging.FileHandler(os.path.join(log_dir, 'app.log'), encoding='utf-8')
file_handler.setLevel(logging.DEBUG)
# 创建统一的格式化器
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
)
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# 给Logger添加Handler
logger.addHandler(console_handler)
logger.addHandler(file_handler)
return logger
# 使用自定义Logger
logger = setup_logger()
logger.debug('调试信息,只会写入文件')
logger.info('普通信息,会同时输出到控制台和文件')
logger.error('错误信息,会同时输出到控制台和文件')
logging模块最佳实践
1. 不要直接使用根Logger
直接使用logging.info等方法是操作根Logger,容易导致不同模块的日志配置互相干扰,建议每个模块创建自己的Logger,命名使用模块名,比如logging.getLogger(__name__)。
2. 合理设置日志级别
不同环境的日志级别应该区分开:开发环境可以设置为DEBUG,方便排查问题;测试环境设置为INFO;生产环境设置为WARNING或者ERROR,避免产生过多冗余日志占用磁盘空间。
3. 日志文件轮转
长期运行的项目如果日志一直写入同一个文件,会导致文件过大,难以查看和管理,建议使用RotatingFileHandler或者TimedRotatingFileHandler实现日志轮转,比如按文件大小或者按时间切割日志文件。
import logging
from logging.handlers import RotatingFileHandler
# 按文件大小轮转的Handler,单个文件最大10MB,最多保留5个备份文件
rotating_handler = RotatingFileHandler(
'app.log',
maxBytes=10*1024*1024, # 10MB
backupCount=5,
encoding='utf-8'
)
4. 日志内容要包含关键信息
日志消息应该包含足够的上下文信息,比如用户ID、请求ID、操作参数等,避免只写模糊的描述,比如不要只写"操作失败",而要写"用户ID:123的订单创建操作失败,原因:库存不足"。
5. 异常日志要记录完整堆栈
捕获异常记录日志时,要使用exc_info=True参数,这样会把完整的异常堆栈信息记录到日志中,方便定位问题根因。
try:
1 / 0
except Exception as e:
logger.error('发生除零错误', exc_info=True)
常见配置误区
- 重复添加Handler:如果多次调用addHandler,会导致同一条日志被输出多次,需要注意避免,或者在添加前检查Handler是否已经存在。
- 日志级别设置混乱:Handler的级别不能低于Logger的级别,否则Handler的级别设置不会生效,比如Logger级别是ERROR,Handler级别是INFO,那么INFO级别的日志还是不会被处理。
- 忘记设置编码:文件Handler默认编码可能不是utf-8,写入中文会出现乱码,建议显式设置encoding='utf-8'。
logging模块的配置需要根据项目实际需求灵活调整,没有绝对统一的方案,但遵循上述最佳实践可以避免大部分常见问题,让日志功能真正发挥价值。