Browse Source

fix(growth): Fixes issues, crash rate, and alerts on Demo Mode (#26978)

This PR fixes a few issues with Demo Mode. First, it lowers the crash free rate for the final release for all projects. Second, it connects one of the iOS events and iOS transaction. Third, it creates a mobile project alert for Android. Finally, it moves an issue to the latest release for each project.
roggenkemper 3 years ago
parent
commit
d5291566d9

+ 6 - 6
src/sentry/demo/data/errors/android/app_not_responding.json

@@ -8,8 +8,8 @@
   "datetime": "2021-05-24T13:15:04+00:00",
   "tags": [
     ["activity", "MainActivity"],
-    ["device", "Android SDK built for x86"],
-    ["device.family", "Android"],
+    ["device", "Pixel5"],
+    ["device.family", "Google"],
     ["level", "error"],
     ["mechanism", "ANR"],
     ["handled", "no"],
@@ -87,15 +87,15 @@
       "type": "app"
     },
     "device": {
-      "name": "Android SDK built for x86",
-      "family": "Android",
-      "model": "Android SDK built for x86",
+      "name": "Pixel 5",
+      "family": "Google",
+      "model": "Pixel 5",
       "model_id": "QSR1.190920.001",
       "arch": "x86",
       "battery_level": 100.0,
       "orientation": "portrait",
       "manufacturer": "Google",
-      "brand": "google",
+      "brand": "Google",
       "screen_resolution": "1794x1080",
       "screen_density": 2.625,
       "screen_dpi": 420,

+ 6 - 6
src/sentry/demo/data/errors/android/out_of_bounds.json

@@ -8,8 +8,8 @@
   "datetime": "2021-05-24T13:10:04+00:00",
   "tags": [
     ["activity", "MainActivity"],
-    ["device", "Android SDK built for x86"],
-    ["device.family", "Android"],
+    ["device", "Pixel4"],
+    ["device.family", "Google"],
     ["level", "error"],
     ["os", "Android 10"],
     ["os.name", "Android"],
@@ -79,15 +79,15 @@
       "type": "app"
     },
     "device": {
-      "name": "Android SDK built for x86",
-      "family": "Android",
-      "model": "Android SDK built for x86",
+      "name": "Pixel 4",
+      "family": "Google",
+      "model": "Pixel 4",
       "model_id": "QSR1.190920.001",
       "arch": "x86",
       "battery_level": 100.0,
       "orientation": "portrait",
       "manufacturer": "Google",
-      "brand": "google",
+      "brand": "Google",
       "screen_resolution": "1794x1080",
       "screen_density": 2.625,
       "screen_dpi": 420,

+ 0 - 2
src/sentry/demo/data/errors/ios/exc_bad_access.json

@@ -7,7 +7,6 @@
   "message": "",
   "datetime": "2021-05-24T13:15:04+00:00",
   "tags": [
-    ["app.device", "76b2897b369b05951cfdc9285516010920b608a9"],
     ["device", "iPad13,1"],
     ["device.family", "iOS"],
     ["environment", "debug"],
@@ -57,7 +56,6 @@
   },
   "contexts": {
     "app": {
-      "device_app_hash": "76b2897b369b05951cfdc9285516010920b608a9",
       "build_type": "debug",
       "app_identifier": "io.sentry.sample.iOS-Swift",
       "app_name": "iOS-Swift",

+ 0 - 2
src/sentry/demo/data/errors/react_native/out_of_memory.json

@@ -7,7 +7,6 @@
   "message": "",
   "datetime": "2021-06-21T06:21:39.599000Z",
   "tags": [
-    ["app.device", "1b94d8374b00174250159b2d866c28a336f51e98"],
     ["device", "iPhone13,2"],
     ["device.family", "iOS"],
     ["environment", "production"],
@@ -25,7 +24,6 @@
   "_metrics": {"bytes.ingested.event": 1231, "bytes.stored.event": 2392},
   "contexts": {
     "app": {
-      "device_app_hash": "1b94d8374b00174250159b2d866c28a336f51e98",
       "build_type": "simulator",
       "app_identifier": "io.sentry.sample",
       "app_name": "sample",

+ 0 - 2
src/sentry/demo/data/errors/react_native/promise_rejection.json

@@ -7,7 +7,6 @@
   "message": "",
   "datetime": "2021-06-21T06:31:46.587000Z",
   "tags": [
-    ["app.device", "1b94d8374b00174250159b2d866c28a336f51e98"],
     ["device", "iPhone13,2"],
     ["device.family", "iOS"],
     ["environment", "production"],
@@ -472,7 +471,6 @@
   },
   "contexts": {
     "app": {
-      "device_app_hash": "1b94d8374b00174250159b2d866c28a336f51e98",
       "build_type": "simulator",
       "app_identifier": "io.sentry.sample",
       "app_name": "sample",

+ 0 - 2
src/sentry/demo/data/errors/ios/handled.json → src/sentry/demo/data/scen3/handled.json

@@ -7,7 +7,6 @@
   "message": "Handled Exception",
   "datetime": "2021-05-24T13:15:04+00:00",
   "tags": [
-    ["app.device", "2e2521004d3aadf6e383318cba7c7c91fdf37961"],
     ["customer-type", "enterprise"],
     ["device", "iPhone12,1"],
     ["device.family", "iOS"],
@@ -70,7 +69,6 @@
       "type": "My Context"
     },
     "app": {
-      "device_app_hash": "2e2521004d3aadf6e383318cba7c7c91fdf37961",
       "build_type": "simulator",
       "app_identifier": "sentry.sentry-cocoa",
       "app_name": "sentry-cocoa",

+ 0 - 0
src/sentry/demo/data/transactions/ios/ios_transaction.json → src/sentry/demo/data/scen3/ios_transaction.json


+ 133 - 25
src/sentry/demo/data_population.py

@@ -66,7 +66,8 @@ commit_message_base_messages = [
 
 base_paths_by_file_type = {"js": ["components/", "views/"], "py": ["flask/", "routes/"]}
 
-rate_by_release_num = [1.0, 0.99, 0.9]
+rate_by_release_num = [0.9, 0.95, 0.85]
+agg_rate_by_release_num = [0.995, 0.998, 0.95]
 
 org_users = [
     ("scefali", "Stephen Cefali"),
@@ -77,6 +78,30 @@ org_users = [
 
 logger = logging.getLogger(__name__)
 
+contexts_by_mobile_platform = {
+    "apple-ios": {
+        "device": [
+            ["iPad13,1", "iOS"],
+            ["iPad13,2", "iOS"],
+            ["iPhone13,1", "iOS"],
+            ["iPhone11", "iOS"],
+        ],
+        "os": [["iOS", "14.5"], ["iOS", "13.3"], ["iOS", "14.6"], ["iOS", "12"]],
+    },
+    "android": {
+        "device": [
+            ["Pixel 4", "Pixel"],
+            ["Pixel 5", "Pixel"],
+            ["Pixel 3a", "Pixel"],
+            ["SM-A125U", "SM-A125U"],
+            ["SM-G973U", "SM-G973U"],
+        ],
+        "os": [["Android", "10"], ["Android", "9"], ["Android", "8"]],
+    },
+}
+
+mobile_platforms = ["apple-ios", "android", "react-native"]
+
 
 def get_data_file_path(file_name):
     return os.path.join(os.path.dirname(__file__), "data", file_name)
@@ -271,6 +296,22 @@ def gen_base_context():
     return random.choice(contexts)
 
 
+def gen_mobile_context(platform):
+    """
+    Generates context for mobile events
+    """
+    if platform == "react-native":
+        platform = random.choice(["apple-ios", "android"])
+    contexts = contexts_by_mobile_platform[platform]
+    device = random.choice(contexts["device"])
+    os = random.choice(contexts["os"])
+    context = {
+        "device": {"model": device[0], "family": device[1], "type": "device"},
+        "os": {"name": os[0], "version": os[1], "type": "os"},
+    }
+    return context
+
+
 def get_release_from_time(org_id, timestamp):
     """
     Returns the most release before a specific time
@@ -466,20 +507,20 @@ def fix_measurements(event_json):
         measurements.update(measurement_markers)
 
 
-def update_context(event, trace=None):
+def update_context(event, trace=None, platform=None):
+    mobile = platform in mobile_platforms
     context = event["contexts"]
     # delete device since we aren't mocking it (yet)
     if "device" in context:
         del context["device"]
     # generate random browser and os
-    context.update(**gen_base_context())
+    base_context = gen_mobile_context(platform) if mobile else gen_base_context()
+    context.update(**base_context)
+
     # add our trace info
     base_trace = context.get("trace", {})
     if not trace:
-        trace = {
-            "trace_id": uuid4().hex,
-            "span_id": uuid4().hex[:16],
-        }
+        trace = {"trace_id": uuid4().hex, "span_id": uuid4().hex[:16]}
     base_trace.update(**trace)
     context["trace"] = base_trace
 
@@ -615,6 +656,8 @@ class DataPopulation:
                     "components/Form.js",
                     "flask/app.py",
                     "purchase.py",
+                    "checkout.swift",
+                    "shop.java",
                 ],
                 ["js", "py"],
             )
@@ -678,7 +721,7 @@ class DataPopulation:
         alert_rule = create_alert_rule(
             org,
             [project],
-            "High Error Rate",
+            f"High Error Rate - {project.name} ",
             "level:error",
             "count()",
             10,
@@ -1053,7 +1096,61 @@ class DataPopulation:
 
         self.log_info("populate_connected_event_scenario_2.finished")
 
-    def populate_generic_error(self, project: Project, file_path, dist_number, starting_release=0):
+    def populate_connected_event_scenario_3(self, ios_project: Project):
+
+        ios_error = get_event_from_file("scen3/handled.json")
+        ios_transaction = get_event_from_file("scen3/ios_transaction.json")
+
+        self.log_info("populate_connected_event_scenario_3.start")
+
+        for (timestamp, day) in self.iter_timestamps(4):
+            transaction_user = self.generate_user()
+            trace_id = uuid4().hex
+            release = get_release_from_time(ios_project.organization_id, timestamp)
+            release_sha = release.version
+
+            old_span_id = ios_transaction["contexts"]["trace"]["span_id"]
+            root_span_id = uuid4().hex[:16]
+            duration = self.gen_frontend_duration(day)
+
+            trace = {
+                "trace_id": trace_id,
+                "span_id": root_span_id,
+            }
+
+            # iOS transaction
+            local_event = copy.deepcopy(ios_transaction)
+            local_event.update(
+                project=ios_project,
+                platform=ios_project.platform,
+                event_id=uuid4().hex,
+                user=transaction_user,
+                release=release_sha,
+                timestamp=timestamp,
+                start_timestamp=timestamp - timedelta(duration),
+            )
+            update_context(local_event, trace, platform=ios_project.platform)
+            self.fix_transaction_event(local_event, old_span_id)
+            self.safe_send_event(local_event)
+
+            # iOS Error
+            local_event = copy.deepcopy(ios_error)
+            local_event.update(
+                project=ios_project,
+                platform=ios_project.platform,
+                timestamp=timestamp,
+                user=transaction_user,
+                release=release_sha,
+            )
+            update_context(local_event, trace, platform=ios_project.platform)
+            self.fix_error_event(local_event)
+            self.safe_send_event(local_event)
+
+        self.log_info("populate_connected_event_scenario_3.finished")
+
+    def populate_generic_error(
+        self, project: Project, file_path, dist_number, mobile=False, starting_release=0
+    ):
         """
         This function populates a single error
         Occurrance times and durations are randomized
@@ -1075,13 +1172,18 @@ class DataPopulation:
                 user=transaction_user,
                 release=release_sha,
             )
-            update_context(local_event)
+            update_context(local_event, platform=project.platform)
             self.fix_error_event(local_event)
             self.safe_send_event(local_event)
         self.log_info("populate_generic_error.finished")
 
     def populate_generic_transaction(
-        self, project: Project, file_path, dist_number, starting_release=0
+        self,
+        project: Project,
+        file_path,
+        dist_number,
+        mobile,
+        starting_release=0,
     ):
         """
         This function populates a single transaction
@@ -1110,7 +1212,7 @@ class DataPopulation:
                 start_timestamp=timestamp - timedelta(seconds=duration),
             )
 
-            update_context(local_event)
+            update_context(local_event, platform=project.platform)
 
             self.fix_transaction_event(local_event, old_span_id)
             self.safe_send_event(local_event)
@@ -1158,7 +1260,7 @@ class DataPopulation:
             release_num = int(version.split(".")[-1])
             threshold = rate_by_release_num[release_num]
             outcome = random.random()
-            if outcome > threshold:
+            if outcome <= threshold:
                 if error_file:
                     local_event = copy.deepcopy(error)
                     local_event.update(
@@ -1218,7 +1320,8 @@ class DataPopulation:
         item_headers = json.dumps({"type": "sessions"})
 
         agg = []
-        threshold = 0.004
+        release_num = int(version.split(".")[-1])
+        threshold = agg_rate_by_release_num[release_num]
 
         if self.quick:
             num_users = int(random.uniform(100, 200))
@@ -1290,13 +1393,15 @@ class DataPopulation:
             self.populate_generic_error(react_project, "errors/react/func_undefined.json", 3)
             self.populate_generic_error(python_project, "errors/python/cert_error.json", 5)
             self.populate_generic_error(
-                python_project, "errors/python/concat_str_none.json", 4, starting_release=2
+                python_project, "errors/python/concat_str_none.json", 4, starting_release=1
             )
         self.assign_issues()
 
     def handle_mobile_scenario(
         self, ios_project: Project, android_project: Project, react_native_project: Project
     ):
+        with sentry_sdk.start_span(op="handle_mobile_scenario", description="pre_event_setup"):
+            self.generate_alerts(android_project)
         if not self.get_config_var("DISABLE_SESSIONS"):
             with sentry_sdk.start_span(
                 op="handle_react_python_scenario", description="populate_sessions"
@@ -1304,28 +1409,31 @@ class DataPopulation:
                 self.populate_sessions(ios_project, 6, True)
                 self.populate_sessions(android_project, 8, True)
                 self.populate_sessions(react_native_project, 9, True)
-
+        with sentry_sdk.start_span(op="handle_mobile_scenario", description="populate_connected"):
+            self.populate_connected_event_scenario_3(ios_project)
         with sentry_sdk.start_span(op="handle_mobile_scenario", description="populate_errors"):
-            self.populate_generic_error(ios_project, "errors/ios/exc_bad_access.json", 3)
             self.populate_generic_error(
-                ios_project, "errors/ios/handled.json", 5, starting_release=1
-            )
-            self.populate_generic_transaction(
-                ios_project, "transactions/ios/ios_transaction.json", 4
+                ios_project, "errors/ios/exc_bad_access.json", 3, mobile=True
             )
+
             self.populate_generic_error(
-                android_project, "errors/android/out_of_bounds.json", 2, starting_release=1
+                android_project,
+                "errors/android/out_of_bounds.json",
+                2,
+                mobile=True,
+                starting_release=2,
             )
             self.populate_generic_error(
-                android_project, "errors/android/app_not_responding.json", 3
+                android_project, "errors/android/app_not_responding.json", 3, mobile=True
             )
             self.populate_generic_error(
-                react_native_project, "errors/react_native/out_of_memory.json", 5
+                react_native_project, "errors/react_native/out_of_memory.json", 5, mobile=True
             )
             self.populate_generic_error(
                 react_native_project,
                 "errors/react_native/promise_rejection.json",
                 3,
-                starting_release=1,
+                mobile=True,
+                starting_release=2,
             )
         self.assign_issues()