1 | use crate::{ |
2 | config::BacktraceFormatter, |
3 | section::help::HelpInfo, |
4 | writers::{EnvSection, WriterExt}, |
5 | Handler, |
6 | }; |
7 | use backtrace::Backtrace; |
8 | use indenter::{indented, Format}; |
9 | use std::fmt::Write; |
10 | #[cfg (feature = "capture-spantrace" )] |
11 | use tracing_error::{ExtractSpanTrace, SpanTrace}; |
12 | |
13 | impl std::fmt::Debug for Handler { |
14 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
15 | f.write_str(data:"redacted" ) |
16 | } |
17 | } |
18 | |
19 | impl Handler { |
20 | /// Return a reference to the captured `Backtrace` type |
21 | pub fn backtrace(&self) -> Option<&Backtrace> { |
22 | self.backtrace.as_ref() |
23 | } |
24 | |
25 | /// Return a reference to the captured `SpanTrace` type |
26 | #[cfg (feature = "capture-spantrace" )] |
27 | #[cfg_attr (docsrs, doc(cfg(feature = "capture-spantrace" )))] |
28 | pub fn span_trace(&self) -> Option<&SpanTrace> { |
29 | self.span_trace.as_ref() |
30 | } |
31 | |
32 | pub(crate) fn format_backtrace<'a>( |
33 | &'a self, |
34 | trace: &'a backtrace::Backtrace, |
35 | ) -> BacktraceFormatter<'a> { |
36 | BacktraceFormatter { |
37 | filters: &self.filters, |
38 | inner: trace, |
39 | theme: self.theme, |
40 | } |
41 | } |
42 | } |
43 | |
44 | impl eyre::EyreHandler for Handler { |
45 | fn debug( |
46 | &self, |
47 | error: &(dyn std::error::Error + 'static), |
48 | f: &mut core::fmt::Formatter<'_>, |
49 | ) -> core::fmt::Result { |
50 | if f.alternate() { |
51 | return core::fmt::Debug::fmt(error, f); |
52 | } |
53 | |
54 | #[cfg (feature = "capture-spantrace" )] |
55 | let errors = || { |
56 | eyre::Chain::new(error) |
57 | .filter(|e| e.span_trace().is_none()) |
58 | .enumerate() |
59 | }; |
60 | |
61 | #[cfg (not(feature = "capture-spantrace" ))] |
62 | let errors = || eyre::Chain::new(error).enumerate(); |
63 | |
64 | for (n, error) in errors() { |
65 | writeln!(f)?; |
66 | write!(indented(f).ind(n), " {}" , self.theme.error.style(error))?; |
67 | } |
68 | |
69 | let mut separated = f.header(" \n\n" ); |
70 | |
71 | #[cfg (feature = "track-caller" )] |
72 | if self.display_location_section { |
73 | write!( |
74 | separated.ready(), |
75 | " {}" , |
76 | crate::SectionExt::header( |
77 | crate::fmt::LocationSection(self.location, self.theme), |
78 | "Location:" |
79 | ) |
80 | )?; |
81 | } |
82 | |
83 | for section in self |
84 | .sections |
85 | .iter() |
86 | .filter(|s| matches!(s, HelpInfo::Error(_, _))) |
87 | { |
88 | write!(separated.ready(), " {}" , section)?; |
89 | } |
90 | |
91 | for section in self |
92 | .sections |
93 | .iter() |
94 | .filter(|s| matches!(s, HelpInfo::Custom(_))) |
95 | { |
96 | write!(separated.ready(), " {}" , section)?; |
97 | } |
98 | |
99 | #[cfg (feature = "capture-spantrace" )] |
100 | let span_trace = self |
101 | .span_trace |
102 | .as_ref() |
103 | .or_else(|| get_deepest_spantrace(error)); |
104 | |
105 | #[cfg (feature = "capture-spantrace" )] |
106 | { |
107 | if let Some(span_trace) = span_trace { |
108 | write!( |
109 | &mut separated.ready(), |
110 | " {}" , |
111 | crate::writers::FormattedSpanTrace(span_trace) |
112 | )?; |
113 | } |
114 | } |
115 | |
116 | if let Some(backtrace) = self.backtrace.as_ref() { |
117 | let fmted_bt = self.format_backtrace(backtrace); |
118 | |
119 | write!( |
120 | indented(&mut separated.ready()).with_format(Format::Uniform { indentation: " " }), |
121 | " {}" , |
122 | fmted_bt |
123 | )?; |
124 | } |
125 | |
126 | let f = separated.ready(); |
127 | let mut h = f.header(" \n" ); |
128 | let mut f = h.in_progress(); |
129 | |
130 | for section in self |
131 | .sections |
132 | .iter() |
133 | .filter(|s| !matches!(s, HelpInfo::Custom(_) | HelpInfo::Error(_, _))) |
134 | { |
135 | write!(&mut f, " {}" , section)?; |
136 | f = h.ready(); |
137 | } |
138 | |
139 | if self.display_env_section { |
140 | let env_section = EnvSection { |
141 | bt_captured: &self.backtrace.is_some(), |
142 | #[cfg (feature = "capture-spantrace" )] |
143 | span_trace, |
144 | }; |
145 | |
146 | write!(&mut separated.ready(), " {}" , env_section)?; |
147 | } |
148 | |
149 | #[cfg (feature = "issue-url" )] |
150 | if self.issue_url.is_some() && (*self.issue_filter)(crate::ErrorKind::Recoverable(error)) { |
151 | let url = self.issue_url.as_ref().unwrap(); |
152 | let mut payload = String::from("Error: " ); |
153 | for (n, error) in errors() { |
154 | writeln!(&mut payload)?; |
155 | write!(indented(&mut payload).ind(n), " {}" , error)?; |
156 | } |
157 | |
158 | let issue_section = crate::section::github::IssueSection::new(url, &payload) |
159 | .with_backtrace(self.backtrace.as_ref()) |
160 | .with_metadata(&**self.issue_metadata); |
161 | |
162 | #[cfg (feature = "capture-spantrace" )] |
163 | let issue_section = issue_section.with_span_trace(span_trace); |
164 | |
165 | write!(&mut separated.ready(), " {}" , issue_section)?; |
166 | } |
167 | |
168 | Ok(()) |
169 | } |
170 | |
171 | #[cfg (feature = "track-caller" )] |
172 | fn track_caller(&mut self, location: &'static std::panic::Location<'static>) { |
173 | self.location = Some(location); |
174 | } |
175 | } |
176 | |
177 | #[cfg (feature = "capture-spantrace" )] |
178 | pub(crate) fn get_deepest_spantrace<'a>( |
179 | error: &'a (dyn std::error::Error + 'static), |
180 | ) -> Option<&'a SpanTrace> { |
181 | eyreimpl Iterator ::Chain::new(head:error) |
182 | .rev() |
183 | .flat_map(|error: &dyn Error| error.span_trace()) |
184 | .next() |
185 | } |
186 | |