feat: 接入自建 Sentry 错误上报

- DSN 指向 rustrak 自建 Sentry(项目 8),release 标记 mqttx@版本
- panic 路径:sentry-panic 链式钩子捕获 + 外层钩子 flush 5s,
  确保 panic=abort 终止进程前事件送出
- 存储层 elog! 持久化错误同步 capture_message 上报
- 传输用 reqwest+rustls,避免 openssl 依赖
This commit is contained in:
2026-09-29 00:30:23 +08:00
parent bc49d1839c
commit 88a8652d7b
4 changed files with 417 additions and 5 deletions
+9
View File
@@ -43,3 +43,12 @@ rfd = "0.17"
# OTA 自更新:Gitea Release 检查/下载(ureq 用 rustls,与 rumqttc 依赖同源)
ureq = { version = "2", features = ["json"] }
sha2 = "0.10"
# 错误上报(自建 Sentry):panic 捕获 + 持久化错误;rustls 传输避免 openssl 依赖
sentry = { version = "0.49", default-features = false, features = [
"backtrace",
"contexts",
"panic",
"reqwest",
"rustls",
] }
+24
View File
@@ -4,6 +4,19 @@ use gpui_kit::*;
use mqttx_desktop::ui;
use mqttx_desktop::update;
/// 自建 Sentry(rustrak):panic 与关键错误上报。
const SENTRY_DSN: &str =
"https://9853b05c2a3a4fce921c650587168f49@api.rustrak.sentry.heavenlybook.cn/8";
/// 返回的 guard 必须在程序整个生命周期内存活,drop 即停用上报。
fn init_sentry() -> sentry::ClientInitGuard {
sentry::init(
sentry::ClientOptions::new()
.dsn(SENTRY_DSN)
.release(format!("mqttx@{}", env!("CARGO_PKG_VERSION"))),
)
}
fn main() {
// OTA 收尾:清理遗留文件;若暂存区有待安装的新版本(用户上次同意安装),
// 在进入 UI 前完成自替换并重启。
@@ -15,6 +28,17 @@ fn main() {
std::process::exit(0);
}
let _sentry_guard = init_sentry();
// sentry 的 panic 钩子只捕获不等待传输;再包一层在钩子末尾 flush,
// 保证 panic=abort 立即终止进程前事件已送出。
let chained_panic_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
chained_panic_hook(info);
if let Some(client) = sentry::Hub::current().client() {
client.flush(Some(std::time::Duration::from_secs(5)));
}
}));
application()
// AllAssets 嵌入全量 Lucide 图标;Assets 只含 default-icons.txt 的 101 个,
// 其余 IconName(MailOpen/SlidersHorizontal 等)运行时会渲染空白
+7 -4
View File
@@ -4,13 +4,16 @@
//!
//! 刻意与官方 Electron MQTTX 的 `MQTTX/` 目录区分,避免互相覆盖数据。
/// 存储层诊断输出到 stderr(避免仅为文件持久化引入日志框架)。
/// 存储层诊断输出到 stderr(避免仅为文件持久化引入日志框架),
/// 并同步上报 Sentry(未初始化时为空操作)。
macro_rules! elog {
($($arg:tt)*) => {
($($arg:tt)*) => {{
let msg = format!($($arg)*);
eprintln!("[store][{}] {}",
chrono::Local::now().format("%H:%M:%S"),
format!($($arg)*))
};
msg);
sentry::capture_message(&msg, sentry::Level::Error);
}};
}
use std::path::{Path, PathBuf};