11from __future__ import annotations
22
33from dataclasses import dataclass
4+ from typing import override
5+
6+ from sentry_kafka_schemas .schema_types .uptime_results_v1 import CheckResult , CheckStatus
47
58from sentry .issues .grouptype import GroupCategory , GroupType
9+ from sentry .issues .issue_occurrence import IssueEvidence
610from sentry .ratelimits .sliding_windows import Quota
711from sentry .types .group import PriorityLevel
12+ from sentry .uptime .models import UptimeSubscription
813from sentry .uptime .types import (
914 GROUP_TYPE_UPTIME_DOMAIN_CHECK_FAILURE ,
1015 ProjectUptimeSubscriptionMode ,
1116)
12- from sentry .workflow_engine .types import DetectorSettings
17+ from sentry .workflow_engine .handlers .detector .base import DetectorOccurrence , EventData
18+ from sentry .workflow_engine .handlers .detector .stateful import (
19+ DetectorThresholds ,
20+ StatefulDetectorHandler ,
21+ )
22+ from sentry .workflow_engine .models import DataPacket , Detector
23+ from sentry .workflow_engine .processors .data_condition_group import ProcessedDataConditionGroup
24+ from sentry .workflow_engine .types import DetectorPriorityLevel , DetectorSettings
25+
26+
27+ @dataclass (frozen = True )
28+ class DetectorPacketValue :
29+ """
30+ Represents the value passed into the uptime detector
31+ """
32+
33+ check_result : CheckResult
34+ subscription : UptimeSubscription
35+
36+
37+ def build_detector_fingerprint_component (detector : Detector ) -> str :
38+ return f"uptime-detector:{ detector .id } "
39+
40+
41+ def build_fingerprint (detector : Detector ) -> list [str ]:
42+ return [build_detector_fingerprint_component (detector )]
43+
44+
45+ class UptimeDetectorHandler (StatefulDetectorHandler [DetectorPacketValue , CheckStatus ]):
46+ @override
47+ @property
48+ def thresholds (self ) -> DetectorThresholds :
49+ """
50+ Require 3 uptime checks to fail before activating the detector.
51+ Likewise require 3 successful checks to recover.
52+ """
53+ return {
54+ DetectorPriorityLevel .OK : 3 ,
55+ DetectorPriorityLevel .HIGH : 3 ,
56+ }
57+
58+ @override
59+ def extract_value (self , data_packet : DataPacket [DetectorPacketValue ]) -> CheckStatus :
60+ return data_packet .packet .check_result ["status" ]
61+
62+ @override
63+ def build_issue_fingerprint (self ) -> list [str ]:
64+ return build_fingerprint (self .detector )
65+
66+ @override
67+ def create_occurrence (
68+ self ,
69+ evaluation_result : ProcessedDataConditionGroup ,
70+ data_packet : DataPacket [DetectorPacketValue ],
71+ priority : DetectorPriorityLevel ,
72+ ) -> tuple [DetectorOccurrence , EventData ]:
73+ result = data_packet .packet .check_result
74+ uptime_subscription = data_packet .packet .subscription
75+
76+ evidence_display : list [IssueEvidence ] = []
77+
78+ status_reason = result ["status_reason" ]
79+ if status_reason :
80+ reason_evidence = IssueEvidence (
81+ name = "Failure reason" ,
82+ value = f'{ status_reason ["type" ]} - { status_reason ["description" ]} ' ,
83+ important = True ,
84+ )
85+ evidence_display .extend ([reason_evidence ])
86+
87+ evidence_display .append (
88+ IssueEvidence (
89+ name = "Duration" ,
90+ value = f"{ result ["duration_ms" ]} ms" ,
91+ important = False ,
92+ ),
93+ )
94+
95+ request_info = result ["request_info" ]
96+ if request_info :
97+ method_evidence = IssueEvidence (
98+ name = "Method" ,
99+ value = request_info ["request_type" ],
100+ important = False ,
101+ )
102+ status_code_evidence = IssueEvidence (
103+ name = "Status Code" ,
104+ value = str (request_info ["http_status_code" ]),
105+ important = False ,
106+ )
107+ evidence_display .extend ([method_evidence , status_code_evidence ])
108+
109+ occurrence = DetectorOccurrence (
110+ issue_title = f"Downtime detected for { uptime_subscription .url } " ,
111+ subtitle = "Your monitored domain is down" ,
112+ evidence_display = evidence_display ,
113+ type = UptimeDomainCheckFailure ,
114+ level = "error" ,
115+ culprit = "" , # TODO: The url?
116+ assignee = self .detector .owner ,
117+ priority = priority ,
118+ )
119+
120+ return (occurrence , {})
13121
14122
15123@dataclass (frozen = True )
@@ -24,6 +132,7 @@ class UptimeDomainCheckFailure(GroupType):
24132 enable_auto_resolve = False
25133 enable_escalation_detection = False
26134 detector_settings = DetectorSettings (
135+ handler = UptimeDetectorHandler ,
27136 config_schema = {
28137 "$schema" : "https://json-schema.org/draft/2020-12/schema" ,
29138 "description" : "A representation of an uptime alert" ,
0 commit comments