cat_gateway/service/utilities/
catch_panic.rsuse std::{any::Any, backtrace::Backtrace, cell::RefCell};
use chrono::prelude::*;
use panic_message::panic_message;
use poem::{http::StatusCode, middleware::PanicHandler, IntoResponse};
use poem_openapi::payload::Json;
use serde_json::json;
use crate::service::common::responses::code_500_internal_server_error::InternalServerError;
#[derive(Clone)]
pub(crate) struct ServicePanicHandler;
thread_local! {
static BACKTRACE: RefCell<Option<String>> = const { RefCell::new(None) };
static LOCATION: RefCell<Option<String>> = const { RefCell::new(None) };
}
pub(crate) fn set_panic_hook() {
std::panic::set_hook(Box::new(|panic_info| {
let raw_trace = Backtrace::force_capture();
let trace = format!("{raw_trace}");
BACKTRACE.with(move |b| b.borrow_mut().replace(trace));
let location = match panic_info.location() {
Some(location) => format!("{location}"),
None => "Unknown".to_string(),
};
LOCATION.with(move |l| l.borrow_mut().replace(location));
}));
}
impl PanicHandler for ServicePanicHandler {
type Response = poem::Response;
fn get_response(&self, err: Box<dyn Any + Send + 'static>) -> Self::Response {
let server_err = InternalServerError::new(None);
let panic_identifier = server_err.id().to_string();
let err_msg = panic_message(&err);
let location = match LOCATION.with(|l| l.borrow_mut().take()) {
Some(location) => location,
None => "Unknown".to_string(),
};
let backtrace = match BACKTRACE.with(|b| b.borrow_mut().take()) {
Some(backtrace) => backtrace,
None => "Unknown".to_string(),
};
let time = chrono::Utc::now().to_rfc3339_opts(SecondsFormat::Nanos, true);
let json_log = json!({
"backtrace": backtrace,
"location": location,
"message": err_msg,
"id": panic_identifier,
"level": "PANIC",
"timestamp": time
})
.to_string();
println!("{json_log}");
let mut resp = Json(server_err).into_response();
resp.set_status(StatusCode::INTERNAL_SERVER_ERROR);
resp
}
}