feat: more descriptive debug error

This commit is contained in:
Tien Do Nam
2026-08-07 01:12:32 +02:00
parent 6bdfab89da
commit 7dd0777211
5 changed files with 80 additions and 19 deletions
+11 -2
View File
@@ -11,6 +11,7 @@ use crate::model::discovery::{MulticastMessageV2, ProtocolType};
use crate::multicast::{
self, InterfaceFilter, MulticastConfig, MulticastDevice, MulticastEvent, MulticastHandle,
};
use crate::util::error::ErrorChain;
use futures_util::StreamExt;
use std::collections::HashSet;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
@@ -425,7 +426,10 @@ async fn answer_announcement(
) {
Ok(client) => client,
Err(err) => {
tracing::error!("Could not create the client to answer {host}: {err:#}");
tracing::error!(
"Could not create the client to answer {host}: {}",
ErrorChain(&err)
);
return;
}
};
@@ -448,7 +452,12 @@ async fn answer_announcement(
state.found(device).await;
}
Err(err) => {
tracing::debug!("Could not register with announcing device {host}: {err:#}");
let url = format!("{}://{host}:{}", message.protocol.as_str(), message.port);
tracing::debug!(
"Could not register with announcing device {} ({url}): {}",
message.alias,
ErrorChain(&err),
);
}
}
}
+60
View File
@@ -0,0 +1,60 @@
use std::error::Error;
use std::fmt::{Display, Formatter};
/// Displays an error together with its whole source chain, joined by `: `.
///
/// Plain [`Display`] only prints the outermost error, which hides the actual
/// cause for wrappers like `reqwest::Error` ("error sending request for url ...").
pub struct ErrorChain<'a>(pub &'a dyn Error);
impl Display for ErrorChain<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)?;
let mut source = self.0.source();
while let Some(err) = source {
write!(f, ": {err}")?;
source = err.source();
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug)]
struct TestError(&'static str, Option<Box<TestError>>);
impl Display for TestError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Error for TestError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.1.as_ref().map(|err| err as &(dyn Error + 'static))
}
}
#[test]
fn formats_the_whole_chain() {
let err = TestError(
"outer",
Some(Box::new(TestError(
"middle",
Some(Box::new(TestError("inner", None))),
))),
);
assert_eq!(ErrorChain(&err).to_string(), "outer: middle: inner");
}
#[test]
fn formats_a_single_error() {
assert_eq!(ErrorChain(&TestError("only", None)).to_string(), "only");
}
}
+1
View File
@@ -1,3 +1,4 @@
pub mod base64;
pub mod error;
pub mod filename;
pub(crate) mod time;
@@ -7,6 +7,7 @@ use localsend::discovery::{
};
use localsend::model::discovery::{DeviceType, ProtocolType};
use localsend::multicast::{DEFAULT_MULTICAST_GROUP_V6, InterfaceFilter, MulticastDevice};
use localsend::util::error::ErrorChain;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::{Mutex, mpsc, oneshot};
@@ -302,7 +303,11 @@ impl RsDiscovery {
match self.instance.handle.discover(&host, port, protocol).await {
Ok(found) => found.map(rs_stored_device),
Err(err) => {
tracing::debug!("Could not discover {host}:{port}: {err:#}");
tracing::debug!(
"Could not discover {}://{host}:{port}: {}",
protocol.as_str(),
ErrorChain(&err)
);
None
}
}
@@ -9,6 +9,7 @@ pub use localsend::http::dto::{
};
use localsend::model::discovery::ProtocolType;
use localsend::reqwest;
use localsend::util::error::ErrorChain;
pub struct RsHttpClient {
inner: localsend::http::client::LsHttpClient,
@@ -231,7 +232,7 @@ impl From<ClientError> for RsHttpClientError {
status: e.status,
message: e.message,
},
ClientError::Reqwest(e) => RsHttpClientError::Reqwest(error_chain(&e)),
ClientError::Reqwest(e) => RsHttpClientError::Reqwest(ErrorChain(&e).to_string()),
ClientError::Json(e) => RsHttpClientError::Json(e.to_string()),
ClientError::Io(e) => RsHttpClientError::Io(e.to_string()),
ClientError::Other(e) => RsHttpClientError::Other(e.to_string()),
@@ -240,21 +241,6 @@ impl From<ClientError> for RsHttpClientError {
}
}
/// Renders an error together with everything that caused it.
///
/// [`reqwest::Error`] alone only says "error sending request for url (...)".
pub(crate) fn error_chain(e: &dyn std::error::Error) -> String {
use std::fmt::Write;
let mut message = e.to_string();
let mut source = e.source();
while let Some(current) = source {
let _ = write!(message, ": {current}");
source = current.source();
}
message
}
#[frb(mirror(LsHttpClientVersion))]
pub enum _LsHttpClientVersion {
V2,