好的,我们开始今天的讲座:Python日志系统Loguru和Logging的高级配置与实践。
引言:日志的重要性及选择
日志是软件开发中不可或缺的一部分。它记录了程序的运行状态、错误信息、调试信息等,对于诊断问题、监控性能、审计安全至关重要。Python 提供了两种主要的日志解决方案:标准库 logging
和第三方库 Loguru
。logging
历史悠久,功能强大,但配置较为繁琐。Loguru
则以其简洁易用、功能丰富的特性受到越来越多开发者的喜爱。本次讲座将深入探讨这两种日志系统的高级配置和实践,帮助大家根据实际需求选择合适的工具。
第一部分:标准库 logging
的高级配置与实践
logging
模块是 Python 内置的日志记录工具。虽然默认配置较为基础,但通过自定义 Handler、Formatter 和 Filter,可以实现非常灵活和强大的日志管理。
1.1 Handler:日志输出的目标地
Handler 负责将日志记录输出到不同的目标,例如控制台、文件、网络等。logging
模块提供了多种 Handler,例如 StreamHandler
(输出到控制台)、FileHandler
(输出到文件)、RotatingFileHandler
(按大小滚动文件)、TimedRotatingFileHandler
(按时间滚动文件) 等。
-
FileHandler:记录到文件
import logging # 创建 logger logger = logging.getLogger('my_logger') logger.setLevel(logging.INFO) # 创建 FileHandler,将日志写入文件 file_handler = logging.FileHandler('my_app.log') file_handler.setLevel(logging.INFO) # 设置 Handler 的日志级别 # 创建 Formatter,定义日志格式 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') file_handler.setFormatter(formatter) # 将 Handler 添加到 logger logger.addHandler(file_handler) # 记录日志 logger.info('This is an info message.') logger.warning('This is a warning message.')
-
RotatingFileHandler:按大小滚动日志文件
import logging from logging.handlers import RotatingFileHandler logger = logging.getLogger('my_logger') logger.setLevel(logging.INFO) # 创建 RotatingFileHandler,设置文件大小和备份文件数量 rotating_handler = RotatingFileHandler('my_app.log', maxBytes=1024*1024, backupCount=5) # 1MB, 5 backups rotating_handler.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') rotating_handler.setFormatter(formatter) logger.addHandler(rotating_handler) for i in range(1000): logger.info(f'This is message number {i}.')
-
TimedRotatingFileHandler:按时间滚动日志文件
import logging from logging.handlers import TimedRotatingFileHandler logger = logging.getLogger('my_logger') logger.setLevel(logging.INFO) # 创建 TimedRotatingFileHandler,设置滚动间隔 (midnight, hourly, daily, weekly) timed_handler = TimedRotatingFileHandler('my_app.log', when='midnight', interval=1, backupCount=7) # Daily, keep 7 days timed_handler.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') timed_handler.setFormatter(formatter) logger.addHandler(timed_handler) logger.info('This is a log message.')
1.2 Formatter:定义日志格式
Formatter 决定了日志记录的输出格式。logging
模块提供了 Formatter
类,可以自定义日志格式。
-
基本格式化
import logging logger = logging.getLogger('my_logger') logger.setLevel(logging.INFO) handler = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) logger.addHandler(handler) logger.info('This is a log message.')
常用的格式化字符串包括:
格式化字符串 含义 %(asctime)s 日志记录的时间 %(name)s Logger 的名称 %(levelname)s 日志级别 (DEBUG, INFO, WARNING, ERROR, CRITICAL) %(message)s 日志消息 %(filename)s 文件名 %(lineno)d 行号 %(module)s 模块名 %(funcName)s 函数名 -
自定义格式化
你可以创建自定义的 Formatter 类,实现更复杂的格式化逻辑。
import logging class MyFormatter(logging.Formatter): def format(self, record): # 自定义格式化逻辑 return f'[{record.asctime}] [{record.levelname}] - {record.message} (Custom Format)' logger = logging.getLogger('my_logger') logger.setLevel(logging.INFO) handler = logging.StreamHandler() formatter = MyFormatter('%Y-%m-%d %H:%M:%S') # 传入时间格式 handler.setFormatter(formatter) logger.addHandler(handler) logger.info('This is a log message.')
1.3 Filter:过滤日志记录
Filter 用于过滤日志记录,只允许符合特定条件的日志消息被处理。
-
基本过滤
import logging class MyFilter(logging.Filter): def filter(self, record): # 只允许包含 "important" 的日志消息通过 return 'important' in record.getMessage() logger = logging.getLogger('my_logger') logger.setLevel(logging.INFO) handler = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) filter = MyFilter() handler.addFilter(filter) # 将filter添加到handler中,也可以添加到logger中 logger.addHandler(handler) logger.info('This is an important message.') logger.info('This is a regular message.')
-
基于 Logger 名称过滤
import logging # 创建两个 logger logger1 = logging.getLogger('my_app.module1') logger1.setLevel(logging.INFO) logger2 = logging.getLogger('my_app.module2') logger2.setLevel(logging.INFO) handler = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) # 只允许来自 'my_app.module1' 的日志通过 filter = logging.Filter('my_app.module1') handler.addFilter(filter) logger1.addHandler(handler) logger2.addHandler(handler) logger1.info('Message from module1.') logger2.info('Message from module2.')
1.4 配置文件的使用
logging
模块允许使用配置文件来定义日志系统的配置,这使得配置管理更加灵活和方便。
-
logging.conf 文件示例
[loggers] keys=root,exampleLogger [handlers] keys=consoleHandler,fileHandler [formatters] keys=simpleFormatter [logger_root] level=WARNING handlers=consoleHandler [logger_exampleLogger] level=INFO handlers=fileHandler qualname=exampleApp propagate=0 [handler_consoleHandler] class=StreamHandler level=DEBUG formatter=simpleFormatter args=(sys.stdout,) [handler_fileHandler] class=FileHandler level=INFO formatter=simpleFormatter args=('example.log',) [formatter_simpleFormatter] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s datefmt=
-
Python 代码中使用配置文件
import logging import logging.config logging.config.fileConfig('logging.conf') logger = logging.getLogger('exampleApp') # 使用配置文件中定义的 logger 名称 logger.info('This is an info message.') logger.warning('This is a warning message.')
1.5 日志级别:控制日志的详细程度
logging
模块定义了不同的日志级别,用于控制日志的详细程度。从低到高依次为:DEBUG
、INFO
、WARNING
、ERROR
、CRITICAL
。
日志级别 | 描述 |
---|---|
DEBUG | 提供详细的调试信息,仅在开发阶段使用。 |
INFO | 记录一般信息,例如程序启动、关闭等。 |
WARNING | 记录潜在的问题,例如资源不足、配置错误等。 |
ERROR | 记录错误信息,程序可能无法正常执行。 |
CRITICAL | 记录严重错误,程序可能崩溃。 |
你可以设置 Logger 和 Handler 的日志级别。只有级别高于或等于 Logger 和 Handler 的日志消息才会被记录。
第二部分:Loguru
的高级配置与实践
Loguru
是一个现代化的 Python 日志库,以其简洁的 API 和丰富的功能而著称。它旨在简化日志配置,并提供开箱即用的强大功能。
2.1 基本使用
from loguru import logger
logger.debug("This is a debug message.")
logger.info("This is an info message.")
logger.warning("This is a warning message.")
logger.error("This is an error message.")
logger.critical("This is a critical message.")
2.2 添加 Handler:输出到不同的目标
Loguru
允许你添加多个 Handler,将日志输出到不同的目标。
from loguru import logger
logger.add("file.log", level="INFO") # 默认保留rotation=None,保留所有日志
logger.add("error.log", level="ERROR")
logger.add("warn.log", level="WARNING", format="{time} {level} {message}")
logger.info("This is an info message.")
logger.error("This is an error message.")
2.3 滚动日志文件
Loguru
提供了方便的滚动日志文件功能,可以按大小、时间或自定义条件滚动日志文件。
-
按大小滚动
from loguru import logger logger.add("file_{time}.log", rotation="500 MB", level="INFO") # 每个文件最大 500MB # 或者 logger.add("file.log", rotation="500 MB", level="INFO") # 不带时间,会覆盖之前的文件,直到大小达到限制
-
按时间滚动
from loguru import logger logger.add("file_{time}.log", rotation="1 week", level="INFO") # 也可以是 '1 day', '1 month', '1 year'
-
自定义滚动条件
from loguru import logger def should_rotate(message, file): # 自定义滚动条件 return "Trigger Rotation" in message logger.add("file.log", rotation=should_rotate, level="INFO") logger.info("This is a log message.") logger.info("This is a log message with Trigger Rotation.") # 触发滚动
2.4 格式化日志消息
Loguru
允许你自定义日志消息的格式。
from loguru import logger
logger.add("file.log", format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}", level="INFO")
logger.info("This is an info message.")
Loguru
提供了许多内置的格式化字符串,例如:
格式化字符串 | 含义 |
---|---|
{time} | 日志记录的时间 |
{level} | 日志级别 |
{message} | 日志消息 |
{file} | 文件名 |
{line} | 行号 |
{function} | 函数名 |
2.5 拦截标准输出和标准错误
Loguru
可以拦截标准输出 (stdout) 和标准错误 (stderr),将其重定向到日志系统。
from loguru import logger
import sys
logger.add("stdout.log", level="INFO", filter=lambda record: record["extra"].get("sink") == sys.stdout)
logger.add("stderr.log", level="ERROR", filter=lambda record: record["extra"].get("sink") == sys.stderr)
logger.info("This is a message to stdout.", sink=sys.stdout)
logger.error("This is a message to stderr.", sink=sys.stderr)
print("This is a regular print statement.") # 将不会被记录,除非拦截 print
要拦截 print
函数,需要使用 sys.stdout
和 sys.stderr
:
import sys
from loguru import logger
logger.add("all.log", level="INFO", format="{time} {level} {message}")
logger.add(sys.stderr, level="ERROR, format="{time} {level} {message}") # 写入stderr
logger.add(sys.stdout, level="INFO", format="{time} {level} {message}") # 写入stdout
print("This is a regular print statement.")
logger.info("This is a log message.")
2.6 异步日志记录
Loguru
支持异步日志记录,可以提高程序的性能。
from loguru import logger
logger.add("file.log", level="INFO", enqueue=True) # 启用异步日志记录
logger.info("This is an info message.")
2.7 上下文日志记录
Loguru
允许你使用上下文管理器来记录特定范围内的日志。
from loguru import logger
with logger.contextualize(request_id="12345", user_id="john.doe"):
logger.info("Processing request.")
logger.debug("Detailed request information.")
logger.info("Request processed.")
2.8 自定义日志级别
可以自定义日志级别。
from loguru import logger
logger.level("MY_LEVEL", no=35, color="<yellow>")
logger.add("file.log", level="MY_LEVEL")
logger.log("MY_LEVEL", "This is a custom level message.")
第三部分:logging
和 Loguru
的对比与选择
特性 | logging |
Loguru |
---|---|---|
易用性 | 配置较为繁琐 | 简洁易用 |
功能 | 功能强大,但需要手动配置 | 功能丰富,开箱即用 |
扩展性 | 灵活,可以通过自定义 Handler、Formatter 扩展 | 灵活,可以通过添加 Handler、Formatter 扩展 |
性能 | 较好 | 较好,支持异步日志记录 |
社区支持 | 广泛 | 活跃 |
选择建议:
- 对于需要高度定制化和精细控制的场景,或者需要与现有
logging
代码集成,logging
是一个不错的选择。 - 对于希望快速上手、简化配置,并享受现代化的日志功能的项目,
Loguru
是一个更合适的选择。 - 对于小型项目或个人项目,
Loguru
的简洁性可以大大提高开发效率。 - 对于大型项目,可以考虑使用
logging
,并结合配置文件进行管理。
第四部分:最佳实践
- 选择合适的日志级别: 根据信息的严重程度选择合适的日志级别。在生产环境中,通常建议将日志级别设置为
INFO
或WARNING
,以避免记录过多的调试信息。 - 使用有意义的日志消息: 编写清晰、简洁、有意义的日志消息,方便问题诊断。
- 包含上下文信息: 在日志消息中包含必要的上下文信息,例如请求 ID、用户 ID 等,方便追踪问题。
- 避免在循环中记录大量日志: 在循环中记录大量日志可能会影响程序的性能。可以考虑使用采样或计数器来控制日志记录的频率。
- 定期审查日志: 定期审查日志,发现潜在的问题和安全隐患。
- 保护敏感信息: 避免在日志中记录敏感信息,例如密码、信用卡号等。
第五部分:总结
本次讲座详细介绍了Python的两种主流日志系统:logging
和Loguru
。logging
的强大之处在于其高度的定制性,而Loguru
则胜在简洁易用和开箱即用的丰富功能。两者各有优劣,选择哪一个取决于具体项目的需求和开发者的偏好。希望通过这次讲座,大家能够更好地理解和使用这两种日志系统,提高开发效率和代码质量。