cat_gateway/service/utilities/
catch_panic.rs1use std::{any::Any, backtrace::Backtrace, cell::RefCell};
3
4use chrono::prelude::*;
5use panic_message::panic_message;
6use poem::{http::StatusCode, middleware::PanicHandler, IntoResponse};
7use poem_openapi::payload::Json;
8use serde_json::json;
9
10use crate::{
11 service::{
12 common::responses::code_500_internal_server_error::InternalServerError,
13 utilities::health::{get_live_counter, inc_live_counter, set_not_live},
14 },
15 settings::Settings,
16};
17
18#[derive(Clone)]
24pub(crate) struct ServicePanicHandler;
25
26thread_local! {
29 static BACKTRACE: RefCell<Option<String>> = const { RefCell::new(None) };
30 static LOCATION: RefCell<Option<String>> = const { RefCell::new(None) };
31}
32
33pub(crate) fn set_panic_hook() {
37 std::panic::set_hook(Box::new(|panic_info| {
38 let raw_trace = Backtrace::force_capture();
40 let trace = format!("{raw_trace}");
41 BACKTRACE.with(move |b| b.borrow_mut().replace(trace));
42
43 let location = match panic_info.location() {
45 Some(location) => format!("{location}"),
46 None => "Unknown".to_string(),
47 };
48 LOCATION.with(move |l| l.borrow_mut().replace(location));
49 }));
50}
51
52impl PanicHandler for ServicePanicHandler {
53 type Response = poem::Response;
54
55 fn get_response(&self, err: Box<dyn Any + Send + 'static>) -> Self::Response {
58 inc_live_counter();
60
61 if get_live_counter() > Settings::service_live_counter_threshold() {
63 set_not_live();
64 }
65
66 let server_err = InternalServerError::new(None);
67
68 let panic_identifier = server_err.id().to_string();
70
71 let err_msg = panic_message(&err);
73
74 let location = match LOCATION.with(|l| l.borrow_mut().take()) {
76 Some(location) => location,
77 None => "Unknown".to_string(),
78 };
79
80 let backtrace = match BACKTRACE.with(|b| b.borrow_mut().take()) {
82 Some(backtrace) => backtrace,
83 None => "Unknown".to_string(),
84 };
85
86 let time = chrono::Utc::now().to_rfc3339_opts(SecondsFormat::Nanos, true);
89
90 let json_log = json!({
91 "backtrace": backtrace,
92 "location": location,
93 "message": err_msg,
94 "id": panic_identifier,
95 "level": "PANIC",
96 "timestamp": time
97 })
98 .to_string();
99
100 println!("{json_log}");
101
102 let mut resp = Json(server_err).into_response();
103 resp.set_status(StatusCode::INTERNAL_SERVER_ERROR);
104 resp
105 }
106}