def build_incident_attachment(incident, integration_key, metric_value=None): data = incident_attachment_info(incident, metric_value) if incident.status == IncidentStatus.CRITICAL.value: severity = "critical" elif incident.status == IncidentStatus.WARNING.value: severity = "warning" elif incident.status == IncidentStatus.CLOSED.value: severity = "info" event_action = "resolve" if incident.status in [IncidentStatus.WARNING.value, IncidentStatus.CRITICAL.value]: event_action = "trigger" return { "routing_key": integration_key, "event_action": event_action, "dedup_key": f"incident_{incident.organization_id}_{incident.identifier}", "payload": { "summary": incident.alert_rule.name, "severity": severity, "source": str(incident.identifier), "custom_details": {"details": data["text"]}, }, "links": [{"href": data["title_link"], "text": data["title"]}], }
def build_incident_attachment(incident, metric_value=None): """ Builds an incident attachment for slack unfurling :param incident: The `Incident` to build the attachment for :param metric_value: The value of the metric that triggered this alert to fire. If not provided we'll attempt to calculate this ourselves. :return: """ data = incident_attachment_info(incident, metric_value) colors = { "Resolved": RESOLVED_COLOR, "Warning": LEVEL_TO_COLOR["warning"], "Critical": LEVEL_TO_COLOR["fatal"], } return { "fallback": data["title"], "title": data["title"], "title_link": data["title_link"], "text": data["text"], "fields": [], "mrkdwn_in": ["text"], "footer_icon": data["logo_url"], "footer": "Sentry Incident", "ts": to_timestamp(data["ts"]), "color": colors[data["status"]], "actions": [], }
def test_with_incident_where_no_sessions_exist(self): alert_rule = self.create_alert_rule( query="", aggregate= "percentage(sessions_crashed, sessions) AS _crash_rate_alert_aggregate", dataset=QueryDatasets.SESSIONS, time_window=60, ) trigger = self.create_alert_rule_trigger(alert_rule, CRITICAL_TRIGGER_LABEL, 95) incident = self.create_incident( self.organization, title="Incident #2", projects=[self.project], alert_rule=alert_rule, status=IncidentStatus.CLOSED.value, date_started=self.now, ) action = self.create_alert_rule_trigger_action( alert_rule_trigger=trigger, triggered_for_incident=incident) data = incident_attachment_info(incident, None, action, method="fire") assert data["title"] == f"Critical: {alert_rule.name}" assert data["status"] == "Critical" assert data[ "text"] == "No sessions crash free rate in the last 60 minutes"
def test_returns_correct_info(self): alert_rule = self.create_alert_rule() date_started = self.now incident = self.create_incident( self.organization, title="Incident #1", projects=[self.project], alert_rule=alert_rule, status=IncidentStatus.CLOSED.value, date_started=date_started, ) trigger = self.create_alert_rule_trigger(alert_rule, CRITICAL_TRIGGER_LABEL, 100) action = self.create_alert_rule_trigger_action( alert_rule_trigger=trigger, triggered_for_incident=incident) metric_value = 123 data = incident_attachment_info(incident, metric_value, action) assert data["title"] == f"Resolved: {alert_rule.name}" assert data["status"] == "Resolved" assert data[ "text"] == "123 events in the last 10 minutes\nFilter: level:error" assert data["ts"] == date_started assert data[ "title_link"] == "http://testserver/organizations/baz/alerts/1/" assert ( data["logo_url"] == "http://testserver/_static/{version}/sentry/images/sentry-email-avatar.png" )
def build_incident_attachment(action, incident, metric_value=None, method=None): from sentry.api.serializers.rest_framework.base import ( camel_to_snake_case, convert_dict_key_case, ) data = incident_attachment_info(incident, metric_value, action=action, method=method) return { "metric_alert": convert_dict_key_case( serialize(incident, serializer=IncidentSerializer()), camel_to_snake_case), "description_text": data["text"], "description_title": data["title"], "web_url": data["title_link"], }
def build_incident_attachment(incident, integration_key, metric_value=None): data = incident_attachment_info(incident, metric_value) if incident.status == IncidentStatus.CRITICAL.value: severity = "critical" elif incident.status == IncidentStatus.WARNING.value: severity = "warning" elif incident.status == IncidentStatus.CLOSED.value: severity = "info" event_action = "resolve" if incident.status in [ IncidentStatus.WARNING.value, IncidentStatus.CRITICAL.value ]: event_action = "trigger" footer_text = "Sentry Incident | {}".format(data["ts"].strftime("%b %d")) return { "routing_key": integration_key, "event_action": event_action, "dedup_key": "incident_{}_{}".format(incident.organization_id, incident.identifier), "payload": { "summary": data["text"], "severity": severity, "source": incident.identifier, "custom_details": footer_text, }, "links": [{ "href": data["title_link"], "text": data["title"] }], }
def test_with_incident_trigger_users_resolve(self): self.create_incident_and_related_objects(field="users") data = incident_attachment_info(self.incident, IncidentStatus.CLOSED) assert data["title"] == f"Resolved: {self.alert_rule.name}" assert data["status"] == "Resolved" assert data["text"] == "100.0% users crash free rate in the last 60 minutes" assert data["ts"] == self.date_started assert ( data["logo_url"] == "http://testserver/_static/{version}/sentry/images/sentry-email-avatar.png" )
def test_with_incident_trigger_sessions(self): self.create_incident_and_related_objects() data = incident_attachment_info(self.incident, IncidentStatus.CRITICAL, 92) assert data["title"] == f"Critical: {self.alert_rule.name}" assert data["status"] == "Critical" assert data["text"] == "92% sessions crash free rate in the last 60 minutes" assert data["ts"] == self.date_started assert ( data["logo_url"] == "http://testserver/_static/{version}/sentry/images/sentry-email-avatar.png" )
def build(self) -> SlackBody: data = incident_attachment_info(self.incident, self.new_status, self.metric_value) return self._build( actions=[], color=INCIDENT_COLOR_MAPPING.get(data["status"]), fallback=data["title"], fields=[], footer=get_footer(data["ts"]), text=data["text"], title=data["title"], title_link=data["title_link"], )
def test_with_incident_trigger_users(self): self.create_incident_and_related_objects(field="users") data = incident_attachment_info(self.incident, 92, self.action, method="fire") assert data["title"] == f"Critical: {self.alert_rule.name}" assert data["status"] == "Critical" assert data[ "text"] == "92% users crash free rate in the last 60 minutes" assert data["ts"] == self.date_started assert ( data["logo_url"] == "http://testserver/_static/{version}/sentry/images/sentry-email-avatar.png" )
def build_incident_attachment( incident: Incident, new_status: IncidentStatus, metric_value: str | None = None, ) -> Mapping[str, str]: from sentry.api.serializers.rest_framework.base import ( camel_to_snake_case, convert_dict_key_case, ) data = incident_attachment_info(incident, new_status, metric_value) return { "metric_alert": convert_dict_key_case( serialize(incident, serializer=IncidentSerializer()), camel_to_snake_case ), "description_text": data["text"], "description_title": data["title"], "web_url": data["title_link"], }
def build_incident_attachment(action, incident, metric_value=None, method=None): """ Builds an incident attachment for slack unfurling :param incident: The `Incident` to build the attachment for :param metric_value: The value of the metric that triggered this alert to fire. If not provided we'll attempt to calculate this ourselves. :return: """ data = incident_attachment_info(incident, metric_value, action=action, method=method) colors = { "Resolved": RESOLVED_COLOR, "Warning": LEVEL_TO_COLOR["warning"], "Critical": LEVEL_TO_COLOR["fatal"], } incident_footer_ts = ( "<!date^{:.0f}^Sentry Incident - Started {} at {} | Sentry Incident>". format(to_timestamp(data["ts"]), "{date_pretty}", "{time}")) return { "fallback": data["title"], "title": data["title"], "title_link": data["title_link"], "text": data["text"], "fields": [], "mrkdwn_in": ["text"], "footer_icon": data["logo_url"], "footer": incident_footer_ts, "color": colors[data["status"]], "actions": [], }
def test_with_incident_trigger(self): alert_rule = self.create_alert_rule() now = self.now date_started = now - timedelta(minutes=5) event_date = now - timedelta(minutes=5) self.create_event(event_date) self.create_event(event_date) self.create_event(event_date) self.create_event(event_date) incident = self.create_incident( self.organization, title="Incident #2", projects=[self.project], alert_rule=alert_rule, status=IncidentStatus.CLOSED.value, date_started=date_started, query="", ) self.create_alert_rule_trigger_action(triggered_for_incident=incident) incident_trigger = ( IncidentTrigger.objects.filter(incident=incident).order_by("-date_modified").first() ) incident_trigger.update(date_modified=now) data = incident_attachment_info(incident) assert data["title"] == f"Resolved: {alert_rule.name}" assert data["status"] == "Resolved" assert data["text"] == "4 events in the last 10 minutes\nFilter: level:error" assert data["ts"] == date_started assert data["title_link"] == "http://testserver/organizations/baz/alerts/1/" assert ( data["logo_url"] == "http://testserver/_static/{version}/sentry/images/sentry-email-avatar.png" )
def test_with_incident_trigger_crash_rate_alerts(self): for idx, field in enumerate(["sessions", "users"]): alert_rule = self.create_alert_rule( query="", aggregate= f"percentage({field}_crashed, {field}) AS _crash_rate_alert_aggregate", ) date_started = self.now incident = self.create_incident( self.organization, title="Incident #1", projects=[self.project], alert_rule=alert_rule, status=IncidentStatus.CLOSED.value, date_started=date_started, ) trigger = self.create_alert_rule_trigger(alert_rule, CRITICAL_TRIGGER_LABEL, 95) action = self.create_alert_rule_trigger_action( alert_rule_trigger=trigger, triggered_for_incident=incident) metric_value = 92 data = incident_attachment_info(incident, metric_value, action, method="fire") assert data["title"] == f"Critical: {alert_rule.name}" assert data["status"] == "Critical" assert data[ "text"] == f"92% {field} crash free rate in the last 10 minutes" assert data["ts"] == date_started assert data[ "title_link"] == f"http://testserver/organizations/baz/alerts/{idx + 1}/" assert ( data["logo_url"] == "http://testserver/_static/{version}/sentry/images/sentry-email-avatar.png" )
def test_with_incident_trigger(self): alert_rule = self.create_alert_rule() now = self.now date_started = now - timedelta(minutes=5) event_date = now - timedelta(minutes=5) self.create_event(event_date) self.create_event(event_date) self.create_event(event_date) self.create_event(event_date) incident = self.create_incident( self.organization, title="Incident #2", projects=[self.project], alert_rule=alert_rule, status=IncidentStatus.CLOSED.value, date_started=date_started, query="", ) trigger = self.create_alert_rule_trigger(alert_rule, CRITICAL_TRIGGER_LABEL, 100) action = self.create_alert_rule_trigger_action( alert_rule_trigger=trigger, triggered_for_incident=incident) incident_trigger = (IncidentTrigger.objects.filter( incident=incident).order_by("-date_modified").first()) incident_trigger.update(date_modified=now) # Test the trigger "firing" data = incident_attachment_info(incident, action=action, method="fire") assert data["title"] == "Critical: {}".format( alert_rule.name) # Pulls from trigger, not incident assert data[ "status"] == "Critical" # Should pull from the action/trigger. assert data[ "text"] == "4 events in the last 10 minutes\nFilter: level:error" assert data["ts"] == date_started assert data[ "title_link"] == "http://testserver/organizations/baz/alerts/1/" assert ( data["logo_url"] == "http://testserver/_static/{version}/sentry/images/sentry-email-avatar.png" ) # Test the trigger "resolving" data = incident_attachment_info(incident, action=action, method="resolve") assert data["title"] == f"Resolved: {alert_rule.name}" assert data["status"] == "Resolved" assert data[ "text"] == "4 events in the last 10 minutes\nFilter: level:error" assert data["ts"] == date_started assert data[ "title_link"] == "http://testserver/organizations/baz/alerts/1/" assert ( data["logo_url"] == "http://testserver/_static/{version}/sentry/images/sentry-email-avatar.png" ) # No trigger passed, uses incident as fallback data = incident_attachment_info(incident, action=action) assert data["title"] == f"Resolved: {alert_rule.name}" assert data["status"] == "Resolved" assert data[ "text"] == "4 events in the last 10 minutes\nFilter: level:error" assert data["ts"] == date_started assert data[ "title_link"] == "http://testserver/organizations/baz/alerts/1/" assert ( data["logo_url"] == "http://testserver/_static/{version}/sentry/images/sentry-email-avatar.png" )
def build_incident_attachment(incident, metric_value=None): data = incident_attachment_info(incident, metric_value) colors = { "Resolved": "good", "Warning": "warning", "Critical": "attention" } footer_text = "Sentry Incident | {}".format(data["ts"].strftime("%b %d")) return { "type": "AdaptiveCard", "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "version": "1.2", "body": [{ "type": "ColumnSet", "columns": [ { "type": "Column", "style": colors[data["status"]], "items": [], "width": "20px", }, { "type": "Column", "items": [{ "type": "Container", "items": [ { "type": "TextBlock", "text": "[{}]({})".format(data["title"], data["title_link"]), "fontType": "Default", "weight": "Bolder", }, { "type": "TextBlock", "text": data["text"], "isSubtle": True }, { "type": "ColumnSet", "columns": [ { "type": "Column", "items": [{ "type": "Image", "url": data["logo_url"], "size": "Small", "width": "20px", }], "width": "auto", }, { "type": "Column", "items": [{ "type": "TextBlock", "spacing": "None", "text": footer_text, "isSubtle": True, "wrap": True, "height": "stretch", }], "width": "stretch", }, ], }, ], }], "width": "stretch", }, ], }], }