Skip to content

Commit

Permalink
Add support for customized panic message sections (#57)
Browse files Browse the repository at this point in the history
* (cargo-release) start next development iteration 0.5.3-alpha.0

* add support for customized panic messages

* reorg a little

* add example and changelog

* reorder items in example

* fix missing semi
  • Loading branch information
yaahc authored Sep 15, 2020
1 parent 17c325d commit ff84554
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 23 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- add `panic_section` method to `HookBuilder` for overriding the printer for
the panic message at the start of panic reports

## [0.5.2] - 2020-08-31
### Added
Expand Down
105 changes: 84 additions & 21 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Configuration options for customizing the behavior of the provided panic
//! and error reporting hooks
use crate::writers::{EnvSection, WriterExt};
use crate::{
section::PanicMessage,
writers::{EnvSection, WriterExt},
};
use fmt::Display;
use indenter::{indented, Format};
use owo_colors::OwoColorize;
Expand Down Expand Up @@ -246,6 +249,7 @@ pub struct HookBuilder {
capture_span_trace_by_default: bool,
display_env_section: bool,
panic_section: Option<Box<dyn Display + Send + Sync + 'static>>,
panic_message: Box<dyn PanicMessage>,
}

impl HookBuilder {
Expand Down Expand Up @@ -279,6 +283,7 @@ impl HookBuilder {
capture_span_trace_by_default: false,
display_env_section: true,
panic_section: None,
panic_message: Box::new(DefaultPanicMessage),
}
}

Expand All @@ -298,6 +303,63 @@ impl HookBuilder {
self
}

/// Overrides the main error message printing section at the start of panic
/// reports
///
/// # Examples
///
/// ```rust
/// use std::{panic::Location, fmt};
/// use color_eyre::section::PanicMessage;
/// use owo_colors::OwoColorize;
///
/// struct MyPanicMessage;
///
/// color_eyre::config::HookBuilder::default()
/// .panic_message(MyPanicMessage)
/// .install()
/// .unwrap();
///
/// impl PanicMessage for MyPanicMessage {
/// fn display(&self, pi: &std::panic::PanicInfo<'_>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// writeln!(f, "{}", "The application panicked (crashed).".red())?;
///
/// // Print panic message.
/// let payload = pi
/// .payload()
/// .downcast_ref::<String>()
/// .map(String::as_str)
/// .or_else(|| pi.payload().downcast_ref::<&str>().cloned())
/// .unwrap_or("<non string panic payload>");
///
/// write!(f, "Message: ")?;
/// writeln!(f, "{}", payload.cyan())?;
///
/// // If known, print panic location.
/// write!(f, "Location: ")?;
/// if let Some(loc) = pi.location() {
/// write!(f, "{}", loc.file().purple())?;
/// write!(f, ":")?;
/// write!(f, "{}", loc.line().purple())?;
///
/// write!(f, "\n\nConsider reporting the bug at {}", custom_url(loc, payload))?;
/// } else {
/// write!(f, "<unknown>")?;
/// }
///
/// Ok(())
/// }
/// }
///
/// fn custom_url(location: &Location<'_>, message: &str) -> impl fmt::Display {
/// "todo"
/// }
/// ```
pub fn panic_message<S: PanicMessage>(mut self, section: S) -> Self {
self.panic_message = Box::new(section);
self
}

/// Configures the default capture mode for `SpanTraces` in error reports and panics
pub fn capture_span_trace_by_default(mut self, cond: bool) -> Self {
self.capture_span_trace_by_default = cond;
Expand Down Expand Up @@ -367,6 +429,7 @@ impl HookBuilder {
#[cfg(feature = "capture-spantrace")]
capture_span_trace_by_default: self.capture_span_trace_by_default,
display_env_section: self.display_env_section,
panic_message: self.panic_message,
};

let eyre_hook = EyreHook {
Expand All @@ -379,6 +442,7 @@ impl HookBuilder {
}
}

#[allow(missing_docs)]
impl Default for HookBuilder {
fn default() -> Self {
Self::new()
Expand Down Expand Up @@ -433,12 +497,11 @@ fn install_panic_hook() {
std::panic::set_hook(Box::new(|pi| eprintln!("{}", PanicPrinter(pi))))
}

struct PanicMessage<'a>(&'a PanicPrinter<'a>);
struct DefaultPanicMessage;

impl fmt::Display for PanicMessage<'_> {
fn fmt(&self, out: &mut fmt::Formatter<'_>) -> fmt::Result {
let pi = (self.0).0;
writeln!(out, "{}", "The application panicked (crashed).".red())?;
impl PanicMessage for DefaultPanicMessage {
fn display(&self, pi: &std::panic::PanicInfo<'_>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "{}", "The application panicked (crashed).".red())?;

// Print panic message.
let payload = pi
Expand All @@ -448,40 +511,39 @@ impl fmt::Display for PanicMessage<'_> {
.or_else(|| pi.payload().downcast_ref::<&str>().cloned())
.unwrap_or("<non string panic payload>");

write!(out, "Message: ")?;
writeln!(out, "{}", payload.cyan())?;
write!(f, "Message: ")?;
writeln!(f, "{}", payload.cyan())?;

// If known, print panic location.
write!(out, "Location: ")?;
write!(f, "Location: ")?;
if let Some(loc) = pi.location() {
write!(out, "{}", loc.file().purple())?;
write!(out, ":")?;
write!(out, "{}", loc.line().purple())?;
write!(f, "{}", loc.file().purple())?;
write!(f, ":")?;
write!(f, "{}", loc.line().purple())?;
} else {
write!(out, "<unknown>")?;
write!(f, "<unknown>")?;
}

Ok(())
}
}

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

let v = panic_verbosity();

let printer = installed_printer();

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

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

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

Expand All @@ -500,15 +562,15 @@ fn print_panic_info(printer: &PanicPrinter<'_>, out: &mut fmt::Formatter<'_>) ->

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

if printer.display_env_section {
if hook.display_env_section {
let env_section = EnvSection {
bt_captured: &capture_bt,
#[cfg(feature = "capture-spantrace")]
Expand All @@ -524,6 +586,7 @@ fn print_panic_info(printer: &PanicPrinter<'_>, out: &mut fmt::Formatter<'_>) ->
pub(crate) struct PanicHook {
filters: Vec<Arc<FilterCallback>>,
section: Option<Box<dyn Display + Send + Sync + 'static>>,
panic_message: Box<dyn PanicMessage>,
#[cfg(feature = "capture-spantrace")]
capture_span_trace_by_default: bool,
display_env_section: bool,
Expand Down Expand Up @@ -671,7 +734,7 @@ impl fmt::Display for BacktraceFormatter<'_> {
}
}

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

Expand Down
4 changes: 2 additions & 2 deletions src/handler.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::config::installed_printer;
use crate::config::installed_hook;
use crate::{
section::help::HelpInfo,
writers::{EnvSection, WriterExt},
Expand Down Expand Up @@ -86,7 +86,7 @@ impl eyre::EyreHandler for Handler {
}

if let Some(backtrace) = self.backtrace.as_ref() {
let fmted_bt = installed_printer().format_backtrace(&backtrace);
let fmted_bt = installed_hook().format_backtrace(&backtrace);

write!(
indented(&mut separated.ready()).with_format(Format::Uniform { indentation: " " }),
Expand Down
6 changes: 6 additions & 0 deletions src/section/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,9 @@ pub trait Section: crate::private::Sealed {
D: Display + Send + Sync + 'static,
F: FnOnce() -> D;
}

/// Trait for printing a panic error message for the given PanicInfo
pub trait PanicMessage: Send + Sync + 'static {
/// Display trait equivalent for implementing the display logic
fn display(&self, pi: &std::panic::PanicInfo<'_>, f: &mut fmt::Formatter<'_>) -> fmt::Result;
}

0 comments on commit ff84554

Please sign in to comment.