Skip to content

Commit 7736c94

Browse files
chore(sentry apps): add SLO context manager for send alert event (issue alerts) (#86356)
1 parent e9404c9 commit 7736c94

File tree

3 files changed

+217
-71
lines changed

3 files changed

+217
-71
lines changed

src/sentry/sentry_apps/metrics.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class SentryAppWebhookFailureReason(StrEnum):
4949
INVALID_EVENT = "invalid_event"
5050
MISSING_SERVICEHOOK = "missing_servicehook"
5151
EVENT_NOT_IN_SERVCEHOOK = "event_not_in_servicehook"
52+
MISSING_ISSUE_OCCURRENCE = "missing_issue_occurrence"
5253
MISSING_USER = "missing_user"
5354

5455

src/sentry/sentry_apps/tasks/sentry_apps.py

Lines changed: 62 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def _webhook_event_data(
119119
@instrumented_task(name="sentry.sentry_apps.tasks.sentry_apps.send_alert_webhook", **TASK_OPTIONS)
120120
@retry_decorator
121121
def send_alert_webhook(
122-
rule: str,
122+
rule_label: str,
123123
sentry_app_id: int,
124124
instance_id: str,
125125
group_id: int,
@@ -128,79 +128,72 @@ def send_alert_webhook(
128128
additional_payload: Mapping[str, Any] | None = None,
129129
**kwargs: Any,
130130
):
131-
group = Group.objects.get_from_cache(id=group_id)
132-
assert group, "Group must exist to get related attributes"
133-
project = Project.objects.get_from_cache(id=group.project_id)
134-
organization = Organization.objects.get_from_cache(id=project.organization_id)
135-
extra = {
136-
"sentry_app_id": sentry_app_id,
137-
"project_slug": project.slug,
138-
"organization_slug": organization.slug,
139-
"rule": rule,
140-
}
131+
with SentryAppInteractionEvent(
132+
operation_type=SentryAppInteractionType.PREPARE_WEBHOOK,
133+
event_type=SentryAppEventType.EVENT_ALERT_TRIGGERED,
134+
).capture() as lifecycle:
135+
group = Group.objects.get_from_cache(id=group_id)
136+
assert group, "Group must exist to get related attributes"
137+
project = Project.objects.get_from_cache(id=group.project_id)
138+
organization = Organization.objects.get_from_cache(id=project.organization_id)
139+
extra: dict[str, int | str] = {
140+
"sentry_app_id": sentry_app_id,
141+
"project_id": project.id,
142+
"organization_slug": organization.slug,
143+
"rule": rule_label,
144+
}
145+
lifecycle.add_extras(extra)
141146

142-
sentry_app = app_service.get_sentry_app_by_id(id=sentry_app_id)
143-
if sentry_app is None:
144-
logger.info("event_alert_webhook.missing_sentry_app", extra=extra)
145-
return
147+
sentry_app = app_service.get_sentry_app_by_id(id=sentry_app_id)
148+
if sentry_app is None:
149+
raise SentryAppSentryError(message=SentryAppWebhookFailureReason.MISSING_SENTRY_APP)
146150

147-
installations = app_service.get_many(
148-
filter=dict(
149-
organization_id=organization.id,
150-
app_ids=[sentry_app.id],
151-
status=SentryAppInstallationStatus.INSTALLED,
151+
installations = app_service.get_many(
152+
filter=dict(
153+
organization_id=organization.id,
154+
app_ids=[sentry_app.id],
155+
status=SentryAppInstallationStatus.INSTALLED,
156+
)
152157
)
153-
)
154-
if not installations:
155-
logger.info("event_alert_webhook.missing_installation", extra=extra)
156-
return
157-
(install,) = installations
158+
if not installations:
159+
raise SentryAppSentryError(message=SentryAppWebhookFailureReason.MISSING_INSTALLATION)
160+
(install,) = installations
158161

159-
nodedata = nodestore.backend.get(
160-
BaseEvent.generate_node_id(project_id=project.id, event_id=instance_id)
161-
)
162+
nodedata = nodestore.backend.get(
163+
BaseEvent.generate_node_id(project_id=project.id, event_id=instance_id)
164+
)
162165

163-
if not nodedata:
164-
extra = {
165-
"event_id": instance_id,
166-
"occurrence_id": occurrence_id,
167-
"rule": rule,
168-
"sentry_app": sentry_app.slug,
169-
"group_id": group_id,
170-
}
171-
logger.info("send_alert_event.missing_event", extra=extra)
172-
return
166+
if not nodedata:
167+
raise SentryAppSentryError(message=SentryAppWebhookFailureReason.MISSING_EVENT)
173168

174-
occurrence = None
175-
if occurrence_id:
176-
occurrence = IssueOccurrence.fetch(occurrence_id, project_id=project.id)
169+
occurrence = None
170+
if occurrence_id:
171+
occurrence = IssueOccurrence.fetch(occurrence_id, project_id=project.id)
177172

178-
if not occurrence:
179-
logger.info(
180-
"send_alert_event.missing_occurrence",
181-
extra={"occurrence_id": occurrence_id, "project_id": project.id},
182-
)
183-
return
184-
185-
group_event = GroupEvent(
186-
project_id=project.id,
187-
event_id=instance_id,
188-
group=group,
189-
data=nodedata,
190-
occurrence=occurrence,
191-
)
173+
if not occurrence:
174+
raise SentryAppSentryError(
175+
message=SentryAppWebhookFailureReason.MISSING_ISSUE_OCCURRENCE
176+
)
192177

193-
event_context = _webhook_event_data(group_event, group.id, project.id)
178+
group_event = GroupEvent(
179+
project_id=project.id,
180+
event_id=instance_id,
181+
group=group,
182+
data=nodedata,
183+
occurrence=occurrence,
184+
)
194185

195-
data = {"event": event_context, "triggered_rule": rule}
186+
event_context = _webhook_event_data(group_event, group.id, project.id)
196187

197-
# Attach extra payload to the webhook
198-
if additional_payload_key and additional_payload:
199-
data[additional_payload_key] = additional_payload
188+
data = {"event": event_context, "triggered_rule": rule_label}
200189

201-
request_data = AppPlatformEvent(
202-
resource="event_alert", action="triggered", install=install, data=data
203-
)
190+
# Attach extra payload to the webhook
191+
if additional_payload_key and additional_payload:
192+
data[additional_payload_key] = additional_payload
193+
194+
request_data = AppPlatformEvent(
195+
resource="event_alert", action="triggered", install=install, data=data
196+
)
204197

205198
send_and_save_webhook_request(sentry_app, request_data)
206199

@@ -210,7 +203,7 @@ def send_alert_webhook(
210203
"alert_rule_ui_component_webhook.sent",
211204
organization_id=organization.id,
212205
sentry_app_id=sentry_app_id,
213-
event=f"{request_data.resource}.{request_data.action}",
206+
event=SentryAppEventType.EVENT_ALERT_TRIGGERED,
214207
)
215208

216209

@@ -501,6 +494,10 @@ def send_resource_change_webhook(
501494
def notify_sentry_app(event: GroupEvent, futures: Sequence[RuleFuture]):
502495
for f in futures:
503496
if not f.kwargs.get("sentry_app"):
497+
logger.error(
498+
"notify_sentry_app.future_missing_sentry_app",
499+
extra={"event": event.as_dict(), "future": f, "event_id": event.event_id},
500+
)
504501
continue
505502

506503
extra_kwargs: dict[str, Any] = {
@@ -522,7 +519,7 @@ def notify_sentry_app(event: GroupEvent, futures: Sequence[RuleFuture]):
522519
instance_id=event.event_id,
523520
group_id=event.group_id,
524521
occurrence_id=event.occurrence_id if hasattr(event, "occurrence_id") else None,
525-
rule=f.rule.label,
522+
rule_label=f.rule.label,
526523
sentry_app_id=f.kwargs["sentry_app"].id,
527524
**extra_kwargs,
528525
)

0 commit comments

Comments
 (0)