diff --git a/src/sentry/constants.py b/src/sentry/constants.py index 6328415846c42f..7bbcd3bec498ec 100644 --- a/src/sentry/constants.py +++ b/src/sentry/constants.py @@ -612,6 +612,31 @@ class InsightModules(Enum): LLM_MONITORING = "llm_monitoring" +# each span filter takes in a span object and returns whether +# the span belongs in the corresponding insight module +INSIGHT_MODULE_SPAN_FILTERS = { + InsightModules.HTTP: lambda span: span.get("op") == "http.client" + and span.get("module") == "http", + InsightModules.DB: lambda span: span.get("module") == "db" and "description" in span.keys(), + InsightModules.ASSETS: lambda span: span.get("op") + in ["resource.script", "resource.css", "resource.font", "resource.img"], + InsightModules.APP_START: lambda span: span.get("op").startswith("app.start."), + InsightModules.SCREEN_LOAD: lambda span: span.get("sentry_tags", {}).get("transaction.op") + == "ui.load", + InsightModules.VITAL: lambda span: span.get("op") + in [ + "ui.interaction.click", + "ui.interaction.hover", + "ui.interaction.drag", + "ui.interaction.press", + ], + InsightModules.CACHE: lambda span: span.get("op") + in ["cache.get_item", "cache.get", "cache.put"], + InsightModules.QUEUE: lambda span: span.get("op") in ["queue.process", "queue.publish"], + InsightModules.LLM_MONITORING: lambda span: span.get("op").startswith("ai.pipeline"), +} + + StatsPeriod = namedtuple("StatsPeriod", ("segments", "interval")) LEGACY_RATE_LIMIT_OPTIONS = frozenset(("sentry:project-rate-limit", "sentry:account-rate-limit")) diff --git a/src/sentry/event_manager.py b/src/sentry/event_manager.py index 8ca1c297458448..abefd859fcf52f 100644 --- a/src/sentry/event_manager.py +++ b/src/sentry/event_manager.py @@ -33,10 +33,12 @@ from sentry.attachments import CachedAttachment, MissingAttachmentChunks, attachment_cache from sentry.constants import ( DEFAULT_STORE_NORMALIZER_ARGS, + INSIGHT_MODULE_SPAN_FILTERS, LOG_LEVELS_MAP, MAX_TAG_VALUE_LENGTH, PLACEHOLDER_EVENT_TITLES, DataCategory, + InsightModules, ) from sentry.culprit import generate_culprit from sentry.dynamic_sampling import LatestReleaseBias, LatestReleaseParams @@ -110,6 +112,7 @@ from sentry.signals import ( first_event_received, first_event_with_minified_stack_trace_received, + first_insight_span_received, first_transaction_received, issue_unresolved, ) @@ -223,6 +226,27 @@ def plugin_is_regression(group: Group, event: BaseEvent) -> bool: return True +def get_project_insight_flag(project: Project, module: InsightModules): + if module == InsightModules.HTTP: + return project.flags.has_insights_http + elif module == InsightModules.DB: + return project.flags.has_insights_db + elif module == InsightModules.ASSETS: + return project.flags.has_insights_assets + elif module == InsightModules.APP_START: + return project.flags.has_insights_app_start + elif module == InsightModules.SCREEN_LOAD: + return project.flags.has_insights_screen_load + elif module == InsightModules.VITAL: + return project.flags.has_insights_vitals + elif module == InsightModules.CACHE: + return project.flags.has_insights_caches + elif module == InsightModules.QUEUE: + return project.flags.has_insights_queues + elif module == InsightModules.LLM_MONITORING: + return project.flags.has_insights_llm_monitoring + + def has_pending_commit_resolution(group: Group) -> bool: """ Checks that the most recent commit that fixes a group has had a chance to release @@ -475,6 +499,13 @@ def save( project=project, event=jobs[0]["event"], sender=Project ) + for module, is_module_span in INSIGHT_MODULE_SPAN_FILTERS.items(): + if not get_project_insight_flag(project, module) and any( + [is_module_span(span) for span in job["data"]["spans"]] + ): + first_insight_span_received.send_robust( + project=project, module=module, sender=Project + ) return jobs[0]["event"] elif event_type == "generic": job["data"]["project"] = project.id