`Python`的`日志`系统:`Loguru`和`Logging`的`高级`配置与`实践`。

好的,我们开始今天的讲座:Python日志系统Loguru和Logging的高级配置与实践。

引言:日志的重要性及选择

日志是软件开发中不可或缺的一部分。它记录了程序的运行状态、错误信息、调试信息等,对于诊断问题、监控性能、审计安全至关重要。Python 提供了两种主要的日志解决方案:标准库 logging 和第三方库 Logurulogging 历史悠久,功能强大,但配置较为繁琐。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 模块定义了不同的日志级别,用于控制日志的详细程度。从低到高依次为:DEBUGINFOWARNINGERRORCRITICAL

日志级别 描述
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.stdoutsys.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.")

第三部分:loggingLoguru 的对比与选择

特性 logging Loguru
易用性 配置较为繁琐 简洁易用
功能 功能强大,但需要手动配置 功能丰富,开箱即用
扩展性 灵活,可以通过自定义 Handler、Formatter 扩展 灵活,可以通过添加 Handler、Formatter 扩展
性能 较好 较好,支持异步日志记录
社区支持 广泛 活跃

选择建议:

  • 对于需要高度定制化和精细控制的场景,或者需要与现有 logging 代码集成,logging 是一个不错的选择。
  • 对于希望快速上手、简化配置,并享受现代化的日志功能的项目,Loguru 是一个更合适的选择。
  • 对于小型项目或个人项目,Loguru 的简洁性可以大大提高开发效率。
  • 对于大型项目,可以考虑使用 logging,并结合配置文件进行管理。

第四部分:最佳实践

  • 选择合适的日志级别: 根据信息的严重程度选择合适的日志级别。在生产环境中,通常建议将日志级别设置为 INFOWARNING,以避免记录过多的调试信息。
  • 使用有意义的日志消息: 编写清晰、简洁、有意义的日志消息,方便问题诊断。
  • 包含上下文信息: 在日志消息中包含必要的上下文信息,例如请求 ID、用户 ID 等,方便追踪问题。
  • 避免在循环中记录大量日志: 在循环中记录大量日志可能会影响程序的性能。可以考虑使用采样或计数器来控制日志记录的频率。
  • 定期审查日志: 定期审查日志,发现潜在的问题和安全隐患。
  • 保护敏感信息: 避免在日志中记录敏感信息,例如密码、信用卡号等。

第五部分:总结

本次讲座详细介绍了Python的两种主流日志系统:loggingLogurulogging的强大之处在于其高度的定制性,而Loguru则胜在简洁易用和开箱即用的丰富功能。两者各有优劣,选择哪一个取决于具体项目的需求和开发者的偏好。希望通过这次讲座,大家能够更好地理解和使用这两种日志系统,提高开发效率和代码质量。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注