cyfs_debug/panic/
manager.rs1use super::panic::*;
2use crate::bug_report::*;
3use cyfs_base::*;
4use cyfs_util::*;
5
6use backtrace::Backtrace;
7use chrono::offset::Local;
8use chrono::DateTime;
9use std::panic;
10use std::path::PathBuf;
11use std::sync::Arc;
12
13pub type FnOnPanic = dyn EventListenerAsyncRoutine<CyfsPanicInfo, ()>;
15type OnPanicEventManager = SyncEventManagerSync<CyfsPanicInfo, ()>;
16
17pub trait BugReportHandler: Send + Sync {
18 fn notify(
19 &self,
20 product_name: &str,
21 service_name: &str,
22 panic_info: &CyfsPanicInfo,
23 ) -> BuckyResult<()>;
24}
25
26struct PanicManagerImpl {
27 product_name: String,
28 service_name: String,
29
30 log_to_file: bool,
31 log_dir: PathBuf,
32
33 exit_on_panic: bool,
34
35 on_panic: OnPanicEventManager,
36
37 reporter: Option<Box<dyn BugReportHandler>>,
39}
40
41impl PanicManagerImpl {
42 pub fn new(builder: PanicBuilder) -> Self {
43 let reporter = if !builder.disable_bug_report {
44 let mut reporter = BugReportManager::new(builder.bug_reporter);
45 if reporter.is_empty() {
46 reporter.load_from_config();
47 }
48
49 Some(Box::new(reporter) as Box<dyn BugReportHandler>)
50 } else {
51 None
52 };
53
54 Self {
55 product_name: builder.product_name,
56 service_name: builder.service_name,
57 log_to_file: builder.log_to_file,
58 log_dir: builder.log_dir,
59 exit_on_panic: builder.exit_on_panic,
60
61 on_panic: OnPanicEventManager::new(),
62 reporter,
63 }
64 }
65}
66
67#[derive(Clone)]
68pub struct PanicManager(Arc<PanicManagerImpl>);
69
70impl PanicManager {
71 pub fn new(builder: PanicBuilder) -> Self {
72 Self(Arc::new(PanicManagerImpl::new(builder)))
73 }
74
75 pub fn start(&self) {
76 let this = self.clone();
77 panic::set_hook(Box::new(move |info| {
78 let backtrace = Backtrace::new();
79 let pinfo = CyfsPanicInfo::new(backtrace, info);
80 let this = this.clone();
81
82 let _ = std::thread::spawn(move || {
83 this.on_panic(pinfo);
84 }).join();
85 }));
86 }
87
88 pub fn event(&self) -> OnPanicEventManager {
89 self.0.on_panic.clone()
90 }
91
92 fn on_panic(&self, info: CyfsPanicInfo) {
93 if self.0.log_to_file {
94 self.log_to_file(&info);
95 }
96
97 if let Some(reporter) = &self.0.reporter {
98 info!("will report panic......");
99 let _ = reporter.notify(&self.0.product_name, &self.0.service_name, &info);
100 }
101
102 let _ = self.0.on_panic.emit(&info);
104
105 if self.0.exit_on_panic {
106 crate::CyfsLogger::flush();
107
108 error!("process will exit on panic......");
109 std::thread::sleep(std::time::Duration::from_secs(3));
110 error!("process exit on panic......");
111 std::process::exit(-1);
112 }
113 }
114
115 fn log_to_file(&self, info: &CyfsPanicInfo) {
116 if let Err(e) = std::fs::create_dir_all(&self.0.log_dir) {
117 error!(
118 "create panic dir failed! dir={}, {}",
119 self.0.log_dir.display(),
120 e
121 );
122 return;
123 }
124
125 let file_name = format!("{}_panic_{}.log", self.0.service_name, info.hash);
126
127 let now = std::time::SystemTime::now();
128 let datetime: DateTime<Local> = now.into();
129 let now = datetime.format("%Y_%m_%d %H:%M:%S.%f");
130
131 let content = format!("{}\n{}\n{}", now, info.msg_with_symbol, info.msg);
132
133 let file_path = self.0.log_dir.join(file_name);
134 if let Err(e) = std::fs::write(&file_path, content) {
135 error!("write panic log failed! dir={}, {}", file_path.display(), e);
136 }
137 }
138}
139
140pub struct PanicBuilder {
141 product_name: String,
142 service_name: String,
143
144 log_to_file: bool,
145 log_dir: PathBuf,
146
147 disable_bug_report: bool,
148 bug_reporter: Vec<Box<dyn BugReportHandler>>,
149
150 exit_on_panic: bool,
152}
153
154impl PanicBuilder {
155 pub fn new(product_name: &str, service_name: &str) -> Self {
156 assert!(!product_name.is_empty());
157 assert!(!service_name.is_empty());
158
159 let mut root = get_cyfs_root_path();
160 root.push("log/panic");
161 root.push(product_name);
162
163 Self {
164 product_name: product_name.to_owned(),
165 service_name: service_name.to_owned(),
166 log_to_file: true,
167 log_dir: root,
168 disable_bug_report: false,
169 bug_reporter: vec![],
170 exit_on_panic: false,
171 }
172 }
173
174 pub fn log_to_file(mut self, enable: bool) -> Self {
176 self.log_to_file = enable;
177 self
178 }
179
180 pub fn log_dir(mut self, log_dir: impl Into<PathBuf>) -> Self {
182 self.log_dir = log_dir.into();
183 self
184 }
185
186 pub fn bug_report(mut self, handler: Box<dyn BugReportHandler>) -> Self {
187 self.bug_reporter.push(handler);
188 self
189 }
190
191 pub fn http_bug_report(mut self, url: &str) -> Self {
193 let handler = HttpBugReporter::new(url);
194 self.bug_reporter.push(Box::new(handler));
195 self
196 }
197
198 pub fn dingtalk_bug_report(mut self, dingtalk_url: &str) -> Self {
200 let handler = DingtalkNotifier::new(dingtalk_url);
201 self.bug_reporter.push(Box::new(handler));
202 self
203 }
204
205 pub fn disable_bug_report(mut self) -> Self {
206 self.disable_bug_report = true;
207 self
208 }
209
210 pub fn exit_on_panic(mut self, exit: bool) -> Self {
212 self.exit_on_panic = exit;
213 self
214 }
215
216 pub fn build(self) -> PanicManager {
217 PanicManager::new(self)
218 }
219}