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;
9use tracing::debug;
10
11use crate::{
12 service::{
13 common::responses::code_500_internal_server_error::InternalServerError,
14 utilities::health::{get_live_counter, inc_live_counter, set_not_live},
15 },
16 settings::Settings,
17};
18
19#[derive(Clone)]
25pub(crate) struct ServicePanicHandler;
26
27#[poem::handler]
30#[allow(clippy::panic)]
31pub(crate) fn panic_endpoint() {
32 panic!("Intentional panicking")
33}
34
35thread_local! {
38 static BACKTRACE: RefCell<Option<String>> = const { RefCell::new(None) };
39 static LOCATION: RefCell<Option<String>> = const { RefCell::new(None) };
40}
41
42pub(crate) fn set_panic_hook() {
46 std::panic::set_hook(Box::new(|panic_info| {
47 let raw_trace = Backtrace::force_capture();
49 let trace = format!("{raw_trace}");
50 BACKTRACE.with(move |b| b.borrow_mut().replace(trace));
51
52 let location = match panic_info.location() {
54 Some(location) => format!("{location}"),
55 None => "Unknown".to_string(),
56 };
57 LOCATION.with(move |l| l.borrow_mut().replace(location));
58 }));
59}
60
61impl PanicHandler for ServicePanicHandler {
62 type Response = poem::Response;
63
64 fn get_response(
67 &self,
68 err: Box<dyn Any + Send + 'static>,
69 ) -> Self::Response {
70 inc_live_counter();
72
73 let current_count = get_live_counter();
74 debug!(
75 live_counter = current_count,
76 "Handling service panic response"
77 );
78
79 if current_count > Settings::service_live_counter_threshold() {
81 set_not_live();
82 }
83
84 let server_err = InternalServerError::new(None);
85
86 let panic_identifier = server_err.id().to_string();
88
89 let err_msg = panic_message(&err);
91
92 let location = match LOCATION.with(|l| l.borrow_mut().take()) {
94 Some(location) => location,
95 None => "Unknown".to_string(),
96 };
97
98 let backtrace = match BACKTRACE.with(|b| b.borrow_mut().take()) {
100 Some(backtrace) => backtrace,
101 None => "Unknown".to_string(),
102 };
103
104 let time = chrono::Utc::now().to_rfc3339_opts(SecondsFormat::Nanos, true);
107
108 let json_log = json!({
109 "backtrace": backtrace,
110 "location": location,
111 "message": err_msg,
112 "id": panic_identifier,
113 "level": "PANIC",
114 "timestamp": time
115 })
116 .to_string();
117
118 println!("{json_log}");
119
120 let mut resp = Json(server_err).into_response();
121 resp.set_status(StatusCode::INTERNAL_SERVER_ERROR);
122 resp
123 }
124}