Scrapy 日志初始化分析,实现自定义日志handler和日志着色

文章讲述了Scrapy框架的日志配置,重点介绍了`install_root_handler`的作用,以及如何在PyCharm中管理和调整Scrapy的日志输出,包括修改PyCharm配置、自定义handler和控制`LOG_STDOUT`的使用。
摘要由CSDN通过智能技术生成
sys.stdout = StreamLogger(logging.getLogger("stdout"))  # type: ignore[assignment]
# 将标准输出改为 logger


该函数在 `install_root_handler` 为 True (默认)的情况下会调用 `install_scrapy_root_handler`




---


### install\_scrapy\_root\_handler


`install_scrapy_root_handler` 该函数会给 `logging.root` 添加一个 handler(由 `_get_handler` 从 settings 中读取配置)



scrapy/utils/log.py#143

settings 为从 settings.py 读取的配置信息

def _get_handler(settings: Settings) -> logging.Handler:
“”“Return a log handler object according to settings”“”
filename = settings.get(“LOG_FILE”) # 输出的日志文件名称
handler: logging.Handler
if filename:
mode = “a” if settings.getbool(“LOG_FILE_APPEND”) else “w” # 日志文件模式
encoding = settings.get(“LOG_ENCODING”) # 日志文件编码
handler = logging.FileHandler(filename, mode=mode, encoding=encoding)
elif settings.getbool(“LOG_ENABLED”): # 是否启用日志
handler = logging.StreamHandler() # 标记1
else:
handler = logging.NullHandler()

formatter = logging.Formatter(  # 设置日志格式
    fmt=settings.get("LOG\_FORMAT"), datefmt=settings.get("LOG\_DATEFORMAT")
)
handler.setFormatter(formatter)
handler.setLevel(settings.get("LOG\_LEVEL"))  # 设置日志最低级别
if settings.getbool("LOG\_SHORT\_NAMES"):  # 是否缩写
    handler.addFilter(TopLevelFormatter(["scrapy"]))
return handler

scrapy/utils/log.py#125

设置 scrapy 的 handler

def install_scrapy_root_handler(settings: Settings) -> None:
global _scrapy_root_handler

if (
    _scrapy_root_handler is not None
    and _scrapy_root_handler in logging.root.handlers
):
    logging.root.removeHandler(_scrapy_root_handler)
logging.root.setLevel(logging.NOTSET)
_scrapy_root_handler = _get_handler(settings)
logging.root.addHandler(_scrapy_root_handler)

* 标记1:scrapy 使用的是 `logging.StreamHandler`,该 `StreamHandler` 默认使用的就是 `sys.stderr`




---


### 分析


`configure_logging` 会在 `scrapy.CrawlerProcess` 中调用:



scrapy/crawler.py 328

class CrawlerProcess(CrawlerRunner):

def \_\_init\_\_(
    self,
    settings: Union[Dict[str, Any], Settings, None] = None,
    install_root_handler: bool = True,
):
    ...
    configure_logging(self.settings, install_root_handler)
    ...

其中: `install_root_handler` 默认值为 True


而 `CrawlerProcess` 只有在 `cmdline` 中被调用:



scrapy/cmdline.py #159

def execute(argv=None, settings=None):

cmd.crawler_process = CrawlerProcess(settings)



> 
> 这部分在之前的命令分析文章中提到,是命令执行的关键函数,框架的启动必定会调用该函数;
> 
> 
> 


那么 `scrapy_root_handler` 必定会被添加到 `logging.root.handlers` 中,所有日志都会经过该 handler 进行输出




---


## 实现


既然已经知道 scrapy 的日志输出是通过将 `_scrapy_root_handler` 添加到 `logging.root.handlers` 中实现的,那可以考虑如何解决在 pycharm 中显示为 红色的问题了;


有3种思路:


1. pycharm 中其实是可以设置 `stderr` 输出样式的(默认为红色)(弊端为 `sys.stderr` 中非日志部分的样式不是红色,分辨不出来)
2. 修改 handlers 的内容,将 stream 设置为 `sys.stdout`(弊端为当 `LOG_STDOUT` 为 True 时会产生递归异常)
3. 将 `install_root_handler` 设置为 `False`(这样就会导致 settings.py 的配置信息无效,需要自己读取设置才行)


### 第1种:修改Pycharm配置


Pycharm中依次点击:File -> Settings -> Editor -> Color Schema -> Console Colors -> Console -> Error outpt


然后就能设置对应的颜色了




---


### 第2种:返回自定义handler


可以通过hack的方式修改:


在 `settings.py` 加入:



import scrapy.utils.log as log

拿到原函数

_get_handler = copy.copy(log._get_handler)

自己修改过后的

def get_handler_custom(settings: Settings):
handler = _get_handler(settings)
# 如果是 StreamHandler,就修改为 sys.stdout
if isinstance(handler, logging.StreamHandler):
handler.setStream(sys.stdout)
return handler

覆盖原来的函数

log._get_handler = get_handler_custom


注意不能设置 `LOG_STDOUT` 为 True,会递归爆栈的


分析:


* 在 `configure_logging` 中有这么一行代码

 

if settings.getbool(“LOG_STDOUT”):
sys.stdout = StreamLogger(logging.getLogger(“stdout”)) # type: ignore[assignment]

 是直接将标准输出定为一个名称为 stdout 的 StreamLogger 了

 StreamLogger 有这样一个方法,将 buf 用 日志输出了

 

def write(self, buf: str) -> None:
for line in buf.rstrip().splitlines():
self.logger.log(self.log_level, line.rstrip())

* 看 `logging.StreamHandler`的代码:

 此时先将 `sys.stdout` 设置为 `StreamLogger` 了

 然后我们上面的代码将 `stream` 设置为 `sys.stdout`

 

def flush(self):
self.acquire()
try:
if self.stream and hasattr(self.stream, “flush”):
self.stream.flush()
finally:
self.release()

 这样产生递归了,没有出口,就爆栈了



> 
> 可以自己返回自定义的 handler,但是日志配置信息还是得自己读取,settings参数已经有了,直接模拟源码读取即可
> 
> 
> 




---


### 第3种:日志着色


我当前写的项目中,是项目外部启动(不使用 Scrapy 命令,而是 CrawlerProcess 启动)


那么我直接传入 `install_root_handler` 为 False 即可



def run_spider(spider_name: str, _settings):
crawler_process = CrawlerProcess(settings, install_root_handler=False)
crawl_defer = crawler_process.crawl(spider_name)
if getattr(crawl_defer, “result”, None) is not None and issubclass(
crawl_defer.result.type, Exception
):
exitcode = 1
else:
crawler_process.start()

    if (
            crawler_process.bootstrap_failed

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注Python)

png)

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注Python)

img
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值