scrapy 中使用 loguru 管理日志

275 阅读2分钟

代码

class InterceptHandler(logging.Handler):

    def emit(self, record):
        # ✓ corresponding Loguru level if it exists
        try:
            level = logger.level(record.levelname).name
        except ValueError:
            level = record.levelno

        # Find caller from where originated the logged message
        frame, depth = logging.currentframe(), 2
        while frame.f_code.co_filename == logging.__file__:
            frame = frame.f_back
            depth += 1

        logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())

logging.basicConfig(handlers=[InterceptHandler()], level=0)

效果

scrapy日志如何与其他模块日志输出到一起; log是loguru的彩色风格


2023-04-07 16:18:51.905 | DEBUG    | rediscluster.connection:reset:187 - Resetting ConnectionPool
2023-04-07 16:18:51.905 | INFO     | bf_sp_scrapy.utils.redis_db:<module>:12 - cluster_client: RedisCluster<10.0.21.190:7000>
2023-04-07 16:18:52.464 | INFO     | scrapy.utils.log:log_scrapy_info:147 - Scrapy 2.6.2 started (bot: bf_sp_scrapy)
2023-04-07 16:18:52.477 | INFO     | scrapy.utils.log:log_scrapy_info:154 - Versions: lxml 4.9.2.0, l

同时,我认为可以回答这个问题:coding.imooc.com/learn/quest…

代码解析from gpt4:

这段代码定义了一个名为InterceptHandler的类,这个类是Python标准库logging模块中的Handler类的子类。它的目的是拦截由Scrapy产生的标准日志,并将它们转发到Loguru日志库进行处理。

我会一步步地解释这段代码:

  1. 类定义

    class InterceptHandler(logging.Handler):
    

    这里定义了一个新的日志处理器类,继承自logging.Handler

  2. emit方法: 这是Handler子类必须实现的一个方法,它描述了当一个日志消息被处理时应该执行的动作。

    • 尝试获取Loguru的日志级别:

      try:
          level = logger.level(record.levelname).name
      except ValueError:
          level = record.levelno
      

      以上代码尝试根据传入的日志记录的级别名(如'INFO', 'WARNING')获取对应的Loguru日志级别。如果该级别在Loguru中不存在,它会回退到使用数字形式的日志级别。

    • 找到产生日志消息的调用者:

      frame, depth = logging.currentframe(), 2
      while frame.f_code.co_filename == logging.__file__:
          frame = frame.f_back
          depth += 1
      

      这里的目标是确定产生日志消息的原始位置(文件名和行号)。logging.currentframe()提供了当前调用栈的帧。然后,代码遍历调用栈,直到找到不是logging模块本身的帧,从而找到原始的调用者位置。

    • 使用Loguru记录日志:

      logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())
      

      这里使用Loguru的logger实例来记录日志。logger.opt()允许为这个特定的日志调用设置一些选项。设置的depth确保日志输出显示的源代码位置是原始的调用者,而不是InterceptHandler类。同时,如果日志记录包含异常信息,它也会被包含进去。

总之,这个InterceptHandler类的目的是为了在Scrapy中使用Loguru作为日志系统,并确保Scrapy产生的所有日志都被正确地转发到Loguru进行处理和输出。