Merge branch 'err-dyn-report' into 'main'

Error logging (ErrorReport, .report()) POC

See merge request tpo/core/arti!936
This commit is contained in:
Ian Jackson 2022-12-15 15:02:17 +00:00
commit fa5a417fc9
4 changed files with 51 additions and 7 deletions

View File

@ -39,6 +39,7 @@
use tor_basic_utils::retry::RetryDelay;
use tor_chanmgr::ChanMgr;
use tor_error::ErrorReport;
use tor_linkspec::ChanTarget;
use tor_netdir::{DirEvent, NetDir, NetDirProvider, Timeliness};
use tor_proto::circuit::{CircParameters, ClientCirc, UniqId};
@ -642,7 +643,7 @@ impl<R: Runtime> CircMgr<R> {
match statemgr.try_lock() {
Err(e) => {
error!("Problem with state lock file: {}", e);
error!("Problem with state lock file: {}", e.report());
break;
}
Ok(NewlyAcquired) => {

View File

@ -291,6 +291,17 @@ mod test {
assert!(s.contains("Couldn't wobble the wobbling device."));
#[cfg(feature = "backtrace")]
assert!(s.contains("internal_macro_test"));
#[derive(thiserror::Error, Debug)]
enum Wrap {
#[error("Internal error")]
Internal(#[from] Bug),
}
let w: Wrap = e.into();
let s = format!("Got: {}", w.report());
dbg!(&s);
assert!(s.contains("Couldn't wobble the wobbling device."));
}
#[test]

View File

@ -1,5 +1,6 @@
//! The Report type which reports errors nicely
use std::error::Error as StdError;
use std::fmt::{self, Debug, Display};
/// Wraps any Error, providing a nicely-reporting Display impl
@ -7,15 +8,15 @@ use std::fmt::{self, Debug, Display};
#[allow(clippy::exhaustive_structs)] // this is a transparent wrapper
pub struct Report<E>(pub E)
where
E: AsRef<dyn std::error::Error>;
E: AsRef<dyn StdError>;
impl<E> Display for Report<E>
where
E: AsRef<dyn std::error::Error>,
E: AsRef<dyn StdError>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
/// Non-generic inner function avoids code bloat
fn inner(mut e: &dyn std::error::Error, f: &mut fmt::Formatter) -> fmt::Result {
fn inner(mut e: &dyn StdError, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "error")?;
let mut last = String::new();
loop {
@ -44,7 +45,7 @@ where
#[allow(clippy::print_stderr)] // this is the point of this function
pub fn report_and_exit<E, R>(e: E) -> R
where
E: AsRef<dyn std::error::Error>,
E: AsRef<dyn StdError>,
{
/// Non-generic inner function avoids code bloat
fn eprint_progname() {
@ -58,10 +59,41 @@ where
std::process::exit(127)
}
/// Helper type for reporting errors that are concrete implementors of `StdError`
///
/// This is an opaque type, only constructable via the `ErrorExt` helper trait
/// and only useable via its `AsRef` implementation.
//
// We need this because Rust's trait object handling rules, and provided AsRef impls,
// are rather anaemic. We cannot simply put a &dyn Error into Report, because
// &dyn Error doesn't impl AsRef<dyn Error> even though the implementation is trivial.
// We can't provide that AsRef impl ourselves due to trait coherency rules.
// So instead, we wrap up the &dyn Error in a newtype, for which we *can* provide the AsRef.
pub struct ReportHelper<'e>(&'e (dyn StdError + 'static));
impl<'e> AsRef<dyn StdError + 'static> for ReportHelper<'e> {
fn as_ref(&self) -> &(dyn StdError + 'static) {
self.0
}
}
/// Extension trait providing `.report()` method on concrete errors
///
/// This is implemented for types that directly implement [`std::error::Error`]` + 'static`.
/// For types like `anyhow::Error` that `impl AsRef<dyn Error>`,
/// use `tor_error::Report(err)` directly.
pub trait ErrorReport: StdError + Sized + 'static {
/// Return an object that displays the error and its causes
//
// We would ideally have returned `Report<impl AsRef<...>>` but that's TAIT.
fn report(&self) -> Report<ReportHelper> {
Report(ReportHelper(self as _))
}
}
impl<E: StdError + Sized + 'static> ErrorReport for E {}
#[cfg(test)]
mod test {
use super::*;
use std::error::Error as StdError;
use std::io;
use thiserror::Error;

View File

@ -495,7 +495,7 @@ pub(crate) mod test {
.build()
.unwrap();
let send1 = send1.sink_map_err(|e| {
trace!("got sink error: {}", e);
trace!("got sink error: {:?}", e);
CodecError::DecCell(tor_cell::Error::ChanProto("dummy message".into()))
});
let (chan, reactor) = crate::channel::Channel::new(