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

推荐订阅源

cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
GbyAI
GbyAI
F
Fortinet All Blogs
Y
Y Combinator Blog
I
InfoQ
Microsoft Azure Blog
Microsoft Azure Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Vercel News
Vercel News
T
The Blog of Author Tim Ferriss
P
Proofpoint News Feed
S
Schneier on Security
NISL@THU
NISL@THU
The Hacker News
The Hacker News
Simon Willison's Weblog
Simon Willison's Weblog
The GitHub Blog
The GitHub Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
F
Full Disclosure
P
Palo Alto Networks Blog
T
The Exploit Database - CXSecurity.com
Know Your Adversary
Know Your Adversary
A
About on SuperTechFans
The Cloudflare Blog
T
Threat Research - Cisco Blogs
M
MIT News - Artificial intelligence
宝玉的分享
宝玉的分享
雷峰网
雷峰网
Hugging Face - Blog
Hugging Face - Blog
博客园 - 三生石上(FineUI控件)
D
DataBreaches.Net
Martin Fowler
Martin Fowler
H
Help Net Security
B
Blog RSS Feed
B
Blog
爱范儿
爱范儿
V
V2EX
I
Intezer
L
LangChain Blog
WordPress大学
WordPress大学
小众软件
小众软件
美团技术团队
Latest news
Latest news
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
Tor Project blog
L
Lohrmann on Cybersecurity
Cyberwarzone
Cyberwarzone
Last Week in AI
Last Week in AI
Project Zero
Project Zero
C
Cybersecurity and Infrastructure Security Agency CISA
V
Visual Studio Blog

博客园 - 一名程序媛呀

Anki插件开发必知必会:钩子函数与右键菜单定制 httpx 传参总报错?这次把 GET、POST、文件上传到响应处理的坑给你一次填平 从卡顿到丝滑:FastAPI 调用外部 API 的正确姿势(httpx 实战) 还在 XHR、Fetch 和 Axios 之间纠结?我踩过的坑,希望你一个都不用碰到 你的REST接口还在“过度投喂”数据吗?——FastAPI + GraphQL实战避坑指南 Uvicorn、Gunicorn 傻傻分不清?FastAPI 生产部署避坑指南 Termux里的二进制和脚本,到底怎么运行才不踩坑?Termux-service 保活妙招! 刚部署的 LibreTranslate 频频翻车?我掏出了 20 年前的 StarDict 词典,用 FastAPI 搭了个本地词典翻译 API 别再用网页翻译看源码了!你的私人翻译神器LibreTranslate,部署避坑指南来了 掏出手机就能搭个 WebDAV 同步服务器?这操作有点香 别只盯着GitBook了!这个文档神器让你的笔记秒变网站 写爬虫时用了代理还被封?Python 代理的那些隐藏坑,我替你踩明白了 FastAPI 身份验证总踩坑?这份 FastAPI Users “避坑指南”请收好 旧手机别扔!用 Termux 搭个私人云盘,比网盘香多了 你的FastAPI又在服务器上“跑不起来”了?来,今天咱把打包这件事彻底聊透 写页面时别再把 Element Plus 整个搬进来啦!Vue3按需加载的坑我帮你踩平了 前端包管理咋选?我从npm叛逃到pnpm的血泪史(附避坑指南) 聊聊 fetch 使用中我踩过的那些坑和正确打开方式 FastApiAdmin 后端接口开发好了,前端管理界面怎么调用与显示? 给 FastApiAdmin 加个“会议纪要”模块,我把后端二次开发的坑踩了个遍 我用了FastApiAdmin后,连夜把踩过的坑都整理出来了 告别 Typora 后的新欢:我把所有笔记迁移到了 Obsidian 这个“第二大脑” 你的Agent API还在裸奔?从认证到沙箱,我用FastAPI搭了几道防线 让 FastAPI Agent 思考不阻塞:手把手教你实现异步任务与后台处理方案 让FastAPI Agent真正记住你:聊聊会话记忆与持久化存储的落地实践 FastAPI Agent 函数调用实战:我让 AI 学会了“自己动手查天气“ 初探:用 FastAPI 搭建你的第一个 AI Agent 接口 FastAPI 少有人提的实用技巧:把 Depends 依赖提到路由层,代码少写60% FastAPI 生产环境静态文件完全指南:从 /favicon.ico 404 到 HSTS 混合内容,一次全根治 FastAPI 后台任务:BackgroundTasks 的使用场景与注意事项 FastAPI配置管理避坑指南:从硬编码到 .env 与 pydantic_settings 类,连路由用法都给你捋清楚 FastAPI 文件上传避坑全指南:分块存盘、类型校验与安全兜底 FastAPI + Pydantic 模型终极实战手册:从能跑就行到固若金汤,这些技巧你一定用得上 FastAPI + SQLAlchemy 2.0 通用CRUD操作手册 —— 从同步到异步,一次讲透 FastAPI订单防超卖实战:从数据库锁到Saga分布式事务,这一篇给你理清了 FastAPI 生产环境避坑指南:用 Alembic 管理数据库迁移,别再手动改表结构了! FastAPI服务半夜又挂了?先别急着重启,查查你的数据库连接池“池子”是不是漏了 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 Vue 3 组合式 API 香是香,但从Vue2迁移时你可别像我当初一样踩进这 3 个深坑里 我用fastapi-scaff搭了个项目,两天工期缩到两小时,老板以为我开挂了 FastAPI+Vue:文件分片上传+秒传+断点续传,这坑我帮你踩平了! FastAPI自动生成的API文档太丑?我花了一晚上把它改成了客户愿意付费的样子 告别手写 API 胶水代码:FastAPI 与 Vue 的“契约自动机” OpenAPI 实战 FastAPI + Vue 前后端分离实战:我的项目结构“避坑指南” FastAPI + Celery 实战:异步任务里调用 Redis 和数据库的全解析,及生产级组织方案 FastAPI里玩转Redis和数据库的正确姿势,别让异步任务把你坑哭了! FastAPI + Celery 实战:异步任务的坑与解法,我帮你踩了一遍 FastAPI子应用挂载:别再让root_path坑你一夜 FastAPI项目半夜报警吵醒你?聊聊告警这事儿怎么搞! 别再数据线了!用FastAPI 5分钟搭个局域网文件+剪贴板神器 FastAPI单元测试实战:别等上线被喷才后悔,TestClient用对了真香! FastAPI状态共享秘籍:别再让中间件、依赖和路由“各自为政”了! FastAPI实战:WebSocket vs Socket.IO,这回真给我整明白了! 从0到1,FastAPI + PostgreSQL + Tortoise ORM 实战避坑指南 FastAPI + PostgreSQL 实战:给应用装上“缓存”和“日志”翅膀 FastAPI + PostgreSQL 实战:从入门到不踩坑,一次讲透
用了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的原因,享受它带来的便利,避开它会引起的坑!
日志是项目上线后你的“眼睛”,花半小时好好配一下,未来无数个深夜调试都会感谢现在的自己。


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