惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
罗磊的独立博客
博客园_首页
T
The Exploit Database - CXSecurity.com
博客园 - 聂微东
雷峰网
雷峰网
V
V2EX
博客园 - 司徒正美
J
Java Code Geeks
Hugging Face - Blog
Hugging Face - Blog
H
Heimdal Security Blog
Apple Machine Learning Research
Apple Machine Learning Research
Last Week in AI
Last Week in AI
K
Kaspersky official blog
爱范儿
爱范儿
T
Threatpost
博客园 - Franky
T
Tailwind CSS Blog
N
News and Events Feed by Topic
P
Privacy & Cybersecurity Law Blog
W
WeLiveSecurity
Google Online Security Blog
Google Online Security Blog
Know Your Adversary
Know Your Adversary
小众软件
小众软件
NISL@THU
NISL@THU
博客园 - 【当耐特】
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
IT之家
IT之家
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Threat Research - Cisco Blogs
Security Latest
Security Latest
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
D
Darknet – Hacking Tools, Hacker News & Cyber Security
P
Palo Alto Networks Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
V
Vulnerabilities – Threatpost
N
News | PayPal Newsroom
C
Cyber Attacks, Cyber Crime and Cyber Security
AI
AI
P
Privacy International News Feed
V
Visual Studio Blog
S
Secure Thoughts
Schneier on Security
Schneier on Security
阮一峰的网络日志
阮一峰的网络日志
S
SegmentFault 最新的问题
人人都是产品经理
人人都是产品经理
博客园 - 叶小钗
C
Cybersecurity and Infrastructure Security Agency CISA
The Cloudflare Blog
Spread Privacy
Spread Privacy

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解 【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响 Hermes Agent 一周暴涨五万 Star,但我劝你别急着追 明明连接的是Redis的DB0,为什么能查到DB3的数据? 【从0到1构建一个ClaudeAgent】协作-Agent团队 熟悉电子元器件之后,电子小白下一步该怎么走? MAF快速入门(23)通过C#类定义Skills .NET 高级开发 | 手写一个对象映射框架 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 mysqldump 参数拾遗:在遗忘与铭记之间 C# .NET 周刊|2026年3月5期 Claude code入门 - 陈彦斌 一文学习入门 ThingsBoard 开源物联网平台 GitHub 热门项目 | 2026年04月16日 如何为GIT设置全局勾子,为每次提交追加信息 Number.isFinite和isFinite与isNaN()和Number.isNaN的区别 PortSwigger SQL注入LAB2 推荐一个测试人必备的Skills,从功能到性能全搞定(附详细实操和安装下载方式) 筑基期:掌握Odoo基础核心知识点02(Odoo XML 开发方式详解) GLM模型这么火,咱们用vllm也咧一个呗! 深入理解 AbortController:从底层原理到跨语言设计哲学 字符串学习笔记 多租户系统框架的基础模块设计和分析设计 Apache SeaTunnel Zeta 为什么能做到“又快又稳”? AI开发-python-LangGraph框架(3-26-LangGraph基本概念及第一个简单样例) Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 ElasticSearch7.X版本配置密码 用Manim实现动态交点计算--从一个动点问题说起 团结引擎+Addressable+Instant Game打包抖音小游戏 function call 实战:让 LLM 自动判断 pod 异常、调用日志工具并完成故障分析 bubseek —— 让 Agent 的足迹,变成团队的洞察 通过 C# 读取并导出 PDF 书签 如何用 GitHub Actions 实现 Steam 自动化发布 【从0到1构建一个ClaudeAgent】并发-后台任务 .NET 高级开发 | 定制 ASP.NET Core 框架 电子小白:什么是运算放大器(运放) zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线 堆上的ORW HC32F460 USB CDC通信异常:非对齐访问异常排查 20260413-Hyperbridge 攻击事件:发生在默克尔山上的验证绕过 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 深度学习进阶(八)Swin Transformer 最小二乘问题详解19:带先验约束的增量式SFM优化与实现 SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习 工作的意义、工作的困难认知再思考 .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 开了 TUN 模式还是直连?90% 的人都踩过这个坑 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术
用了loguru我才明白,Python日志还能这么写
一名程序媛呀 · 2026-04-30 · via 博客园_首页

不知道对面的朋友是不是也经历过这种绝望:FastAPI 服务跑得好好的,一查日志文件却是空的,或者更惨——日志全串成一锅粥,根本看不出哪个请求在哪儿挂了?

之前用 Python 自带的 logging 模块写了一大坨配置,又是 handlers 又是 formatters,结果放进异步协程里直接“精神分裂”。
直到看见那句“Loguru is a library which aims to bring enjoyable logging in Python.”心想:“还能这么玩?赶紧试试...”
好家伙,那种感觉就像手动档开惯了,突然换了一辆自动档性能车——又快又丝滑。

今天咱就来聊聊,在 FastAPI 里怎么把 loguru 用得明明白白,不只要“能跑”,还得“在生产环境稳如泰山”。我会把从踩坑到解脱的全过程讲给你听。🎯

⚡️ 为什么我暂时抛弃了 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的原因,享受它带来的便利,避开它会引起的坑!
日志是项目上线后你的“眼睛”,花半小时好好配一下,未来无数个深夜调试都会感谢现在的自己。


好啦,今天掏心窝子的分享就到这儿。如果你也被日志折腾过、踩过同样的坑,点个「赞」加个「关注」吧,别让这篇溜走,说不定下次就能帮你省掉一大把掉头发的时间。收藏起来,需要的时候心里不慌。我们下个坑里见~ 🛠️