Skip to content

Commit

Permalink
reimplement all of kellpossibles pr
Browse files Browse the repository at this point in the history
  • Loading branch information
yaahc committed Nov 17, 2020
1 parent d8cde8a commit 3601d21
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 127 deletions.
284 changes: 162 additions & 122 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,13 +503,8 @@ impl HookBuilder {
/// Install the given Hook as the global error report hook
pub fn install(self) -> Result<(), crate::eyre::Report> {
let (panic_hook, eyre_hook) = self.into_hooks();
crate::eyre::set_hook(Box::new(move |e| Box::new(eyre_hook.default(e))))?;
install_panic_hook();

if crate::CONFIG.set(panic_hook).is_err() {
Err(InstallError)?
}

eyre_hook.install()?;
panic_hook.install();
Ok(())
}

Expand All @@ -519,11 +514,13 @@ impl HookBuilder {
.add_frame_filter(Box::new(eyre_frame_filters))
}

pub(crate) fn into_hooks(self) -> (PanicHook, EyreHook) {
/// Create a `PanicHook` and `EyreHook` from this `HookBuilder`.
/// This can be used if you want to combine these handlers with other handlers.
pub fn into_hooks(self) -> (PanicHook, EyreHook) {
#[cfg(feature = "issue-url")]
let metadata = Arc::new(self.issue_metadata);
let panic_hook = PanicHook {
filters: self.filters.into_iter().map(Into::into).collect(),
filters: self.filters.into(),
section: self.panic_section,
#[cfg(feature = "capture-spantrace")]
capture_span_trace_by_default: self.capture_span_trace_by_default,
Expand All @@ -538,6 +535,7 @@ impl HookBuilder {
};

let eyre_hook = EyreHook {
filters: panic_hook.filters.clone(),
#[cfg(feature = "capture-spantrace")]
capture_span_trace_by_default: self.capture_span_trace_by_default,
display_env_section: self.display_env_section,
Expand Down Expand Up @@ -596,18 +594,6 @@ fn eyre_frame_filters(frames: &mut Vec<&Frame>) {
});
}

struct PanicPrinter<'a>(&'a std::panic::PanicInfo<'a>);

impl fmt::Display for PanicPrinter<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
print_panic_info(self, f)
}
}

fn install_panic_hook() {
std::panic::set_hook(Box::new(|pi| eprintln!("{}", PanicPrinter(pi))))
}

struct DefaultPanicMessage;

impl PanicMessage for DefaultPanicMessage {
Expand All @@ -633,93 +619,9 @@ impl PanicMessage for DefaultPanicMessage {
}
}

fn print_panic_info(printer: &PanicPrinter<'_>, out: &mut fmt::Formatter<'_>) -> fmt::Result {
let hook = installed_hook();
hook.panic_message.display(printer.0, out)?;

let v = panic_verbosity();
let capture_bt = v != Verbosity::Minimal;

#[cfg(feature = "capture-spantrace")]
let span_trace = if hook.spantrace_capture_enabled() {
Some(tracing_error::SpanTrace::capture())
} else {
None
};

let bt = if capture_bt {
Some(backtrace::Backtrace::new())
} else {
None
};

let mut separated = out.header("\n\n");

if let Some(ref section) = hook.section {
write!(&mut separated.ready(), "{}", section)?;
}

#[cfg(feature = "capture-spantrace")]
{
if let Some(span_trace) = span_trace.as_ref() {
write!(
&mut separated.ready(),
"{}",
crate::writers::FormattedSpanTrace(span_trace)
)?;
}
}

if let Some(bt) = bt.as_ref() {
let fmted_bt = hook.format_backtrace(&bt);
write!(
indented(&mut separated.ready()).with_format(Format::Uniform { indentation: " " }),
"{}",
fmted_bt
)?;
}

if hook.display_env_section {
let env_section = EnvSection {
bt_captured: &capture_bt,
#[cfg(feature = "capture-spantrace")]
span_trace: span_trace.as_ref(),
};

write!(&mut separated.ready(), "{}", env_section)?;
}

#[cfg(feature = "issue-url")]
{
let payload = printer.0.payload();

if hook.issue_url.is_some()
&& (*hook.issue_filter)(crate::ErrorKind::NonRecoverable(payload))
{
let url = hook.issue_url.as_ref().unwrap();
let payload = payload
.downcast_ref::<String>()
.map(String::as_str)
.or_else(|| payload.downcast_ref::<&str>().cloned())
.unwrap_or("<non string panic payload>");

let issue_section = crate::section::github::IssueSection::new(url, payload)
.with_backtrace(bt.as_ref())
.with_location(printer.0.location())
.with_metadata(&**hook.issue_metadata);

#[cfg(feature = "capture-spantrace")]
let issue_section = issue_section.with_span_trace(span_trace.as_ref());

write!(&mut separated.ready(), "{}", issue_section)?;
}
}

Ok(())
}

pub(crate) struct PanicHook {
filters: Vec<Arc<FilterCallback>>,
/// A panic reporting hook
pub struct PanicHook {
filters: Arc<[Box<FilterCallback>]>,
section: Option<Box<dyn Display + Send + Sync + 'static>>,
panic_message: Box<dyn PanicMessage>,
#[cfg(feature = "capture-spantrace")]
Expand All @@ -739,7 +641,7 @@ impl PanicHook {
trace: &'a backtrace::Backtrace,
) -> BacktraceFormatter<'a> {
BacktraceFormatter {
printer: self,
filters: &self.filters,
inner: trace,
}
}
Expand All @@ -750,9 +652,138 @@ impl PanicHook {
.map(|val| val != "0")
.unwrap_or(self.capture_span_trace_by_default)
}

/// Install self as a global panic hook via `std::panic::set_hook`.
pub fn install(self) {
std::panic::set_hook(self.into_panic_hook());
}

/// Convert self into the type expected by `std::panic::set_hook`.
pub fn into_panic_hook(
self,
) -> Box<dyn Fn(&std::panic::PanicInfo<'_>) + Send + Sync + 'static> {
Box::new(move |panic_info| {
eprintln!("{}", self.panic_report(panic_info));
})
}

/// Construct a panic reporter which prints it's panic report via the
/// `Display` trait.
pub fn panic_report<'a>(
&'a self,
panic_info: &'a std::panic::PanicInfo<'_>,
) -> PanicReport<'a> {
let v = panic_verbosity();
let capture_bt = v != Verbosity::Minimal;

#[cfg(feature = "capture-spantrace")]
let span_trace = if self.spantrace_capture_enabled() {
Some(tracing_error::SpanTrace::capture())
} else {
None
};

let backtrace = if capture_bt {
Some(backtrace::Backtrace::new())
} else {
None
};

PanicReport {
panic_info,
#[cfg(feature = "capture-spantrace")]
span_trace,
backtrace,
hook: self,
}
}
}

pub(crate) struct EyreHook {
/// A type representing an error report for a panic.
pub struct PanicReport<'a> {
hook: &'a PanicHook,
panic_info: &'a std::panic::PanicInfo<'a>,
backtrace: Option<backtrace::Backtrace>,
#[cfg(feature = "capture-spantrace")]
span_trace: Option<tracing_error::SpanTrace>,
}

impl<'a, 'b> fmt::Display for PanicReport<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.hook.panic_message.display(self.panic_info, f)?;

let v = panic_verbosity();
let capture_bt = v != Verbosity::Minimal;

let mut separated = f.header("\n\n");

if let Some(ref section) = self.hook.section {
write!(&mut separated.ready(), "{}", section)?;
}

#[cfg(feature = "capture-spantrace")]
{
if let Some(span_trace) = self.span_trace.as_ref() {
write!(
&mut separated.ready(),
"{}",
crate::writers::FormattedSpanTrace(span_trace)
)?;
}
}

if let Some(bt) = self.backtrace.as_ref() {
let fmted_bt = self.hook.format_backtrace(&bt);
write!(
indented(&mut separated.ready()).with_format(Format::Uniform { indentation: " " }),
"{}",
fmted_bt
)?;
}

if self.hook.display_env_section {
let env_section = EnvSection {
bt_captured: &capture_bt,
#[cfg(feature = "capture-spantrace")]
span_trace: self.span_trace.as_ref(),
};

write!(&mut separated.ready(), "{}", env_section)?;
}

#[cfg(feature = "issue-url")]
{
let payload = self.panic_info.payload();

if self.hook.issue_url.is_some()
&& (*self.hook.issue_filter)(crate::ErrorKind::NonRecoverable(payload))
{
let url = self.hook.issue_url.as_ref().unwrap();
let payload = payload
.downcast_ref::<String>()
.map(String::as_str)
.or_else(|| payload.downcast_ref::<&str>().cloned())
.unwrap_or("<non string panic payload>");

let issue_section = crate::section::github::IssueSection::new(url, payload)
.with_backtrace(self.backtrace.as_ref())
.with_location(self.panic_info.location())
.with_metadata(&**self.hook.issue_metadata);

#[cfg(feature = "capture-spantrace")]
let issue_section = issue_section.with_span_trace(self.span_trace.as_ref());

write!(&mut separated.ready(), "{}", issue_section)?;
}
}

Ok(())
}
}

/// An eyre reporting hook used to construct `EyreHandler`s
pub struct EyreHook {
filters: Arc<[Box<FilterCallback>]>,
#[cfg(feature = "capture-spantrace")]
capture_span_trace_by_default: bool,
display_env_section: bool,
Expand Down Expand Up @@ -783,6 +814,7 @@ impl EyreHook {
};

crate::Handler {
filters: self.filters.clone(),
backtrace,
#[cfg(feature = "capture-spantrace")]
span_trace,
Expand All @@ -805,11 +837,28 @@ impl EyreHook {
.map(|val| val != "0")
.unwrap_or(self.capture_span_trace_by_default)
}

/// Installs self as the global eyre handling hook via `eyre::set_hook`
pub fn install(self) -> Result<(), crate::eyre::InstallError> {
crate::eyre::set_hook(self.into_eyre_hook())
}

/// Convert the self into the boxed type expected by `eyre::set_hook`.
pub fn into_eyre_hook(
self,
) -> Box<
dyn Fn(&(dyn std::error::Error + 'static)) -> Box<dyn eyre::EyreHandler>
+ Send
+ Sync
+ 'static,
> {
Box::new(move |e| Box::new(self.default(e)))
}
}

pub(crate) struct BacktraceFormatter<'a> {
printer: &'a PanicHook,
inner: &'a backtrace::Backtrace,
pub(crate) filters: &'a [Box<FilterCallback>],
pub(crate) inner: &'a backtrace::Backtrace,
}

impl fmt::Display for BacktraceFormatter<'_> {
Expand All @@ -835,7 +884,7 @@ impl fmt::Display for BacktraceFormatter<'_> {
match env::var("COLORBT_SHOW_HIDDEN").ok().as_deref() {
Some("1") | Some("on") | Some("y") => (),
_ => {
for filter in &self.printer.filters {
for filter in self.filters {
filter(&mut filtered_frames);
}
}
Expand Down Expand Up @@ -889,15 +938,6 @@ impl fmt::Display for BacktraceFormatter<'_> {
}
}

pub(crate) fn installed_hook() -> &'static PanicHook {
crate::CONFIG.get_or_init(default_printer)
}

fn default_printer() -> PanicHook {
let (panic_hook, _) = HookBuilder::default().into_hooks();
panic_hook
}

#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub(crate) enum Verbosity {
Minimal,
Expand Down
Loading

0 comments on commit 3601d21

Please sign in to comment.