当前位置: 首页 > article >正文

Scrapy: log日志模块的设计详解下

1、概述

scrapy中的日志模块使用了python中logging库实现。是logging库的典型应用。我们可以分析其使用方法,借鉴设计方法,提取使用技巧,来优化我们自己开发项目的日志模块设计。
我把scrapy中的日志模块设计分为三篇文章介绍,此篇为下篇

Scrapy: log日志模块的设计详解上
这篇文章主要介绍了logging库本身的一些概念,由于篇幅有限,scrapy源码中的logging用法将在这篇文章中介绍
Scrapy: log日志模块的设计详解中
中篇详细介绍了scrapy中日志模块的核心配置,这篇文章可以算为中篇的续集,继续介绍一些中篇中没有介绍到的方法

2、代码解析

2.1、log_scrapy_info

我们用scrapy的时候都会发现,在启动程序的时候,会直接打印出来一些当前scrapy版本,还有一些关键依赖包的信息。这些信息就是通过这个log_scrapy_info实现的。
这个函数在scrapy/crawler.py中CrawlerProcess对象初始化的时候调用。

"""
在crawler中__init__的时候设置好configure_logging后,打印的一些scrapy的一些基础信息
这里面的logger就是本文件中的记录器,就是一次日志的正常打印
"""
def log_scrapy_info(settings: Settings) -> None:
    logger.info(
        "Scrapy %(version)s started (bot: %(bot)s)",
        {"version": scrapy.__version__, "bot": settings["BOT_NAME"]},
    )
    versions = [
        f"{name} {version}"
        for name, version in scrapy_components_versions()
        if name != "Scrapy"
    ]
    logger.info("Versions: %(versions)s", {"versions": ", ".join(versions)})

在这里插入图片描述

2.2、log_reactor_info

顾名思义,和log_scrapy_info干的事情差不多,这个方法是打印出twisted中的反应堆reactor的一些信息

"""
打印twisted的反应堆的基本信息
"""
def log_reactor_info() -> None:
    from twisted.internet import reactor

    logger.debug("Using reactor: %s.%s", reactor.__module__, reactor.__class__.__name__)
    from twisted.internet import asyncioreactor

    if isinstance(reactor, asyncioreactor.AsyncioSelectorReactor):
        logger.debug(
            "Using asyncio event loop: %s.%s",
            reactor._asyncioEventloop.__module__,
            reactor._asyncioEventloop.__class__.__name__,
        )

在这里插入图片描述

2.3、LogCounterHandler

自定义了一个记录器。继承自logging.Handler。主要是实现了抽象方法emit的逻辑。具体细节在介绍crawler对象的时候再详细阐述

"""
用来记录日志数量的处理器
需要了解下crawler.stats的结构,是如何存储这些计数数据的
"""
class LogCounterHandler(logging.Handler):
    """Record log levels count into a crawler stats"""

    def __init__(self, crawler: Crawler, *args: Any, **kwargs: Any):
        super().__init__(*args, **kwargs)
        self.crawler: Crawler = crawler
    
    """
    emit方法slogging.Handler中抽象方法,必须在其子类中实现,用于实现日志记录的逻辑。
    这里emit方法借助crawler的stats记录不同级别的日志数量。详情在介绍crawler的文章中介绍
    """
    def emit(self, record: logging.LogRecord) -> None:
        sname = f"log_count/{record.levelname}"
        assert self.crawler.stats
        self.crawler.stats.inc_value(sname)

2.4、logformatter_adapter

顾名思义,是个日子格式适配器,为了实现日志格式统一的工具。在讲解scraper对象的时候再做详细分析

"""
为了把其他格式的数据,转化为log的格式。所以是个适配器
在scraper中有调用,还传入一些遇到错误的时候的参数,研究到scraper的时候再仔细研究这个方法
另外关于log的各种参数的描述,用得到
https://docs.python.org/zh-cn/3.9/library/logging.html?highlight=logging%20log#logging.log
"""
def logformatter_adapter(
    logkws: LogFormatterResult,
) -> tuple[int, str, dict[str, Any] | tuple[Any, ...]]:
    """
    Helper that takes the dictionary output from the methods in LogFormatter
    and adapts it into a tuple of positional arguments for logger.log calls,
    handling backward compatibility as well.
    """

    level = logkws.get("level", logging.INFO)
    message = logkws.get("msg") or ""
    # NOTE: This also handles 'args' being an empty dict, that case doesn't
    # play well in logger.log calls
    args = cast(dict[str, Any], logkws) if not logkws.get("args") else logkws["args"]

    return (level, message, args)

2.5、SpiderLoggerAdapter

顾名思义,是spider对象的日志输出适配器。介绍spider对象的文章中再做详细介绍

"""
在scrapy/spiders/__init__.py中调用的。用于给spider生成一个记录器对象,具体到研究到spider的时候再仔细研究这个
LoggerAdapter的官方链接
https://docs.python.org/zh-cn/3.9/library/logging.html?highlight=loggeradapter#logging.LoggerAdapter
"""
class SpiderLoggerAdapter(logging.LoggerAdapter):
    def process(
        self, msg: str, kwargs: MutableMapping[str, Any]
    ) -> tuple[str, MutableMapping[str, Any]]:
        """Method that augments logging with additional 'extra' data"""
        if isinstance(kwargs.get("extra"), MutableMapping):
            kwargs["extra"].update(self.extra)
        else:
            kwargs["extra"] = self.extra

        return msg, kwargs

总结

这篇文件主要介绍几个比较独立的函数方法。包括打印scrapy基本信息的方法。打印twisted基本信息的方法。还有在scraper、spider对象中使用日志适配器。另外还有一个自己实现的日志数量记录器LogCounterHandler。当然,这些方法还要结合其他对象来使用。后面我会详细为大家讲解!


http://www.kler.cn/a/454835.html

相关文章:

  • Web漏洞之CSRF和SSRF
  • 支持向量机入门指南:从原理到实践
  • 前端图像处理(二)
  • docker 容器中没有ping命令和ifconfig命令和wget怎么办?
  • LeetCode-Z 字形变换(006)
  • layui动态拼接生成下拉框验证必填项失效问题
  • 曼哈顿图如何指定不同染色体不同的颜色
  • 【Linux命令】ps -a 和 ps -ef 的区别
  • MySQL 服务正在启动.MySQL 服务无法启动.服务没有报告任何错误。请键入 NET HELPMSG 3534 以获得更多的帮助。总结较全 (已解决)
  • 香港站群服务器如何排查 Linux 系统的内存泄漏问题
  • 远程作业专家指导调度系统
  • 中巨伟业推出高安全高性能32位智能卡内核可编程加密芯片SMEC88SP/ST
  • 通过百度api处理交通数据
  • Java中处理if-else的几种高级方法
  • 用Excel表格在线发布期末考试成绩单
  • USB免驱IC读写器QT小程序开发
  • 计算机网络 (9)数据链路层
  • 深度学习在图像识别中的最新进展与实践案例
  • 如何在 Vue 中处理 API 请求?
  • 第3章 并行循环调度的准则