fix: rowid column shift in get_message_with_rowid; add file logging and frontend error reporting
This commit is contained in:
Generated
+21
@@ -3743,6 +3743,12 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "symlink"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "1.0.109"
|
||||||
@@ -4471,6 +4477,19 @@ dependencies = [
|
|||||||
"tracing-core",
|
"tracing-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-appender"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "050686193eb999b4bb3bc2acfa891a13da00f79734704c4b8b4ef1a10b368a3c"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"symlink",
|
||||||
|
"thiserror 2.0.20",
|
||||||
|
"time",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-attributes"
|
name = "tracing-attributes"
|
||||||
version = "0.1.31"
|
version = "0.1.31"
|
||||||
@@ -5522,6 +5541,7 @@ dependencies = [
|
|||||||
name = "xianren-desktop"
|
name = "xianren-desktop"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"dirs 5.0.1",
|
||||||
"futures",
|
"futures",
|
||||||
"reqwest 0.12.28",
|
"reqwest 0.12.28",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -5531,6 +5551,7 @@ dependencies = [
|
|||||||
"tauri-plugin-opener",
|
"tauri-plugin-opener",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"tracing-appender",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"uuid",
|
"uuid",
|
||||||
"xianren-api",
|
"xianren-api",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ serde_json = "1"
|
|||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
|
tracing-appender = "0.2"
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "rustls-tls"] }
|
reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "rustls-tls"] }
|
||||||
|
|||||||
@@ -21,12 +21,14 @@ tauri = { version = "2", features = [] }
|
|||||||
tauri-plugin-opener = "2"
|
tauri-plugin-opener = "2"
|
||||||
tracing-subscriber.workspace = true
|
tracing-subscriber.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
|
tracing-appender.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
reqwest.workspace = true
|
reqwest.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
|
dirs.workspace = true
|
||||||
xianren-core = { path = "../../crates/core" }
|
xianren-core = { path = "../../crates/core" }
|
||||||
xianren-engine = { path = "../../crates/engine" }
|
xianren-engine = { path = "../../crates/engine" }
|
||||||
xianren-download = { path = "../../crates/download" }
|
xianren-download = { path = "../../crates/download" }
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ pub struct AppInfo {
|
|||||||
pub version: String,
|
pub version: String,
|
||||||
pub platform: String,
|
pub platform: String,
|
||||||
pub models_dir: String,
|
pub models_dir: String,
|
||||||
|
pub logs_dir: String,
|
||||||
pub engine_bin: Option<String>,
|
pub engine_bin: Option<String>,
|
||||||
pub engine_exists: bool,
|
pub engine_exists: bool,
|
||||||
pub db_path: String,
|
pub db_path: String,
|
||||||
@@ -133,6 +134,7 @@ pub fn app_info(state: State<'_, App>) -> AppInfo {
|
|||||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
platform: std::env::consts::OS.to_string(),
|
platform: std::env::consts::OS.to_string(),
|
||||||
models_dir: state.core.models_dir.to_string_lossy().to_string(),
|
models_dir: state.core.models_dir.to_string_lossy().to_string(),
|
||||||
|
logs_dir: state.core.logs_dir.to_string_lossy().to_string(),
|
||||||
engine_exists: engine_bin
|
engine_exists: engine_bin
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|p| PathBuf::from(p).exists()),
|
.is_some_and(|p| PathBuf::from(p).exists()),
|
||||||
@@ -1416,3 +1418,10 @@ pub async fn server_status(state: State<'_, App>) -> Result<serde_json::Value, S
|
|||||||
pub fn open_path(path: String) -> Result<(), String> {
|
pub fn open_path(path: String) -> Result<(), String> {
|
||||||
tauri_plugin_opener::open_path(path, None::<&str>).map_err(|e| e.to_string())
|
tauri_plugin_opener::open_path(path, None::<&str>).map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 前端上报运行时错误:写入本地日志便于排查。
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn report_error(source: String, message: String) -> Result<(), String> {
|
||||||
|
tracing::error!(source = %source, error = %message, "frontend reported error");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -17,11 +17,20 @@ pub struct App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
|
let data_dir = dirs::data_dir()
|
||||||
|
.unwrap_or_else(|| PathBuf::from("."))
|
||||||
|
.join("XianrenStudio");
|
||||||
|
let logs_dir = data_dir.join("logs");
|
||||||
|
let _ = std::fs::create_dir_all(&logs_dir);
|
||||||
|
let file_appender = tracing_appender::rolling::daily(&logs_dir, "app.log");
|
||||||
|
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
.with_env_filter(
|
.with_env_filter(
|
||||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||||
.unwrap_or_else(|_| "info".into()),
|
.unwrap_or_else(|_| "info".into()),
|
||||||
)
|
)
|
||||||
|
.with_writer(non_blocking)
|
||||||
|
.with_ansi(false)
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
@@ -115,6 +124,7 @@ pub fn run() {
|
|||||||
commands::edit_message,
|
commands::edit_message,
|
||||||
commands::list_message_versions,
|
commands::list_message_versions,
|
||||||
commands::apply_message_version,
|
commands::apply_message_version,
|
||||||
|
commands::report_error,
|
||||||
commands::download_enqueue,
|
commands::download_enqueue,
|
||||||
commands::server_start,
|
commands::server_start,
|
||||||
commands::server_stop,
|
commands::server_stop,
|
||||||
|
|||||||
@@ -148,11 +148,11 @@ pub fn get_message(db: &Connection, id: &str) -> Result<Option<Message>> {
|
|||||||
|
|
||||||
pub fn get_message_with_rowid(db: &Connection, id: &str) -> Result<Option<(i64, Message)>> {
|
pub fn get_message_with_rowid(db: &Connection, id: &str) -> Result<Option<(i64, Message)>> {
|
||||||
let mut stmt = db.prepare(
|
let mut stmt = db.prepare(
|
||||||
"SELECT rowid, id, conversation_id, role, content, tokens_in, tokens_out, elapsed_ms, first_token_ms, images_json, created_at
|
"SELECT id, conversation_id, role, content, tokens_in, tokens_out, elapsed_ms, first_token_ms, images_json, created_at, rowid
|
||||||
FROM messages WHERE id = ?1",
|
FROM messages WHERE id = ?1",
|
||||||
)?;
|
)?;
|
||||||
let mut rows = stmt.query_map(params![id], |row| {
|
let mut rows = stmt.query_map(params![id], |row| {
|
||||||
let rowid: i64 = row.get(0)?;
|
let rowid: i64 = row.get(10)?;
|
||||||
let msg = row_to_message(row)?;
|
let msg = row_to_message(row)?;
|
||||||
Ok((rowid, msg))
|
Ok((rowid, msg))
|
||||||
})?;
|
})?;
|
||||||
|
|||||||
+19
-1
@@ -1,10 +1,28 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke as rawInvoke } from "@tauri-apps/api/core";
|
||||||
import { listen } from "@tauri-apps/api/event";
|
import { listen } from "@tauri-apps/api/event";
|
||||||
|
|
||||||
|
/** 统一命令包装:失败时自动上报错误到本地日志,再抛出给界面。 */
|
||||||
|
async function invoke<T>(command: string, args?: Record<string, unknown>): Promise<T> {
|
||||||
|
try {
|
||||||
|
return await rawInvoke<T>(command, args);
|
||||||
|
} catch (error) {
|
||||||
|
try {
|
||||||
|
await rawInvoke("report_error", {
|
||||||
|
source: command,
|
||||||
|
message: String(error),
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
// 上报失败不影响主流程
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface AppInfo {
|
export interface AppInfo {
|
||||||
version: string;
|
version: string;
|
||||||
platform: string;
|
platform: string;
|
||||||
models_dir: string;
|
models_dir: string;
|
||||||
|
logs_dir: string;
|
||||||
engine_bin: string | null;
|
engine_bin: string | null;
|
||||||
engine_exists: boolean;
|
engine_exists: boolean;
|
||||||
db_path: string;
|
db_path: string;
|
||||||
|
|||||||
@@ -30,6 +30,19 @@ export default function SettingsPage() {
|
|||||||
<div>
|
<div>
|
||||||
数据目录:<span className="text-slate-300">{info?.db_path}</span>
|
数据目录:<span className="text-slate-300">{info?.db_path}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span>
|
||||||
|
日志目录:<span className="text-slate-300">{info?.logs_dir}</span>
|
||||||
|
</span>
|
||||||
|
{info?.logs_dir ? (
|
||||||
|
<button
|
||||||
|
className="text-xs text-slate-400 hover:text-slate-200"
|
||||||
|
onClick={() => api.openPath(info.logs_dir!)}
|
||||||
|
>
|
||||||
|
打开
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
引擎可用:
|
引擎可用:
|
||||||
{info?.engine_exists ? (
|
{info?.engine_exists ? (
|
||||||
@@ -103,4 +116,3 @@ function SettingInput({
|
|||||||
</label>
|
</label>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user