11
11
12
12
from apps .alerts import tasks
13
13
from apps .alerts .constants import ActionSource
14
+ from apps .alerts .incident_appearance .renderers .constants import DEFAULT_BACKUP_TITLE
14
15
from apps .alerts .utils import render_relative_timeline
15
16
from apps .slack .slack_formatter import SlackFormatter
16
17
from common .utils import clean_markup
17
18
18
19
if typing .TYPE_CHECKING :
19
20
from apps .alerts .models import AlertGroup , CustomButton , EscalationPolicy , Invitation
20
- from apps .user_management .models import User
21
+ from apps .user_management .models import Organization , User
21
22
22
23
logger = logging .getLogger (__name__ )
23
24
logger .setLevel (logging .DEBUG )
24
25
25
26
27
+ class RelatedIncidentData (typing .TypedDict ):
28
+ incident_link : typing .Optional [str ]
29
+ incident_title : str
30
+
31
+
26
32
class AlertGroupLogRecord (models .Model ):
27
33
alert_group : "AlertGroup"
28
34
author : typing .Optional ["User" ]
@@ -161,7 +167,9 @@ class AlertGroupLogRecord(models.Model):
161
167
ERROR_ESCALATION_TRIGGER_CUSTOM_WEBHOOK_ERROR ,
162
168
ERROR_ESCALATION_NOTIFY_TEAM_MEMBERS_STEP_IS_NOT_CONFIGURED ,
163
169
ERROR_ESCALATION_TRIGGER_WEBHOOK_IS_DISABLED ,
164
- ) = range (20 )
170
+ ERROR_ESCALATION_DECLARE_INCIDENT_STEP_IS_NOT_ENABLED ,
171
+ ERROR_ESCALATION_INCIDENT_COULD_NOT_BE_DECLARED ,
172
+ ) = range (22 )
165
173
166
174
type = models .IntegerField (choices = TYPE_CHOICES )
167
175
@@ -225,7 +233,14 @@ class AlertGroupLogRecord(models.Model):
225
233
escalation_policy_step = models .IntegerField (null = True , default = None )
226
234
step_specific_info = JSONField (null = True , default = None )
227
235
228
- STEP_SPECIFIC_INFO_KEYS = ["schedule_name" , "custom_button_name" , "usergroup_handle" , "source_integration_name" ]
236
+ STEP_SPECIFIC_INFO_KEYS = [
237
+ "schedule_name" ,
238
+ "custom_button_name" ,
239
+ "usergroup_handle" ,
240
+ "source_integration_name" ,
241
+ "incident_id" ,
242
+ "incident_title" ,
243
+ ]
229
244
230
245
def _make_log_line_link (self , url , title , html = False , for_slack = False , substitute_with_tag = False ):
231
246
if html and url :
@@ -244,6 +259,7 @@ def render_log_line_json(self):
244
259
author = self .author .short (organization ) if self .author is not None else None
245
260
escalation_chain = self .alert_group .channel_filter .escalation_chain if self .alert_group .channel_filter else None
246
261
step_info = self .get_step_specific_info ()
262
+ related_incident = self .render_incident_data_from_step_info (organization , step_info )
247
263
escalation_chain_data = (
248
264
{
249
265
"pk" : escalation_chain .public_primary_key ,
@@ -280,6 +296,7 @@ def render_log_line_json(self):
280
296
"type" : self .type ,
281
297
"created_at" : created_at ,
282
298
"author" : author ,
299
+ "incident" : related_incident ,
283
300
"escalation_chain" : escalation_chain_data ,
284
301
"schedule" : schedule ,
285
302
"webhook" : webhook ,
@@ -425,6 +442,14 @@ def rendered_log_line_action(self, for_slack=False, html=False, substitute_with_
425
442
result += f'triggered step "Notify on-call from Schedule { schedule_text } { important_text } "'
426
443
elif escalation_policy_step == EscalationPolicy .STEP_REPEAT_ESCALATION_N_TIMES :
427
444
result += "escalation started from the beginning"
445
+ elif escalation_policy_step == EscalationPolicy .STEP_DECLARE_INCIDENT :
446
+ organization = self .alert_group .channel .organization
447
+ incident_data = self .render_incident_data_from_step_info (organization , step_specific_info )
448
+ incident_link = incident_data ["incident_link" ]
449
+ incident_title = incident_data ["incident_title" ]
450
+ tag = "related_incident" if substitute_with_tag else False
451
+ incident_text = self ._make_log_line_link (incident_link , incident_title , html , for_slack , tag )
452
+ result += self .reason + f": { incident_text } "
428
453
else :
429
454
result += f'triggered step "{ EscalationPolicy .get_step_display_name (escalation_policy_step )} "'
430
455
elif self .type == AlertGroupLogRecord .TYPE_SILENCE :
@@ -640,8 +665,32 @@ def rendered_log_line_action(self, for_slack=False, html=False, substitute_with_
640
665
result += f"failed to notify User Group{ usergroup_handle_text } in Slack"
641
666
elif self .escalation_error_code == AlertGroupLogRecord .ERROR_ESCALATION_TRIGGER_WEBHOOK_IS_DISABLED :
642
667
result += 'skipped escalation step "Trigger Outgoing Webhook" because it is disabled'
668
+ elif (
669
+ self .escalation_error_code == AlertGroupLogRecord .ERROR_ESCALATION_DECLARE_INCIDENT_STEP_IS_NOT_ENABLED
670
+ ):
671
+ result += 'skipped escalation step "Declare Incident": step is not enabled'
672
+ elif self .escalation_error_code == AlertGroupLogRecord .ERROR_ESCALATION_INCIDENT_COULD_NOT_BE_DECLARED :
673
+ result += "failed to declare an Incident"
674
+ if self .reason :
675
+ result += f": { self .reason } "
643
676
return result
644
677
678
+ def render_incident_data_from_step_info (
679
+ self , organization : "Organization" , step_specific_info : dict
680
+ ) -> RelatedIncidentData | None :
681
+ from apps .alerts .models .related_incident import get_incident_url
682
+
683
+ if not step_specific_info or not all (key in step_specific_info for key in ["incident_title" , "incident_id" ]):
684
+ return None
685
+
686
+ incident_link = (
687
+ get_incident_url (organization , step_specific_info ["incident_id" ])
688
+ if step_specific_info ["incident_id" ]
689
+ else None
690
+ )
691
+ incident_title = step_specific_info ["incident_title" ] or DEFAULT_BACKUP_TITLE
692
+ return {"incident_link" : incident_link , "incident_title" : incident_title }
693
+
645
694
def get_step_specific_info (self ):
646
695
step_specific_info = None
647
696
# in some cases step_specific_info was saved with using json.dumps
0 commit comments