Python 的 logging
模块是一个功能强大且灵活的日志记录系统,用于跟踪程序的运行状态、调试和错误分析。本文将从基础到高级,详细介绍如何使用 logging
模块的各项功能。
目录
- [为什么使用 Logging 模块](#为什么使用 Logging 模块)
- 基础使用
- 日志级别
- 基本配置
basicConfig
- 简单日志记录示例
- Logger、Handler 和 Formatter
- Logger
- Handler
- Formatter
- 配置日志记录器
- 使用不同的 Handler
- 添加多个 Handler
- 日志格式化
- 格式化字符串
- 自定义 Formatter
- 高级功能
- RotatingFileHandler 和 TimedRotatingFileHandler
- 过滤器
- 日志记录到多个目的地
- 最佳实践
- 示例代码
- 总结
为什么使用 Logging 模块
在开发过程中,打印日志是调试和监控程序运行的重要手段。虽然可以使用 print
函数进行简单的输出,但 logging
模块提供了更为灵活和强大的日志记录功能,包括不同的日志级别、日志输出到不同的目的地、日志格式化、日志轮转等。
基础使用
日志级别
logging
模块定义了以下标准日志级别,从低到高依次为:
DEBUG
: 最详细的信息,通常只在诊断问题时使用。INFO
: 确认程序按预期运行的信息。WARNING
: 指示某些意外情况或即将发生的问题,但程序仍在继续运行。ERROR
: 更严重的问题,程序无法执行某些功能。CRITICAL
: 非常严重的问题,程序可能无法继续运行。
基本配置 basicConfig
basicConfig
是配置日志记录的简便方法,适用于简单的日志需求。它可以设置日志级别、日志格式、日志输出位置等。
import logging
logging.basicConfig(level=logging.INFO)
logging.debug("这是一个调试信息")
logging.info("这是一个普通信息")
logging.warning("这是一个警告信息")
logging.error("这是一个错误信息")
logging.critical("这是一个严重错误信息")
在上述代码中,只有 INFO
及以上级别的日志会被输出,因为 level
设置为 INFO
。
简单日志记录示例
import logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logging.debug("调试信息")
logging.info("普通信息")
logging.warning("警告信息")
logging.error("错误信息")
logging.critical("严重错误信息")
输出示例:
2024-04-27 12:00:00,000 - DEBUG - 调试信息
2024-04-27 12:00:00,001 - INFO - 普通信息
2024-04-27 12:00:00,002 - WARNING - 警告信息
2024-04-27 12:00:00,003 - ERROR - 错误信息
2024-04-27 12:00:00,004 - CRITICAL - 严重错误信息
Logger、Handler 和 Formatter
logging
模块的核心概念包括 Logger、Handler 和 Formatter。理解它们之间的关系对于有效配置日志记录器至关重要。
Logger
Logger 是应用程序中记录日志的对象。每个 Logger 都有一个名称(通常是模块名),并且可以有不同的日志级别。
创建 Logger 的方式:
import logging
logger = logging.getLogger(__name__)
Handler
Handler 负责将日志记录输出到不同的目的地,如控制台、文件、网络等。常见的 Handler 包括:
StreamHandler
: 输出到控制台或其他流。FileHandler
: 输出到文件。RotatingFileHandler
: 输出到文件,并支持日志轮转。TimedRotatingFileHandler
: 按时间轮转日志文件。
Formatter
Formatter 定义日志的输出格式,包括时间、日志级别、消息等。
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
配置日志记录器
通过组合 Logger、Handler 和 Formatter,可以灵活地配置日志记录。
使用不同的 Handler
示例:同时输出到控制台和文件
import logging
# 创建 Logger
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)
# 创建控制台 Handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# 创建文件 Handler
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)
# 创建 Formatter 并设置给 Handler
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# 将 Handler 添加到 Logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# 记录日志
logger.debug("这是一条调试信息")
logger.info("这是一条普通信息")
logger.warning("这是一条警告信息")
logger.error("这是一条错误信息")
logger.critical("这是一条严重错误信息")
在上述代码中:
DEBUG
级别的日志会被记录到app.log
文件中。INFO
及以上级别的日志会输出到控制台。
添加多个 Handler
可以为同一个 Logger 添加多个 Handler,以实现日志输出到多个目的地。例如,输出到控制台和多个文件。
import logging
logger = logging.getLogger('multi_handler_logger')
logger.setLevel(logging.DEBUG)
# 控制台 Handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING)
# 文件 Handler1
file_handler1 = logging.FileHandler('debug.log')
file_handler1.setLevel(logging.DEBUG)
# 文件 Handler2
file_handler2 = logging.FileHandler('error.log')
file_handler2.setLevel(logging.ERROR)
# 设置 Formatter
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
file_handler1.setFormatter(formatter)
file_handler2.setFormatter(formatter)
# 添加 Handler 到 Logger
logger.addHandler(console_handler)
logger.addHandler(file_handler1)
logger.addHandler(file_handler2)
# 记录日志
logger.debug("调试信息")
logger.info("普通信息")
logger.warning("警告信息")
logger.error("错误信息")
logger.critical("严重错误信息")
在此示例中:
- 所有
DEBUG
及以上级别的日志记录到debug.log
。 - 所有
ERROR
及以上级别的日志记录到error.log
。 - 所有
WARNING
及以上级别的日志输出到控制台。
日志格式化
通过 Formatter,可以自定义日志的输出格式,包含时间、模块名、日志级别、消息等信息。
格式化字符串
Formatter
使用格式化字符串定义日志的结构,常用的格式化字段包括:
%(asctime)s
: 时间%(name)s
: Logger 名称%(levelname)s
: 日志级别%(message)s
: 日志消息%(filename)s
: 文件名%(lineno)d
: 行号%(funcName)s
: 函数名
示例:
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
自定义 Formatter
可以通过继承 logging.Formatter
自定义日志格式化器,实现更复杂的格式需求。
import logging
class CustomFormatter(logging.Formatter):
def format(self, record):
record.msg = f"自定义前缀: {record.msg}"
return super().format(record)
# 使用自定义 Formatter
formatter = CustomFormatter('%(asctime)s - %(levelname)s - %(message)s')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger = logging.getLogger('custom_formatter')
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
logger.info("这是一个测试信息")
输出:
2024-04-27 12:00:00,000 - INFO - 自定义前缀: 这是一个测试信息
高级功能
RotatingFileHandler 和 TimedRotatingFileHandler
RotatingFileHandler
RotatingFileHandler
允许在日志文件达到一定大小后进行轮转,生成新的日志文件。
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger('rotating_logger')
logger.setLevel(logging.INFO)
# 设置 RotatingFileHandler,最大 1MB,保留 3 个备份
handler = RotatingFileHandler('rotating.log', maxBytes=1*1024*1024, backupCount=3)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
for i in range(10000):
logger.info(f"日志信息 {i}")
TimedRotatingFileHandler
TimedRotatingFileHandler
根据时间进行日志轮转,例如每天、每小时等。
import logging
from logging.handlers import TimedRotatingFileHandler
logger = logging.getLogger('timed_rotating_logger')
logger.setLevel(logging.INFO)
# 设置 TimedRotatingFileHandler,每天轮转,保留 7 天的日志
handler = TimedRotatingFileHandler('timed_rotating.log', when='midnight', interval=1, backupCount=7)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.info("这是一个定时轮转的日志信息")
过滤器
过滤器用于更细粒度地控制哪些日志记录被输出。可以基于 Logger 名称、日志级别等条件进行过滤。
import logging
class LevelFilter(logging.Filter):
def __init__(self, level):
super().__init__()
self.level = level
def filter(self, record):
return record.levelno == self.level
logger = logging.getLogger('filter_logger')
logger.setLevel(logging.DEBUG)
# Handler1 只记录 ERROR 级别
error_handler = logging.StreamHandler()
error_handler.setLevel(logging.ERROR)
error_filter = LevelFilter(logging.ERROR)
error_handler.addFilter(error_filter)
logger.addHandler(error_handler)
# Handler2 记录所有级别
all_handler = logging.StreamHandler()
all_handler.setLevel(logging.DEBUG)
logger.addHandler(all_handler)
logger.debug("调试信息")
logger.info("普通信息")
logger.warning("警告信息")
logger.error("错误信息")
logger.critical("严重错误信息")
在上述代码中:
error_handler
只输出ERROR
级别的日志。all_handler
输出所有级别的日志。
日志记录到多个目的地
通过添加多个 Handler,可以将日志同时记录到多个目的地,例如控制台、文件、远程服务器等。
import logging
from logging.handlers import SMTPHandler
logger = logging.getLogger('multi_destination_logger')
logger.setLevel(logging.DEBUG)
# 控制台 Handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
# 文件 Handler
file_handler = logging.FileHandler('multi_destination.log')
file_handler.setLevel(logging.INFO)
# 邮件 Handler(仅在 ERROR 及以上级别发送邮件)
mail_handler = SMTPHandler(
mailhost=('smtp.example.com', 587),
fromaddr='error@example.com',
toaddrs=['admin@example.com'],
subject='应用程序错误',
credentials=('username', 'password'),
secure=()
)
mail_handler.setLevel(logging.ERROR)
# 设置 Formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
mail_handler.setFormatter(formatter)
# 添加 Handler 到 Logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)
logger.addHandler(mail_handler)
# 记录日志
logger.info("这是普通信息")
logger.error("这是一个错误信息")
在此示例中:
- 所有
DEBUG
及以上级别的日志输出到控制台。 - 所有
INFO
及以上级别的日志记录到文件。 - 所有
ERROR
及以上级别的日志通过邮件发送。
最佳实践
-
模块化 Logger: 每个模块使用自己的 Logger,通常使用
__name__
作为 Logger 名称。import logging logger = logging.getLogger(__name__)
-
避免重复日志: 确保每个 Handler 只被添加一次,防止日志重复输出。
-
配置文件管理: 对于复杂的日志配置,使用配置文件(如
logging.config
模块支持的配置文件)进行管理,便于维护。 -
合理设置日志级别: 开发阶段使用较低的日志级别(如
DEBUG
),生产环境中适当提高日志级别以减少日志量。 -
使用 Rotating Handler: 防止日志文件过大,使用轮转 Handler(如
RotatingFileHandler
或TimedRotatingFileHandler
)。 -
敏感信息处理: 避免在日志中记录敏感信息,如密码、密钥等。
示例代码
以下是一个综合示例,展示了如何配置 Logger、多个 Handler、Formatter 和过滤器。
import logging
from logging.handlers import RotatingFileHandler
# 创建 Logger
logger = logging.getLogger('comprehensive_logger')
logger.setLevel(logging.DEBUG)
# 创建 Formatter
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# 创建控制台 Handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)
# 创建 RotatingFileHandler
file_handler = RotatingFileHandler('comprehensive.log', maxBytes=5*1024*1024, backupCount=2)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
# 添加 Handler 到 Logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# 添加过滤器,只记录 WARNING 及以上级别
class WarningFilter(logging.Filter):
def filter(self, record):
return record.levelno >= logging.WARNING
warning_filter = WarningFilter()
file_handler.addFilter(warning_filter)
# 记录日志
logger.debug("这是调试信息,不会在文件中显示")
logger.info("这是普通信息,不会在文件中显示")
logger.warning("这是警告信息,会在文件中显示")
logger.error("这是错误信息,会在文件中显示")
logger.critical("这是严重错误信息,会在文件中显示")
在此示例中:
- 控制台输出
INFO
及以上级别的日志。 - 文件输出
WARNING
及以上级别的日志,并使用RotatingFileHandler
进行日志轮转。
总结
Python 的 logging
模块提供了强大且灵活的日志记录功能,适用于各种规模和复杂度的项目。通过合理配置 Logger、Handler 和 Formatter,可以实现多样化的日志记录需求,帮助开发者更好地调试和维护程序。掌握 logging
模块的使用,有助于提升代码质量和可维护性。