11use std:: time:: Duration ;
22
3- use crate :: alerts:: model:: WebhookNotification ;
43use crate :: errors:: MapToSendError ;
54use crate :: probe:: model:: ProbeAlert ;
5+ use crate :: { alerts:: model:: WebhookNotification , probe:: model:: ProbeResponse } ;
66use chrono:: { DateTime , Utc } ;
77use lazy_static:: lazy_static;
88use tracing:: { info, warn} ;
99
10- use super :: model:: SlackNotification ;
10+ use super :: model:: { SlackBlock , SlackNotification , SlackTextBlock } ;
1111
1212const REQUEST_TIMEOUT_SECS : u64 = 10 ;
1313
@@ -21,6 +21,7 @@ lazy_static! {
2121pub async fn alert_if_failure (
2222 success : bool ,
2323 error : Option < & str > ,
24+ probe_response : Option < & ProbeResponse > ,
2425 probe_name : & str ,
2526 failure_timestamp : DateTime < Utc > ,
2627 alerts : & Option < Vec < ProbeAlert > > ,
@@ -30,16 +31,20 @@ pub async fn alert_if_failure(
3031 return Ok ( ( ) ) ;
3132 }
3233 let error_message = error. unwrap_or ( "No error message" ) ;
34+ let truncated_body = probe_response. map ( |r| r. truncated_body ( 500 ) ) ;
3335 warn ! (
34- "Probe {probe_name} failed at {failure_timestamp} with trace ID {}. Error: {error_message}" ,
35- trace_id. as_ref( ) . unwrap_or( & "N/A" . to_owned( ) )
36+ "Probe {probe_name} failed at {failure_timestamp} with trace ID {}. Status code: {}. Error: {error_message}. Body: {}" ,
37+ trace_id. as_ref( ) . unwrap_or( & "N/A" . to_owned( ) ) ,
38+ probe_response. map_or( "N/A" . to_owned( ) , |r| r. status_code. to_string( ) ) ,
39+ truncated_body. unwrap_or( "N/A" . to_owned( ) ) ,
3640 ) ;
3741 let mut errors = Vec :: new ( ) ;
3842 if let Some ( alerts_vec) = alerts {
3943 for alert in alerts_vec {
4044 if let Err ( e) = send_alert (
4145 alert,
4246 probe_name. to_owned ( ) ,
47+ probe_response,
4348 error_message,
4449 failure_timestamp,
4550 trace_id. clone ( ) ,
@@ -81,6 +86,7 @@ pub async fn send_generic_webhook(
8186pub async fn send_webhook_alert (
8287 url : & String ,
8388 probe_name : String ,
89+ probe_response : Option < & ProbeResponse > ,
8490 error_message : & str ,
8591 failure_timestamp : DateTime < Utc > ,
8692 trace_id : Option < String > ,
@@ -91,6 +97,8 @@ pub async fn send_webhook_alert(
9197 error_message : error_message. to_owned ( ) ,
9298 failure_timestamp,
9399 trace_id,
100+ body : probe_response. map ( |r| r. truncated_body ( 500 ) ) ,
101+ status_code : probe_response. map ( |r| r. status_code ) ,
94102 } ;
95103
96104 let json = serde_json:: to_string ( & request_body) . map_to_send_err ( ) ?;
@@ -100,26 +108,77 @@ pub async fn send_webhook_alert(
100108pub async fn send_slack_alert (
101109 webhook_url : & String ,
102110 probe_name : String ,
111+ probe_response : Option < & ProbeResponse > ,
103112 error_message : & str ,
104113 failure_timestamp : DateTime < Utc > ,
105114 trace_id : Option < String > ,
106115) -> Result < ( ) , Box < dyn std:: error:: Error + Send > > {
107- let request_body = SlackNotification {
108- text : format ! (
109- "Probe {} failed at {}. Trace ID: {}. Error: {}" ,
110- probe_name,
111- failure_timestamp,
112- trace_id. unwrap_or_else( || "N/A" . to_owned( ) ) ,
113- error_message,
114- ) ,
115- } ;
116+ // Uses Slack's Block Kit UI to make the message prettier
117+ let mut blocks = vec ! [
118+ SlackBlock {
119+ r#type: "header" . to_owned( ) ,
120+ text: Some ( SlackTextBlock {
121+ r#type: "plain_text" . to_owned( ) ,
122+ text: format!( "\" {}\" failed." , probe_name) ,
123+ } ) ,
124+ elements: None ,
125+ } ,
126+ SlackBlock {
127+ r#type: "section" . to_owned( ) ,
128+ text: Some ( SlackTextBlock {
129+ r#type: "mrkdwn" . to_owned( ) ,
130+ text: format!( "Error message:\n \n > {}" , error_message) ,
131+ } ) ,
132+ elements: None ,
133+ } ,
134+ ] ;
135+
136+ if let Some ( response) = probe_response {
137+ blocks. extend ( [
138+ SlackBlock {
139+ r#type : "divider" . to_owned ( ) ,
140+ elements : None ,
141+ text : None ,
142+ } ,
143+ SlackBlock {
144+ r#type : "section" . to_owned ( ) ,
145+ elements : None ,
146+ text : Some ( SlackTextBlock {
147+ r#type : "mrkdwn" . to_owned ( ) ,
148+ text : format ! (
149+ "Received status code *{}* and (truncated) body:\n ```\n {}\n ```" ,
150+ response. status_code,
151+ response. body. chars( ) . take( 500 ) . collect:: <String >( )
152+ ) ,
153+ } ) ,
154+ } ,
155+ ] )
156+ }
157+
158+ blocks. push ( SlackBlock {
159+ r#type : "context" . to_owned ( ) ,
160+ elements : Some ( vec ! [
161+ SlackTextBlock {
162+ r#type: "mrkdwn" . to_owned( ) ,
163+ text: format!( "Time: *{}*" , failure_timestamp) ,
164+ } ,
165+ SlackTextBlock {
166+ r#type: "mrkdwn" . to_owned( ) ,
167+ text: format!( "Trace ID: *{}*" , trace_id. unwrap_or( "N/A" . to_owned( ) ) ) ,
168+ } ,
169+ ] ) ,
170+ text : None ,
171+ } ) ;
172+ let request_body = SlackNotification { blocks } ;
116173 let json = serde_json:: to_string ( & request_body) . map_to_send_err ( ) ?;
174+ println ! ( "{}" , json) ;
117175 send_generic_webhook ( webhook_url, json) . await
118176}
119177
120178pub async fn send_alert (
121179 alert : & ProbeAlert ,
122180 probe_name : String ,
181+ probe_response : Option < & ProbeResponse > ,
123182 error_message : & str ,
124183 failure_timestamp : DateTime < Utc > ,
125184 trace_id : Option < String > ,
@@ -130,6 +189,7 @@ pub async fn send_alert(
130189 send_slack_alert (
131190 & alert. url ,
132191 probe_name. clone ( ) ,
192+ probe_response,
133193 error_message,
134194 failure_timestamp,
135195 trace_id. clone ( ) ,
@@ -140,6 +200,7 @@ pub async fn send_alert(
140200 send_webhook_alert (
141201 & alert. url ,
142202 probe_name. clone ( ) ,
203+ probe_response,
143204 error_message,
144205 failure_timestamp,
145206 trace_id. clone ( ) ,
@@ -181,6 +242,7 @@ mod webhook_tests {
181242 let alert_result = alert_if_failure (
182243 false ,
183244 Some ( "Test error" ) ,
245+ None ,
184246 & probe_name,
185247 failure_timestamp,
186248 & alerts,
0 commit comments