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

为什么我暂时抛弃了 logging

先说我踩过最大的坑:logging 的默认输出是同步阻塞的,FastAPI 的异步特性一来,日志不但会打乱顺序,还可能悄无声息地丢失。
而且你要拿到一个像样的日志,得先写几十行配置,每次开新项目都要先把以前的代码复制过来,烦得要死。

loguru一上来就告诉我:别折腾了,直接打就行。
它只有一个全局的logger对象,装上就能用,彩色控制台输出,自带日期、级别和超好看的格式。一句话:清爽,真的清爽。

这里为什么我说是“暂时”呢?是因为Loguru的魅力在于它直击痛点的简洁。用 logger.add() 一行代码就能搞定输出目标、格式、轮转策略等所有事情。
不过它也存在短板。如果直接代替标准库,可能出现业务代码用Loguru日志,uvicorn服务器依然打官方日志的混乱局面,或是要对标准库日志做好“拦截”,避免重复刷屏。
能用和用好,是完全不同的两个维度。在大型项目里,选择标准logging,本质上不是因为它更好用,而是因为它能让整个复杂的系统更好地在一起工作,更具确定性可控性

所以,对于新项目,建议从 Loguru 起步,享受它的便利。当项目成长为大型/微服务架构时,再将日志系统核心回迁到标准库logging + dictConfig上。
组合使用,各司其职:Loguru 作用于你的业务代码 + 标准库logging作为“基础设施”,兼顾开发体验和生产系统的兼容性。

📦 安装与第一个日志

好,咱们先来安装。就一行:

uv add loguru

然后在 FastAPI 里直接开怼:

from loguru import logger from fastapi import FastAPI app = FastAPI() @app.get("/") async def hello(): logger.info("有人访问了首页,美滋滋") return {"msg": "Hello"}

跑起来后,你的终端会立马出现一条带有时间戳、级别和高亮颜色的日志,再也不用对着黑白海量的输出发呆了。

这里有个问题,在正式环境一定不要只往控制台打印,接下来重点来了:怎么把日志稳稳地写到文件里。

🔧 常用配置,一次搞懂

你可能会问:“loguru 写文件是不是又要配一堆东西?”完全不用,一行搞定:

logger.add("app.log", rotation="10 MB", retention="7 days", level="INFO")

你看,rotation 按文件大小自动轮转,retention 自动清理老日志,而且压缩、自定义格式都能在同一个方法里搞定。
这比 logging 的RotatingFileHandler + TimedRotatingFileHandler那种套娃组合直观太多了。

但说个容易翻车的点:在 FastAPI 这种异步框架里,千万别漏了 enqueue=True 参数。
官方示例可能没写,但根据我线上血的教训,不加这个参数,多并发下日志写入会阻塞事件循环,轻则响应变慢,重则日志串行甚至丢数据。正确姿势:

logger.add("app.log", rotation="10 MB", retention="7 days", level="INFO", enqueue=True)

loguru 会把日志消息扔进一个线程安全的队列,专门有后台线程负责写入,你的主流程该干嘛干嘛,完全不用分心。
这就像点一杯奶茶,小程序下单后不用在店里干等,做好了自然叫你。😊

🧩 和 Uvicorn 的日志整合

是不是以为这样就完了?还有个折磨过我的地方:FastAPI 底层用的 Uvicorn 自己也哗哗地打印日志,两边各玩各的,管理起来特难受。

我的做法是,在应用启动的地方,把 Uvicorn 的日志也“绑架”到 loguru 里来:

import logging from loguru import logger class InterceptHandler(logging.Handler): def emit(self, record): logger_opt = logger.opt(depth=6, exception=record.exc_info) logger_opt.log(record.levelname, record.getMessage()) # 在 FastAPI 的 lifespan 或 startup 事件里执行 logging.basicConfig(handlers=[InterceptHandler()], level=0)

这样,所有日志都汇聚到一个口子输出,规则统一,查找问题就像过红绿灯,一次看清。🚦

但在实际开发时,还有个问题就是如果启用了--reload模式,或者使用命令fastapi dev main.py启动的项目,那有些日志拦截还是会漏掉,
想办法把自定义的InterceptHandler拦截的更彻底些:

import logging from loguru import logger class InterceptHandler(logging.Handler): def emit(self, record): # 拿到对应的 loguru 级别 try: level = logger.level(record.levelname).name except ValueError: level = record.levelno # 找到调用栈里真正发出日志的地方 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()) # 这里是关键:启动的时候,把 root logger 的所有旧 handler 都干掉, # 只留我们自己的 InterceptHandler def setup_logging(): logging.root.handlers = [InterceptHandler()] logging.root.setLevel(logging.INFO) # 把 uvicorn 那几个 logger 也顺手接管 for name in ("uvicorn", "uvicorn.access", "uvicorn.error"): logging.getLogger(name).handlers = [] logging.getLogger(name).propagate = True # 在 FastAPI 应用实例化之前就调用 setup_logging()

⚠️ 这些不足和注意点,请刻在脑子里

loguru 虽香,但也别盲目吹。我总结了几个生产环境必须注意的:

🔸 全局只有一个 logger,多进程部署(如Gunicorn多个worker)时务必注意隔离,避免写入冲突,推荐每个进程单独 add 文件,文件名可以用 PID 区分。

🔸 异常回溯虽然默认就漂亮,但要捕获完整 traceback,记得用logger.exception()或者在 add 时加上backtrace=True

🔸 敏感信息(密码、token)一定要在日志里脱敏,loguru 支持 filter 功能,可以优雅地过滤字段。

🔸 日志文件路径别写成相对路径,否则在守护进程启动时可能写到莫名其妙的地方,建议使用绝对路径或基于项目根目录拼接

最后啰嗦一句:
有优点,也有不足,这也是为什么前面我说“暂时”替代标准库logging的原因,享受它带来的便利,避开它会引起的坑!
日志是项目上线后你的“眼睛”,花半小时好好配一下,未来无数个深夜调试都会感谢现在的自己。

http://www.jsqmd.com/news/1075133/

相关文章:

  • 让 AI 越写越像你:用 Hook 自动积累编码规范的实践
  • 跨平台资源下载神器:5分钟掌握res-downloader完整使用指南
  • 计算机小程序毕设实战-基于 SpringBoot+UniApp 的区域文旅(冀鲁豫)旅行推荐系统设计与实现 基于 SpringBoot+UniA【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 如何用好accio work 做好客户开发,背调
  • 智人曾经这样灭绝猛犸象:AI入侵与行业灭绝
  • 如何免费实现高效语音转字幕:STS-Bcut完整使用指南
  • 临床AI代理为何跳过药物相互作用检查?工具调用失效的根因与驯服方案
  • 东莞翻译中心 意大利语法律翻译术语
  • 有孵化器的亚洲EMBA实测测评与理性选型指南
  • 生成式AI落地实战:从流程锚定到组织级AI能力建设
  • 大湾区高含金量EMBA客观测评与理性选型指南
  • 《龙虾软件一线深度落地的体系拆解》
  • 3分钟永久解锁Microsoft 365:零风险Office激活终极指南
  • Gemma4 E4B本地部署实操指南:旧设备跑通轻量大模型
  • Windows内存优化神器:Mem Reduct让你的电脑性能飙升50%以上
  • 终极免费解锁:如何用Ohook完整激活Microsoft 365所有功能
  • Loop Engineering :从提示词工程到循环工程,AI 编程的范式革命
  • 别再分不清JBOD/RAID0/1/5!Win2016软RAID图文实操全记录
  • 遗传算法工程落地:自适应机制与种群多样性控制实战
  • 深度剖析SQL注入攻防:从MySQL语法特性到多层防护体系
  • 终极SPT-AKI存档编辑器:免费开源的游戏进度管理神器
  • 电梯里同事问我:“你觉得RAG落地最难的地方在哪?”,我愣了,保安转头:“我以前干过,主要就文档预处理、召回质量、生成忠诚度”
  • Seraphine:英雄联盟智能辅助工具,你的排位赛制胜法宝
  • 登报遗失声明收费标准是什么?登报遗失声明去哪办?流程+费用保姆级指南
  • 淘宝闪购 AI 应用研发二面,我笑了!!!
  • Adobe-GenP 3.0:三步解锁Adobe全家桶完整功能的终极指南
  • 如果你懂医者不自医,你就应该知道译者无法自我校对自己的译文……
  • 5分钟掌握Windows右键菜单终极定制:ContextMenuManager完整使用指南
  • 大模型AI智能客服系 AI智能客服系统 - 全功能详细介绍
  • VoiceFixer终极指南:10分钟掌握AI语音修复与噪音消除技术