使用Trapfall收集错误信息

最近闲暇时间都在琢磨我的个人微信小程序:

minilove

昨天在看项目访问日志的时候,发现了个异常。

一个新疆·喀什用户 POST 请求 422, 哪个接口大部分时候应该是触发 400, FastAPI 的 422 是在路由函数执行之前,由内置异常处理器抛出的,路由内部代码还没跑,普通日志拿不到原始请求体。

想要专门捕获 Pydantic 校验失败(422),拿到完整报错 + 原始请求 body,或者 5xx 等错误,定位代码块,拿到请求 body 什么的,通常需要 Sentry 等专业方案。

自己部署过 Sentry 的都知道,它不是一个简单单体程序,整套包含非常多组件:PostgreSQL、Redis、Kafka、ZooKeeper、ClickHouse、Symbolicator 等一堆微服务容器,最低推荐 8G 内存、4 核 CPU,服务器配置要求高,后续还要持续升级、备份、排障,运维成本很高。

个人项目的丐版主机明显资源不够用啊,告诉 AI 我的窘迫状况,让它帮忙推荐类似轻量方案,且要开放源码的。

推荐了几个,只有下面 2 个入眼:

  • PocketSentry:MIT Golang 单二进制 + SQLite <20MB ✅ ⭐⭐⭐⭐⭐
  • TrapFall:Apache2.0/MIT Rust 单二进制 + SQLite 占用极低 ✅ ⭐⭐⭐⭐

个人偏好 Rust 版的 TrapFall,带前端面板:SvelteKit 5,静态嵌入到二进制包里面,不需要单独启动前端服务。其实看了下 PocketSentry 也不错。留着以后再试用吧!

1. FastAPI 项目接入 TrapFall

sentry-sdk 直接复用,方便啊

1uv add sentry-sdk

伪代码:

 1import sentry_sdk
 2from sentry_sdk.integrations.starlette import StarletteIntegration
 3from sentry_sdk.integrations.fastapi import FastApiIntegration
 4from sentry_sdk import capture_exception
 5
 6# ===================== SENTRY 初始化【放在最前面!】=====================
 7SENTRY_DSN = "替换成你的DSN"  # TrapFall
 8if SENTRY_DSN:
 9    sentry_sdk.init(
10        dsn=SENTRY_DSN,
11        send_default_pii=True,  # 采集IP、headers,方便定位那个新疆用户
12        traces_sample_rate=0.1, # 生产调低,避免流量爆炸,0.1=10%采样
13        environment="production",
14        integrations=[
15            StarletteIntegration(
16                transaction_style="endpoint",
17                # 重点:把422加入上报
18                failed_request_status_codes={422, *range(500, 599)},
19            ),
20            FastApiIntegration(
21                transaction_style="endpoint",
22                failed_request_status_codes={422, *range(500, 599)},
23            ),
24        ],
25    )
26
27#略去其它业务代码#
28
29# 测试接口,验证sentry连通性,上线前删掉
30@app.get("/sentry-debug")
31async def sentry_debug():
32    1/0

2. Trapfall 部署

trapfall 现在才 8 个标星,我点了个变成 9 个,我也是服了 AI 了,这都能找到。

直接采用源码编译的方式部署:

1# Build frontend
2cd web && npm ci && npm run build && cd ..
3
4# Build + run
5cargo run --release -p trapfalld -- --db trapfall.db serve --listen 0.0.0.0:9090

注意:我是在我个人 Arch Linux 电脑上部署好后,放到服务器上的,都是 x86 架构,不在服务器上部署污染环境!不让部署的时候要装个 npm 就要安装一大堆包,对待丐版主机要温柔。

编译出 trapfall 二进制文件后,传到服务器上,Rust 程序很爽的地方就是直接用 Systemd 管理就好了:

 1[Unit]
 2Description=TrapFall Binary Service
 3After=network.target
 4
 5[Service]
 6Type=simple
 7User=www-data
 8WorkingDirectory=/data/trapfall
 9
10# ========== 在这里自定义环境变量 ==========
11Environment="TRAPFALL_LISTEN=127.0.0.1:9090"
12Environment="TRAPFALL_DATABASE_URL=sqlite:/data/trapfall/trapfall.db"
13Environment="TRAPFALL_SECURE_COOKIE=true"
14Environment="TRAPFALL_CORS_ORIGINS=xxx"
15Environment="TRAPFALL_PUBLIC_URL=xxx"
16Environment="RUST_LOG=trapfall=info"
17Environment="TRAPFALL_TIMEZONE=Asia/Shanghai"
18
19ExecStart=/bin/trapfall
20Restart=on-failure
21RestartSec=5
22
23# 资源限制,防止OOM
24MemoryMax=768M
25CPUQuota=150%
26
27[Install]
28WantedBy=multi-user.target

再设置启动:systemctl enable --now trapfall

稍微看下官方文档,理解后使用就 ok 了,实在是懒得敲字了。

3. web 前端面板配置

web 前端面板相关设置, 我用 caddy 代理,只开放给家里电脑访问。

 1# 类似Sentry 的替代轻量工具 Trapfall
 2errors.xxx.com{
 3	encode zstd br gzip
 4	log {
 5		output file /var/log/caddy/xxx.xxx.com.log {
 6			roll_size 30mb
 7			roll_keep 10
 8			roll_keep_for 7d
 9		}
10	}
11
12	@allowed {
13		client_ip xxx.xxx.xxx.xx  # your_home_public_ip
14	}
15
16	handle @allowed {
17		reverse_proxy 127.0.0.1:9090
18	}
19
20	respond "Forbidden: Access denied👻" 403
21}

访问后,设置用户密码,创建项目,让后复制项目的 DSN,给上面的 FastApi 代码使用。

这里有个需要注意⚠️的点:

1SENTRY_DSN = "http://c84e5ad7-adc3-42b8-82b7-e55b9ae414bf@127.0.0.1:9090/1"

仔细看,用 http 协议,后面也是内网地址,因为是同一台机器的两个服务,不用绕道外面去。

4. 触发错误并验证

部署应用错误收集系统的最终目的,就是为了收集信息,方便定位处理的嘛。

  • 直接在 Python REPL 测试投递:
1import sentry_sdk
2sentry_sdk.init(
3    dsn="这里粘贴你完整的TrapFall DSN",
4    traces_sample_rate=1.0,
5)
6try:
7    1/0
8except Exception as e:
9    sentry_sdk.capture_exception(e)
  • 再用你的 sentry-debug 接口测试
1curl \
2-H "xxx: xxx" \
3-H "Referer: xxx" \
4http://你的域名/sentry-debug

你自己的服务需要什么 Header 就都带上,正常没有问题的话,你就可以在 web 前端面板,看到收集信息啦。

如图所示:

trapfall issuers

两行分别对应上面的两种触发方式,相比 sentry 维护起来那么麻烦,组件一堆,真是简单实用啊...

5. 告警配置

老实说,一般个人项目我都是不配置告警的,这辈子被公司告警支配,赚点钱真实不容易。

但是个人项目,有业务错误了,都不修,那可以说毫无责任心了。比如你人在外面玩,个人项目都挂了,总得发个告警让自己知道吧,万一是影响赚打钱了呢?

Trapfall 默认支持 webthook 的放方式和外部交互,在其 web 面板上配置好 rules,需要填 Webhook URL

国内一般是企业微信、飞书、钉钉的,也有手机上的 bark 什么的。这个项目的作者我看了下是个印尼人,不可能原生支持我们国内这一套的。

所以需要自己缝合下,好在现在是 AI 时代,用 AI 我自己花了几个小时用 rust 编写了程序,调试通了告警到飞书群聊机器人。

本质上就是要解决 trapfall 触发告警吐出来的东西,按照飞书 webhook 封装下,两套系统,需要中间程序翻一下。

整个链路简单大概这样:

TrapFall → 中间 Rust 翻译服务(没错,让 AI 用 rust 写的) → 飞书机器人

复杂点是这样:

TrapFall → Caddy (HTTPS) → 127.0.0.1:3000 → 你的 Rust 服务 → 飞书机器人

为什么是 3000,axum 框架默认监听端口,Trapfall 就 Webhook Url 就填你这个中间服务暴露的服务地址。直接填飞书 webhook 肯定是不行的吗,语言不通嘛。

有需要我找个程序的,可以关注我微信公众号留言索取,懒得贴了,不又要写一大堆文字。其实,都不用索取,AI 解决这种转换程序,真的是小菜一碟。

最终触发手动触发错误的时候,收到的告警信息如下图所示:

trapfall lark alert

最后,这样一通操作下来,个人业余项目也是能正常收集致命错误和告警了,有问题就能及时发现并修复。也不用维护 Sentry 等重量级服务,自主可控,多项目都可以自由使用。

就这样很完美了吗?No,实测发现 trapfall 的 rules 相关接口还是有 bug 的,经常删不掉😅,也没办法修改, 8个标星,还要什么自行车?

遇到删不掉,我都是直接去 sqlite 数据库里面删的,rules 也不用经常改动,勉强可以接受。不然呢?提交 pr 吧......

最后修改于: Wednesday, October 28, 2026
欢迎关注微信公众号,留言交流;也欢迎使用我开发的微信小程序。

相关文章:

翻译: