feat: 优化错误信息的格式

This commit is contained in:
lanyeeee
2026-03-03 04:08:46 +08:00
parent 5478edc770
commit b5db04f5bd
12 changed files with 149 additions and 85 deletions
+11
View File
@@ -317,6 +317,7 @@ dependencies = [
"tokio",
"tracing",
"tracing-appender",
"tracing-error",
"tracing-subscriber",
"uuid",
"yaserde",
@@ -4890,6 +4891,16 @@ dependencies = [
"valuable",
]
[[package]]
name = "tracing-error"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db"
dependencies = [
"tracing",
"tracing-subscriber",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
+1
View File
@@ -40,6 +40,7 @@ parking_lot = { version = "0.12.4", features = ["send_guard"] }
tracing = { version = "0.1.41" }
tracing-subscriber = { version = "0.3.19", features = ["json", "time", "local-time"] }
tracing-appender = { version = "0.2.3" }
tracing-error = { version = "0.2.1" }
notify = { version = "8.0.0" }
tokio = { version = "1.46.0", features = ["full"] }
byteorder = { version = "1.5.0" }
+3 -3
View File
@@ -18,7 +18,7 @@ use tokio::task::JoinSet;
use crate::{
config::ProxyMode,
extensions::{AppHandleExt, EyreToStringChain},
extensions::{AppHandleExt, EyreReportToMessage},
protobuf::DmSegMobileReply,
types::{
bangumi_follow_info::BangumiFollowInfo, bangumi_info::BangumiInfo,
@@ -1132,8 +1132,8 @@ impl ClientBuilderExt for reqwest::ClientBuilder {
Ok(proxy) => self.proxy(proxy),
Err(err) => {
let err_title = format!("{client_name}将`{proxy_url}`设为代理失败,将直连");
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
self.no_proxy()
}
}
+19 -19
View File
@@ -16,7 +16,7 @@ use tokio::sync::Semaphore;
use crate::{
events::DownloadEvent,
extensions::{AppHandleExt, EyreToStringChain},
extensions::{AppHandleExt, EyreReportToMessage},
types::{
create_download_task_params::CreateDownloadTaskParams,
restart_download_task_params::RestartDownloadTaskParams,
@@ -109,8 +109,8 @@ impl DownloadManager {
for task_id in task_ids {
let Some(task) = tasks.get(task_id) else {
let err_title = "暂停下载任务失败";
let err_msg = format!("未找到ID为`{task_id}`的下载任务");
tracing::error!(err_title, message = err_msg);
let message = format!("未找到ID为`{task_id}`的下载任务");
tracing::error!(err_title, message);
continue;
};
task.set_state(DownloadTaskState::Paused);
@@ -123,8 +123,8 @@ impl DownloadManager {
for task_id in task_ids {
let Some(task) = tasks.get(task_id) else {
let err_title = "继续下载任务失败";
let err_msg = format!("未找到ID为`{task_id}`的下载任务");
tracing::error!(err_title, message = err_msg);
let message = format!("未找到ID为`{task_id}`的下载任务");
tracing::error!(err_title, message);
continue;
};
task.set_state(DownloadTaskState::Pending);
@@ -137,15 +137,15 @@ impl DownloadManager {
for task_id in task_ids {
let Some(task) = tasks.remove(task_id) else {
let err_title = "删除下载任务失败";
let err_msg = format!("未找到ID为`{task_id}`的下载任务");
tracing::error!(err_title, message = err_msg);
let message = format!("未找到ID为`{task_id}`的下载任务");
tracing::error!(err_title, message);
continue;
};
if let Err(err) = self.delete_progress_file(task_id) {
let err_title = "删除下载任务失败";
let err_msg = format!("删除ID为`{task_id}`的下载任务文件失败: {err}");
tracing::error!(err_title, message = err_msg);
let message = format!("删除ID为`{task_id}`的下载任务文件失败: {err}");
tracing::error!(err_title, message);
tasks.insert(task_id.clone(), task);
continue;
}
@@ -153,8 +153,8 @@ impl DownloadManager {
if let Err(err) = task.delete_sender.send(()).map_err(eyre::Report::from) {
let err_title = "删除下载任务失败";
let err = err.wrap_err(format!("通知ID为`{task_id}`的下载任务删除失败"));
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
tasks.insert(task_id.clone(), task);
continue;
}
@@ -168,16 +168,16 @@ impl DownloadManager {
for task_id in task_ids {
let Some(task) = tasks.get(task_id) else {
let err_title = "重来下载任务失败";
let err_msg = format!("未找到ID为`{task_id}`的下载任务");
tracing::error!(err_title, message = err_msg);
let message = format!("未找到ID为`{task_id}`的下载任务");
tracing::error!(err_title, message);
continue;
};
if let Err(err) = task.restart_sender.send(()).map_err(eyre::Report::from) {
let err_title = "重来下载任务失败";
let err = err.wrap_err(format!("通知ID为`{task_id}`的下载任务重来失败"));
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
continue;
}
@@ -191,8 +191,8 @@ impl DownloadManager {
let tasks = self.download_tasks.read();
let Some(task) = tasks.get(task_id) else {
let err_title = "重来下载任务失败";
let err_msg = format!("未找到ID为`{task_id}`的下载任务");
tracing::error!(err_title, message = err_msg);
let message = format!("未找到ID为`{task_id}`的下载任务");
tracing::error!(err_title, message);
return;
};
@@ -220,8 +220,8 @@ impl DownloadManager {
if let Err(err) = task.restart_sender.send(()).map_err(eyre::Report::from) {
let err_title = "重来下载任务失败";
let err = err.wrap_err(format!("通知ID为`{task_id}`的下载任务重来失败"));
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
return;
}
+19 -19
View File
@@ -11,7 +11,7 @@ use tokio::{
use crate::{
events::DownloadEvent,
extensions::{AppHandleExt, EyreToStringChain},
extensions::{AppHandleExt, EyreReportToMessage},
types::create_download_task_params::CreateDownloadTaskParams,
};
@@ -42,8 +42,8 @@ impl DownloadTask {
let cid = cid.map_or("None".to_string(), |id| id.to_string());
let ids_string = format!("aid: {aid}, cid: {cid}");
let err_title = format!("{ids_string} 创建普通视频的下载进度失败");
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
continue;
}
};
@@ -58,8 +58,8 @@ impl DownloadTask {
Err(err) => {
let ids_string = format!("ep_id: {ep_id}");
let err_title = format!("{ids_string} 创建番剧的下载进度失败");
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
continue;
}
};
@@ -74,8 +74,8 @@ impl DownloadTask {
Err(err) => {
let ids_string = format!("ep_id: {ep_id}");
let err_title = format!("{ids_string} 创建课程的下载进度失败");
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
continue;
}
};
@@ -91,8 +91,8 @@ impl DownloadTask {
let ids_string = progress.get_ids_string();
let episode_title = &progress.episode_title;
let err_title = format!("{ids_string} `{episode_title}`保存下载进度到文件失败");
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
}
let auto_start = app.get_config().read().auto_start_download_task;
@@ -237,8 +237,8 @@ impl DownloadTask {
.wrap_err("[继续]失败的任务可以断点续传")
{
let err_title = format!("{ids_string} `{episode_title}`下载失败");
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
self.set_state(DownloadTaskState::Failed);
@@ -289,8 +289,8 @@ impl DownloadTask {
Err(err) => {
let err_title =
format!("{ids_string} `{episode_title}`获取下载任务的permit失败");
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
self.set_state(DownloadTaskState::Failed);
@@ -309,8 +309,8 @@ impl DownloadTask {
.map_err(eyre::Report::from)
{
let err_title = format!("{ids_string} `{episode_title}`发送状态`Downloading`失败");
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
self.set_state(DownloadTaskState::Failed);
}
@@ -352,8 +352,8 @@ impl DownloadTask {
if let Err(err) = self.state_sender.send(state).map_err(eyre::Report::from) {
let err_title = format!("{ids_string} `{episode_title}`发送状态`{state:?}`失败");
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
}
}
@@ -374,8 +374,8 @@ impl DownloadTask {
let ids_string = updated_progress.get_ids_string();
let episode_title = &updated_progress.episode_title;
let err_title = format!("{ids_string} `{episode_title}`保存下载进度到文件失败");
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
}
}
}
+3 -3
View File
@@ -18,7 +18,7 @@ use crate::{
download_chunk_task::DownloadChunkTask, download_progress::DownloadProgress,
download_task::DownloadTask, media_chunk::MediaChunk,
},
extensions::{AppHandleExt, EyreToStringChain},
extensions::{AppHandleExt, EyreReportToMessage},
types::{
audio_quality::AudioQuality, bangumi_media_url::BangumiMediaUrl,
cheese_media_url::CheeseMediaUrl, normal_media_url::NormalMediaUrl,
@@ -393,8 +393,8 @@ impl AudioTask {
Ok(i) => download_task.update_progress(|p| p.audio_task.chunks[i].completed = true),
Err(err) => {
let err_title = format!("{ids_string} `{episode_title}`音频的一个分片下载失败");
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
}
}
}
+3 -3
View File
@@ -18,7 +18,7 @@ use crate::{
download_chunk_task::DownloadChunkTask, download_progress::DownloadProgress,
download_task::DownloadTask, media_chunk::MediaChunk,
},
extensions::{AppHandleExt, EyreToStringChain},
extensions::{AppHandleExt, EyreReportToMessage},
types::{
bangumi_media_url::BangumiMediaUrl, cheese_media_url::CheeseMediaUrl,
codec_type::CodecType, normal_media_url::NormalMediaUrl, video_quality::VideoQuality,
@@ -404,8 +404,8 @@ impl VideoTask {
Ok(i) => download_task.update_progress(|p| p.video_task.chunks[i].completed = true),
Err(err) => {
let err_title = format!("{ids_string} `{episode_title}`视频的一个分片下载失败");
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
}
}
}
+64 -6
View File
@@ -1,14 +1,16 @@
use std::panic::Location;
use eyre::EyreHandler;
use serde::{Deserialize, Serialize};
use specta::Type;
use crate::extensions::EyreToStringChain;
use tracing_error::SpanTrace;
pub type CommandResult<T> = Result<T, CommandError>;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct CommandError {
pub err_title: String,
pub err_message: String,
pub message: String,
}
impl CommandError {
@@ -16,11 +18,67 @@ impl CommandError {
where
E: Into<eyre::Report>,
{
let string_chain = err.into().to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = format!("{:?}", err.into());
tracing::error!(err_title, message);
Self {
err_title: err_title.to_string(),
err_message: string_chain,
message,
}
}
}
struct CustomEyreHandler {
span_trace: SpanTrace,
location: Option<&'static Location<'static>>,
}
impl EyreHandler for CustomEyreHandler {
fn debug(
&self,
error: &(dyn std::error::Error + 'static),
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
use std::fmt::Write;
let mut buf = String::new();
writeln!(&mut buf, "Error:")?;
writeln!(&mut buf, " 0: {error}")?;
let mut current = error.source();
let mut i = 1;
while let Some(cause) = current {
writeln!(&mut buf, " {i}: {cause}")?;
current = cause.source();
i += 1;
}
if let Some(loc) = self.location {
writeln!(&mut buf, "Location:")?;
writeln!(&mut buf, " at {}:{}", loc.file(), loc.line())?;
}
let span_trace = format!("{}", self.span_trace);
if !span_trace.is_empty() {
writeln!(&mut buf, "SpanTrace:")?;
writeln!(&mut buf, "{span_trace}")?;
}
write!(f, "{}", buf.trim_end())?;
Ok(())
}
fn track_caller(&mut self, location: &'static Location<'static>) {
self.location = Some(location);
}
}
pub fn install_custom_eyre_handler() -> eyre::Result<()> {
eyre::set_hook(Box::new(|_error| {
Box::new(CustomEyreHandler {
span_trace: SpanTrace::capture(),
location: None,
})
}))?;
Ok(())
}
+5 -16
View File
@@ -9,24 +9,13 @@ use crate::{
types::player_info::PlayerInfo,
};
pub trait EyreToStringChain {
/// 将 `eyre::Report` 转换为chain格式
/// # Example
/// 0: error message\
/// 1: error message\
/// 2: error message
fn to_string_chain(&self) -> String;
pub trait EyreReportToMessage {
fn to_message(&self) -> String;
}
impl EyreToStringChain for eyre::Report {
fn to_string_chain(&self) -> String {
use std::fmt::Write;
self.chain()
.enumerate()
.fold(String::new(), |mut output, (i, e)| {
let _ = writeln!(output, "{i}: {e}");
output
})
impl EyreReportToMessage for eyre::Report {
fn to_message(&self) -> String {
format!("{self:?}")
}
}
+3
View File
@@ -31,6 +31,7 @@ use tauri::{Manager, Wry};
use crate::{
bili_client::BiliClient,
downloader::download_manager::DownloadManager,
errors::install_custom_eyre_handler,
events::{DownloadEvent, LogEvent},
};
@@ -41,6 +42,8 @@ fn generate_context() -> tauri::Context<Wry> {
#[allow(clippy::missing_panics_doc)]
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
install_custom_eyre_handler().unwrap();
let builder = tauri_specta::Builder::<Wry>::new()
.commands(tauri_specta::collect_commands![
get_config,
+17 -15
View File
@@ -9,6 +9,7 @@ use tracing_appender::{
non_blocking::WorkerGuard,
rolling::{RollingFileAppender, Rotation},
};
use tracing_error::ErrorLayer;
use tracing_subscriber::{
Layer, Registry,
filter::{FilterExt, Targets, filter_fn},
@@ -20,7 +21,7 @@ use tracing_subscriber::{
use crate::{
events::LogEvent,
extensions::{AppHandleExt, EyreToStringChain},
extensions::{AppHandleExt, EyreReportToMessage},
};
struct LogEventWriter {
@@ -36,8 +37,8 @@ impl Write for LogEventWriter {
}
Err(err) => {
let log_string = log_string.to_string();
let err_msg = err.to_string();
tracing::error!(log_string, err_msg, "将日志字符串解析为LogEvent失败");
let message = err.to_string();
tracing::error!(log_string, message, "将日志字符串解析为LogEvent失败");
}
}
Ok(buf.len())
@@ -85,6 +86,7 @@ pub fn init(app: &AppHandle) -> eyre::Result<()> {
.with(reloadable_file_layer)
.with(console_layer)
.with(log_event_layer)
.with(ErrorLayer::default())
.init();
GUARD.get_or_init(|| parking_lot::Mutex::new(guard));
@@ -154,8 +156,8 @@ async fn file_log_watcher(app: AppHandle) {
tauri::async_runtime::block_on(async {
if let Err(err) = sender.send(res).await.map_err(eyre::Report::from) {
let err_title = "发送日志文件watcher事件失败";
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
}
});
};
@@ -166,8 +168,8 @@ async fn file_log_watcher(app: AppHandle) {
Ok(watcher) => watcher,
Err(err) => {
let err_title = "创建日志文件watcher失败";
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
return;
}
};
@@ -176,8 +178,8 @@ async fn file_log_watcher(app: AppHandle) {
Ok(logs_dir) => logs_dir,
Err(err) => {
let err_title = "日志文件watcher获取日志目录失败";
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
return;
}
};
@@ -187,8 +189,8 @@ async fn file_log_watcher(app: AppHandle) {
.map_err(eyre::Report::from)
{
let err_title = "日志文件watcher监听日志目录失败";
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
return;
}
@@ -199,14 +201,14 @@ async fn file_log_watcher(app: AppHandle) {
&& let Err(err) = reload_file_logger()
{
let err_title = "重置日志文件失败";
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
}
}
Err(err) => {
let err_title = "接收日志文件watcher事件失败";
let string_chain = err.to_string_chain();
tracing::error!(err_title, message = string_chain);
let message = err.to_message();
tracing::error!(err_title, message);
}
}
}
+1 -1
View File
@@ -266,7 +266,7 @@ export type CheeseSearchResult = { ep: EpInCheese | null; info: CheeseInfo }
export type CntInfo = { collect: number; play: number; thumb_up: number; share: number }
export type CntInfoInMedia = { collect: number; play: number; danmaku: number; vt: number; play_switch: number; reply: number; view_text_1: string }
export type CodecType = "Unknown" | "Audio" | "AVC" | "HEVC" | "AV1"
export type CommandError = { err_title: string; err_message: string }
export type CommandError = { err_title: string; message: string }
export type Config = { download_dir: string; enable_file_logger: boolean; sessdata: string; video_quality_priority: VideoQuality[]; codec_type_priority: CodecType[]; audio_quality_priority: AudioQuality[]; download_video: boolean; download_audio: boolean; auto_merge: boolean; embed_chapter: boolean; embed_skip: boolean; download_xml_danmaku: boolean; download_ass_danmaku: boolean; download_json_danmaku: boolean; download_subtitle: boolean; download_cover: boolean; download_nfo: boolean; download_json: boolean; dir_fmt: string; dir_fmt_for_part: string; time_fmt: string; proxy_mode: ProxyMode; proxy_host: string; proxy_port: number; task_concurrency: number; task_download_interval_sec: number; chunk_concurrency: number; chunk_download_interval_sec: number; danmaku_config: CanvasConfig; file_exist_action: FileExistAction; auto_start_download_task: boolean }
export type Consulting = { consulting_flag: boolean; consulting_url: string }
export type ContentAttr = { text: string; bg_color: string; bg_color_night: string; img: string; multi_img: MultiImg }