loguru替代logging因其开箱即用、线程/进程安全、自动彩色输出、内置文件轮转与异常完整堆栈追踪,尤其适配爬虫等高并发易错场景。

为什么不用 logging 而选 Loguru?
Python 标准库 logging 配置繁琐,多进程/异步场景下容易丢日志、格式难统一;loguru 开箱即用,自动支持线程/进程上下文、彩色输出、JSON 日志、旋转文件,对爬虫这种高并发、易出错、需快速定位的场景更友好。
常见错误现象:logging.basicConfig() 在多线程中被多次调用导致重复日志;logger.exception() 没捕获到异步任务里的异常;日志时间没毫秒级精度,无法对齐请求耗时。
- Loguru 默认启用
backtrace=True, diagnose=True,未捕获异常会打印完整变量值(非仅 traceback) - 爬虫中推荐禁用默认控制台输出(
remove(0)),避免干扰print()调试或 stdout 重定向 - 注意:Loguru 的
add()是全局生效,不要在循环里反复调用,否则日志重复写入
如何为 Scrapy 或 requests 爬虫添加结构化日志?
核心是把请求 URL、状态码、耗时、响应大小等关键字段注入日志 record,而非拼接字符串。Loguru 支持通过 patch() 动态注入字段,比 extra 参数更灵活。
使用场景:调试反爬触发点(比如某次 412 响应前的日志是否漏了 headers)、分析慢请求(耗时 >3s 的请求自动标为 warning)。
- 给每个请求加唯一 ID:
logger.patch(lambda r: r.update(request_id=str(uuid4())[:8])) - 记录 requests 耗时:
response = session.get(url, timeout=10); logger.info("GET {url} → {status} ({elapsed:.2f}s)", url=url, status=response.status_code, elapsed=response.elapsed.total_seconds()) - Scrapy 中在
parse()方法开头加:logger.bind(spider_name=self.name, item_count=len(response.css('a::attr(href)'))).debug("Parsed {url}", url=response.url)
如何避免日志文件爆炸并保留关键信息?
爬虫跑一晚上可能产生 GB 级日志,但真正需要查的只是 ERROR 和部分 WARNING。Loguru 的 rotation 和 retention 参数必须配合 filter 使用,否则 debug 日志照样塞满磁盘。
性能影响:频繁写磁盘会拖慢爬取速度,尤其在 SSD 寿命敏感的部署环境(如树莓派、云函数)。
- 按大小轮转:
add("logs/crawl_{time}.log", rotation="100 MB", retention="7 days") - ERROR 单独存档:
add("logs/error.log", level="ERROR", backtrace=True, diagnose=True) - 过滤掉 requests 冗余日志:
add(sys.stderr, filter=lambda r: "requests.packages.urllib3" not in r["name"]) - 生产环境禁用
diagnose=True(它会序列化局部变量,有安全和性能风险)
异步爬虫(aiohttp / httpx)中怎么捕获 task 异常?
asyncio 里未被 await 的 task 抛出的异常会被静默吞掉,logger.exception() 也捕不到——必须用 Loguru 的 enable() + 自定义 exception hook。
容易踩的坑:async with aiohttp.ClientSession() as session: 里抛异常,但 session 关闭后 logger 已被销毁;或 asyncio.create_task() 启动的任务没加 try/except。
- 全局捕获未处理异常:
import asyncio; asyncio.get_event_loop().set_exception_handler(lambda loop, ctx: logger.opt(exception=ctx.get('exception')).error("Uncaught exception", **ctx)) - task 内部主动记录:
try: await fetch(url) except Exception: logger.exception("Fetch failed for {url}", url=url) - httpx 的 timeout 异常类型是
httpx.TimeoutException,不是TimeoutError,过滤日志时别写错
bind() 和 patch() 很好用,但 bind 的字段不会自动继承到子 logger,跨模块传递上下文得靠 contextvars 配合 patch;另外,Docker 容器里 stdout 重定向时,add(sys.stdout) 可能导致日志截断,优先用文件输出。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











