log_panics/lib.rs
1//! A crate which logs panics instead of writing to standard error.
2//!
3//! The format used is identical to the standard library's.
4//!
5//! Because logging with a backtrace requires additional dependencies,
6//! the `with-backtrace` feature must be enabled. You can add the
7//! following in your `Cargo.toml`:
8//!
9//! ```toml
10//! log-panics = { version = "2", features = ["with-backtrace"]}
11//! ```
12//!
13//! To use, call [`log_panics::init()`](init) somewhere early in execution,
14//! such as immediately after initializing `log`, or use the [`Config`]
15//! builder for more customization.
16
17#![doc(html_root_url = "https://docs.rs/log-panics/2.0.0")]
18#![warn(missing_docs)]
19
20// Enable feature requirements on docs.rs.
21#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
22
23#[macro_use]
24extern crate log;
25
26#[cfg(feature = "with-backtrace")]
27extern crate backtrace;
28
29use std::{fmt, panic, thread};
30
31use backtrace::Backtrace;
32
33#[cfg(not(feature = "with-backtrace"))]
34mod backtrace {
35 #[derive(Default)]
36 pub struct Backtrace;
37}
38
39struct Shim(Backtrace);
40
41impl fmt::Debug for Shim {
42 #[cfg(feature = "with-backtrace")]
43 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
44 if !self.0.frames().is_empty() {
45 write!(fmt, "\n{:?}", self.0)
46 } else {
47 Ok(())
48 }
49 }
50
51 #[inline]
52 #[cfg(not(feature = "with-backtrace"))]
53 fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
54 Ok(())
55 }
56}
57
58/// Determines how backtraces will be displayed.
59#[cfg(feature = "with-backtrace")]
60#[derive(Debug, PartialEq, Eq, Clone, Copy)]
61pub enum BacktraceMode {
62 /// Backtraces will be omitted from the log.
63 Off,
64 /// Backtraces will include addresses, but no symbol names or locations.
65 Unresolved,
66 /// Backtraces will include addresses as well as symbol names and locations when possible.
67 Resolved
68}
69
70/// Configures the panic hook, ending with initialization.
71///
72/// ## Example
73///
74/// ```
75/// # #[cfg(feature = "with-backtrace")]
76/// log_panics::Config::new()
77/// .backtrace_mode(log_panics::BacktraceMode::Unresolved)
78/// .install_panic_hook()
79/// ```
80#[derive(Debug)]
81pub struct Config {
82 // We store a constructor function instead of a BacktraceMode enum
83 // so that inlining can eliminate references to `Backtrace::default`
84 // if symbolication is not desired.
85 make_backtrace: fn() -> Backtrace,
86}
87
88impl Config {
89 /// Initializes the builder with the default set of features.
90 pub fn new() -> Self {
91 Self {
92 make_backtrace: Backtrace::default,
93 }
94 }
95
96 /// Controls how backtraces are displayed.
97 ///
98 /// The default when backtraces are enabled is [`BacktraceMode::Resolved`].
99 #[cfg(feature = "with-backtrace")]
100 pub fn backtrace_mode(mut self, mode: BacktraceMode) -> Self {
101 self.make_backtrace = match mode {
102 BacktraceMode::Off => || Backtrace::from(vec![]),
103 BacktraceMode::Unresolved => Backtrace::new_unresolved,
104 BacktraceMode::Resolved => Backtrace::default,
105 };
106 self
107 }
108
109 /// Initializes the panic hook.
110 ///
111 /// After this method is called, all panics will be logged rather than printed
112 /// to standard error.
113 pub fn install_panic_hook(self) {
114 panic::set_hook(Box::new(move |info| {
115 let backtrace = (self.make_backtrace)();
116
117 let thread = thread::current();
118 let thread = thread.name().unwrap_or("<unnamed>");
119
120 let msg = match info.payload().downcast_ref::<&'static str>() {
121 Some(s) => *s,
122 None => match info.payload().downcast_ref::<String>() {
123 Some(s) => &**s,
124 None => "Box<Any>",
125 },
126 };
127
128 match info.location() {
129 Some(location) => {
130 error!(
131 target: "panic", "thread '{}' panicked at '{}': {}:{}{:?}",
132 thread,
133 msg,
134 location.file(),
135 location.line(),
136 Shim(backtrace)
137 );
138 }
139 None => error!(
140 target: "panic",
141 "thread '{}' panicked at '{}'{:?}",
142 thread,
143 msg,
144 Shim(backtrace)
145 ),
146 }
147 }));
148 }
149}
150
151impl Default for Config {
152 fn default() -> Self {
153 Self::new()
154 }
155}
156
157/// Initializes the panic hook with the default settings.
158///
159/// After this method is called, all panics will be logged rather than printed
160/// to standard error.
161///
162/// See [`Config`] for more information.
163pub fn init() {
164 Config::new().install_panic_hook()
165}