1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
use lazy_static::lazy_static; use pagerduty_rs::eventsv2sync::*; use pagerduty_rs::types::*; use parking_lot::RwLock; use std::{fmt::Debug, sync::Arc}; pub mod prelude; lazy_static! { static ref PAGERDUTY_INTEGRATION_KEY: Arc<RwLock<Option<String>>> = Arc::new(RwLock::new(None)); } pub fn configure_pagerduty(integration_key: impl Into<String>) { PAGERDUTY_INTEGRATION_KEY .write() .replace(integration_key.into()); } pub trait AirbagResult: Sized { fn airbag_swallow(self) { drop(self.airbag()) } fn airbag(self) -> Self; } impl<T, E: Debug + 'static> AirbagResult for Result<T, E> { fn airbag(self) -> Self { if let Err(e) = &self { let integration_key = PAGERDUTY_INTEGRATION_KEY.read().clone(); if let Some(key) = integration_key { let e = generate_error_event(e); let _ = std::thread::spawn(move || { let ev2 = EventsV2::new(key, Some("airbag".to_owned())).unwrap(); if let Err(e) = ev2.event(e) { log::error!("Unable to send alert to PagerDuty: {:?}", e); } }) .join() .map_err(|_| log::error!("Thread panic detected")); } } self } } fn generate_error_event(e: &(impl Debug + 'static)) -> Event<String> { let e_any: &dyn std::any::Any = e; let e_dbg = format!("{:?}", e); let (message, description, dedup_key) = if let Some(e) = e_any.downcast_ref::<anyhow::Error>() { (e.to_string(), e_dbg.clone(), Some(sha256(&e_dbg))) } else { (e_dbg.clone(), e_dbg, None) }; Event::AlertTrigger(AlertTrigger { payload: AlertTriggerPayload { severity: Severity::Error, summary: message, source: description.clone(), timestamp: None, component: None, group: None, class: None, custom_details: Some(description), }, dedup_key, images: None, links: None, client: None, client_url: None, }) } fn sha256(s: &str) -> String { use sha2::{Digest, Sha256}; let mut hasher = Sha256::new(); hasher.update(s.as_bytes()); format!("{:x}", hasher.finalize()) }