使用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
欢迎关注微信公众号,留言交流;也欢迎使用我开发的微信小程序。

相關文章:

翻譯: