From 9b0ac3df0b196866ebfce9b161272471eb77c8eb Mon Sep 17 00:00:00 2001 From: Fabian Schindler Date: Thu, 10 Jul 2025 09:24:27 +0200 Subject: [PATCH] ref(analytics): Transform analytics events for TET-830 - Transform event classes to use @analytics.eventclass decorator - Transform analytics.record calls to use event class instances - Update imports as needed Closes TET-830 --- .../alert_rule_ui_component_webhook_sent.py | 13 ++-- src/sentry/analytics/events/alert_sent.py | 27 ++++---- .../events/checkin_processing_error_stored.py | 13 ++-- .../analytics/events/codeowners_created.py | 13 ++-- .../events/eventuser_endpoint_request.py | 9 +-- .../events/first_cron_checkin_sent.py | 13 ++-- .../events/first_insight_span_sent.py | 15 ++--- .../events/first_transaction_sent.py | 13 ++-- .../integration_commit_context_all_frames.py | 46 ++++++-------- src/sentry/analytics/events/member_invited.py | 13 ++-- .../events/onboarding_continuation_sent.py | 11 ++-- .../analytics/events/project_transferred.py | 13 ++-- .../events/release_get_previous_commits.py | 13 ++-- .../analytics/events/release_set_commits.py | 13 ++-- .../events/webhook_repository_created.py | 11 ++-- src/sentry/api/base.py | 12 ++-- src/sentry/api/endpoints/codeowners/index.py | 12 ++-- .../api/endpoints/group_tagkey_values.py | 8 ++- ...anization_onboarding_continuation_email.py | 10 +-- src/sentry/api/endpoints/project_users.py | 8 ++- src/sentry/incidents/action_handlers.py | 18 +++--- src/sentry/integrations/github/webhook.py | 10 +-- .../integrations/services/integration/impl.py | 12 ++-- .../integrations/utils/commit_context.py | 46 ++++++++------ .../organization_release_previous_commits.py | 12 ++-- .../monitors/processing_errors/manager.py | 14 +++-- .../notifications/notifications/digest.py | 18 +++--- .../notifications/notifications/rules.py | 18 +++--- src/sentry/receivers/onboarding.py | 62 +++++++++++-------- src/sentry/rules/actions/integrations/base.py | 18 +++--- src/sentry/sentry_apps/tasks/sentry_apps.py | 12 ++-- src/sentry/tasks/commit_context.py | 20 +++--- src/sentry/tasks/unmerge.py | 8 ++- 33 files changed, 281 insertions(+), 273 deletions(-) diff --git a/src/sentry/analytics/events/alert_rule_ui_component_webhook_sent.py b/src/sentry/analytics/events/alert_rule_ui_component_webhook_sent.py index cb5aab4edbc078..d9bc0ee6f377d8 100644 --- a/src/sentry/analytics/events/alert_rule_ui_component_webhook_sent.py +++ b/src/sentry/analytics/events/alert_rule_ui_component_webhook_sent.py @@ -1,15 +1,12 @@ from sentry import analytics +@analytics.eventclass("alert_rule_ui_component_webhook.sent") class AlertRuleUiComponentWebhookSentEvent(analytics.Event): - type = "alert_rule_ui_component_webhook.sent" - - attributes = ( - # organization_id refers to the organization that installed the sentryapp - analytics.Attribute("organization_id"), - analytics.Attribute("sentry_app_id"), - analytics.Attribute("event"), - ) + # organization_id refers to the organization that installed the sentryapp + organization_id: str + sentry_app_id: str + event: str analytics.register(AlertRuleUiComponentWebhookSentEvent) diff --git a/src/sentry/analytics/events/alert_sent.py b/src/sentry/analytics/events/alert_sent.py index ccc0b33c6378eb..d2e9063388e97b 100644 --- a/src/sentry/analytics/events/alert_sent.py +++ b/src/sentry/analytics/events/alert_sent.py @@ -1,22 +1,19 @@ from sentry import analytics +@analytics.eventclass("alert.sent") class AlertSentEvent(analytics.Event): - type = "alert.sent" - - attributes = ( - analytics.Attribute("organization_id"), - analytics.Attribute("project_id"), - # The id of the Alert or AlertRule - analytics.Attribute("alert_id"), - # "issue_alert" or "metric_alert" - analytics.Attribute("alert_type"), - # Slack, msteams, email, etc. - analytics.Attribute("provider"), - # User_id if sent via email, channel id if sent via slack, etc. - analytics.Attribute("external_id", type=str, required=False), - analytics.Attribute("notification_uuid", required=False), - ) + organization_id: str + project_id: str + # The id of the Alert or AlertRule + alert_id: str + # "issue_alert" or "metric_alert" + alert_type: str + # Slack, msteams, email, etc. + provider: str + # User_id if sent via email, channel id if sent via slack, etc. + external_id: str | None = None + notification_uuid: str | None = None analytics.register(AlertSentEvent) diff --git a/src/sentry/analytics/events/checkin_processing_error_stored.py b/src/sentry/analytics/events/checkin_processing_error_stored.py index cbae333ee2271c..1034b354db9958 100644 --- a/src/sentry/analytics/events/checkin_processing_error_stored.py +++ b/src/sentry/analytics/events/checkin_processing_error_stored.py @@ -1,15 +1,12 @@ from sentry import analytics +@analytics.eventclass("checkin_processing_error.stored") class CheckinProcessingErrorStored(analytics.Event): - type = "checkin_processing_error.stored" - - attributes = ( - analytics.Attribute("organization_id"), - analytics.Attribute("project_id"), - analytics.Attribute("monitor_slug"), - analytics.Attribute("error_types", type=list), - ) + organization_id: str + project_id: str + monitor_slug: str + error_types: list analytics.register(CheckinProcessingErrorStored) diff --git a/src/sentry/analytics/events/codeowners_created.py b/src/sentry/analytics/events/codeowners_created.py index 856507a0d7835a..2128f974855cac 100644 --- a/src/sentry/analytics/events/codeowners_created.py +++ b/src/sentry/analytics/events/codeowners_created.py @@ -1,15 +1,12 @@ from sentry import analytics +@analytics.eventclass("codeowners.created") class CodeownersCreated(analytics.Event): - type = "codeowners.created" - - attributes = ( - analytics.Attribute("user_id", required=False), - analytics.Attribute("organization_id"), - analytics.Attribute("project_id"), - analytics.Attribute("codeowners_id"), - ) + user_id: str | None = None + organization_id: str + project_id: str + codeowners_id: str analytics.register(CodeownersCreated) diff --git a/src/sentry/analytics/events/eventuser_endpoint_request.py b/src/sentry/analytics/events/eventuser_endpoint_request.py index 79c28fdc6868a5..df1dd2af56d5a5 100644 --- a/src/sentry/analytics/events/eventuser_endpoint_request.py +++ b/src/sentry/analytics/events/eventuser_endpoint_request.py @@ -1,13 +1,10 @@ from sentry import analytics +@analytics.eventclass("eventuser_endpoint.request") class EventUserEndpointRequest(analytics.Event): - type = "eventuser_endpoint.request" - - attributes = ( - analytics.Attribute("endpoint", required=True), - analytics.Attribute("project_id", required=False), - ) + endpoint: str + project_id: str | None = None analytics.register(EventUserEndpointRequest) diff --git a/src/sentry/analytics/events/first_cron_checkin_sent.py b/src/sentry/analytics/events/first_cron_checkin_sent.py index 3e3f3448ebdd58..2d29d97adc138a 100644 --- a/src/sentry/analytics/events/first_cron_checkin_sent.py +++ b/src/sentry/analytics/events/first_cron_checkin_sent.py @@ -1,15 +1,12 @@ from sentry import analytics +@analytics.eventclass("first_cron_checkin.sent") class FirstCronCheckinSent(analytics.Event): - type = "first_cron_checkin.sent" - - attributes = ( - analytics.Attribute("organization_id"), - analytics.Attribute("project_id"), - analytics.Attribute("monitor_id"), - analytics.Attribute("user_id", required=False), - ) + organization_id: str + project_id: str + monitor_id: str + user_id: str | None = None analytics.register(FirstCronCheckinSent) diff --git a/src/sentry/analytics/events/first_insight_span_sent.py b/src/sentry/analytics/events/first_insight_span_sent.py index e9b7124f3d73d0..8ba721daa6c9ff 100644 --- a/src/sentry/analytics/events/first_insight_span_sent.py +++ b/src/sentry/analytics/events/first_insight_span_sent.py @@ -1,16 +1,13 @@ from sentry import analytics +@analytics.eventclass("first_insight_span.sent") class FirstInsightSpanSentEvent(analytics.Event): - type = "first_insight_span.sent" - - attributes = ( - analytics.Attribute("organization_id"), - analytics.Attribute("user_id"), - analytics.Attribute("project_id"), - analytics.Attribute("module"), - analytics.Attribute("platform", required=False), - ) + organization_id: str + user_id: str + project_id: str + module: str + platform: str | None = None analytics.register(FirstInsightSpanSentEvent) diff --git a/src/sentry/analytics/events/first_transaction_sent.py b/src/sentry/analytics/events/first_transaction_sent.py index 3e8821b914e6ca..4c701ab448efbc 100644 --- a/src/sentry/analytics/events/first_transaction_sent.py +++ b/src/sentry/analytics/events/first_transaction_sent.py @@ -1,15 +1,12 @@ from sentry import analytics +@analytics.eventclass("first_transaction.sent") class FirstTransactionSentEvent(analytics.Event): - type = "first_transaction.sent" - - attributes = ( - analytics.Attribute("organization_id"), - analytics.Attribute("project_id"), - analytics.Attribute("platform", required=False), - analytics.Attribute("default_user_id", required=False), - ) + organization_id: str + project_id: str + platform: str | None = None + default_user_id: str | None = None analytics.register(FirstTransactionSentEvent) diff --git a/src/sentry/analytics/events/integration_commit_context_all_frames.py b/src/sentry/analytics/events/integration_commit_context_all_frames.py index 053472114c9f23..58545df56e0043 100644 --- a/src/sentry/analytics/events/integration_commit_context_all_frames.py +++ b/src/sentry/analytics/events/integration_commit_context_all_frames.py @@ -1,36 +1,30 @@ from sentry import analytics +@analytics.eventclass("integrations.failed_to_fetch_commit_context_all_frames") class IntegrationsFailedToFetchCommitContextAllFrames(analytics.Event): - type = "integrations.failed_to_fetch_commit_context_all_frames" - - attributes = ( - analytics.Attribute("organization_id"), - analytics.Attribute("project_id"), - analytics.Attribute("group_id"), - analytics.Attribute("event_id"), - analytics.Attribute("num_frames", type=int), - analytics.Attribute("num_successfully_mapped_frames", type=int), - analytics.Attribute("reason"), - ) + organization_id: str + project_id: str + group_id: str + event_id: str + num_frames: int + num_successfully_mapped_frames: int + reason: str +@analytics.eventclass("integrations.successfully_fetched_commit_context_all_frames") class IntegrationsSuccessfullyFetchedCommitContextAllFrames(analytics.Event): - type = "integrations.successfully_fetched_commit_context_all_frames" - - attributes = ( - analytics.Attribute("organization_id"), - analytics.Attribute("project_id"), - analytics.Attribute("group_id"), - analytics.Attribute("event_id"), - analytics.Attribute("num_frames", type=int), - analytics.Attribute("num_unique_commits", type=int), - analytics.Attribute("num_unique_commit_authors", type=int), - analytics.Attribute("num_successfully_mapped_frames", type=int), - analytics.Attribute("selected_frame_index", type=int), - analytics.Attribute("selected_provider", type=str), - analytics.Attribute("selected_code_mapping_id"), - ) + organization_id: str + project_id: str + group_id: str + event_id: str + num_frames: int + num_unique_commits: int + num_unique_commit_authors: int + num_successfully_mapped_frames: int + selected_frame_index: int + selected_provider: str + selected_code_mapping_id: str analytics.register(IntegrationsSuccessfullyFetchedCommitContextAllFrames) diff --git a/src/sentry/analytics/events/member_invited.py b/src/sentry/analytics/events/member_invited.py index 0ff2057bc8d9a5..1414471208902e 100644 --- a/src/sentry/analytics/events/member_invited.py +++ b/src/sentry/analytics/events/member_invited.py @@ -1,15 +1,12 @@ from sentry import analytics +@analytics.eventclass("member.invited") class MemberInvitedEvent(analytics.Event): - type = "member.invited" - - attributes = ( - analytics.Attribute("inviter_user_id"), - analytics.Attribute("invited_member_id"), - analytics.Attribute("organization_id"), - analytics.Attribute("referrer", required=False), - ) + inviter_user_id: str + invited_member_id: str + organization_id: str + referrer: str | None = None analytics.register(MemberInvitedEvent) diff --git a/src/sentry/analytics/events/onboarding_continuation_sent.py b/src/sentry/analytics/events/onboarding_continuation_sent.py index 904aa331158f85..f07b4dda91c39f 100644 --- a/src/sentry/analytics/events/onboarding_continuation_sent.py +++ b/src/sentry/analytics/events/onboarding_continuation_sent.py @@ -1,14 +1,11 @@ from sentry import analytics +@analytics.eventclass("onboarding_continuation.sent") class OnboardingContinuationSent(analytics.Event): - type = "onboarding_continuation.sent" - - attributes = ( - analytics.Attribute("user_id"), - analytics.Attribute("organization_id"), - analytics.Attribute("providers"), - ) + user_id: str + organization_id: str + providers: str analytics.register(OnboardingContinuationSent) diff --git a/src/sentry/analytics/events/project_transferred.py b/src/sentry/analytics/events/project_transferred.py index 4eb4c3278b254f..22bda706cb4fcd 100644 --- a/src/sentry/analytics/events/project_transferred.py +++ b/src/sentry/analytics/events/project_transferred.py @@ -1,15 +1,12 @@ from sentry import analytics +@analytics.eventclass("project.transferred") class ProjectTransferredEvent(analytics.Event): - type = "project.transferred" - - attributes = ( - analytics.Attribute("old_organization_id"), - analytics.Attribute("new_organization_id"), - analytics.Attribute("project_id"), - analytics.Attribute("platform", required=False), - ) + old_organization_id: str + new_organization_id: str + project_id: str + platform: str | None = None analytics.register(ProjectTransferredEvent) diff --git a/src/sentry/analytics/events/release_get_previous_commits.py b/src/sentry/analytics/events/release_get_previous_commits.py index bbf84139bced14..68219915942fec 100644 --- a/src/sentry/analytics/events/release_get_previous_commits.py +++ b/src/sentry/analytics/events/release_get_previous_commits.py @@ -1,15 +1,12 @@ from sentry import analytics +@analytics.eventclass("release.get_previous_commits") class ReleaseGetPreviousCommitsEvent(analytics.Event): - type = "release.get_previous_commits" - - attributes = ( - analytics.Attribute("user_id", required=False), - analytics.Attribute("organization_id"), - analytics.Attribute("project_ids"), - analytics.Attribute("user_agent", required=False), - ) + user_id: str | None = None + organization_id: str + project_ids: str + user_agent: str | None = None analytics.register(ReleaseGetPreviousCommitsEvent) diff --git a/src/sentry/analytics/events/release_set_commits.py b/src/sentry/analytics/events/release_set_commits.py index 4017ba27608fdb..30a4758ef1b70f 100644 --- a/src/sentry/analytics/events/release_set_commits.py +++ b/src/sentry/analytics/events/release_set_commits.py @@ -1,15 +1,12 @@ from sentry import analytics +@analytics.eventclass("release.set_commits_local") class ReleaseSetCommitsLocalEvent(analytics.Event): - type = "release.set_commits_local" - - attributes = ( - analytics.Attribute("user_id", required=False), - analytics.Attribute("organization_id"), - analytics.Attribute("project_ids"), - analytics.Attribute("user_agent", required=False), - ) + user_id: str | None = None + organization_id: str + project_ids: str + user_agent: str | None = None analytics.register(ReleaseSetCommitsLocalEvent) diff --git a/src/sentry/analytics/events/webhook_repository_created.py b/src/sentry/analytics/events/webhook_repository_created.py index 002b00c7086e3b..ea7e0c9a4f41c5 100644 --- a/src/sentry/analytics/events/webhook_repository_created.py +++ b/src/sentry/analytics/events/webhook_repository_created.py @@ -1,14 +1,11 @@ from sentry import analytics +@analytics.eventclass("webhook.repository_created") class WebHookRepositoryCreatedEvent(analytics.Event): - type = "webhook.repository_created" - - attributes = ( - analytics.Attribute("organization_id"), - analytics.Attribute("repository_id"), - analytics.Attribute("integration"), - ) + organization_id: str + repository_id: str + integration: str analytics.register(WebHookRepositoryCreatedEvent) diff --git a/src/sentry/api/base.py b/src/sentry/api/base.py index 7797d08868d74f..6ce8fa13193f66 100644 --- a/src/sentry/api/base.py +++ b/src/sentry/api/base.py @@ -24,6 +24,7 @@ from sentry_sdk import Scope from sentry import analytics, options, tsdb +from sentry.analytics.events.release_set_commits import ReleaseSetCommitsLocalEvent from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.exceptions import StaffRequired, SuperuserRequired @@ -648,11 +649,12 @@ def _parse_resolution(self, value: str) -> int: class ReleaseAnalyticsMixin: def track_set_commits_local(self, request: Request, organization_id=None, project_ids=None): analytics.record( - "release.set_commits_local", - user_id=request.user.id if request.user and request.user.id else None, - organization_id=organization_id, - project_ids=project_ids, - user_agent=request.META.get("HTTP_USER_AGENT", ""), + ReleaseSetCommitsLocalEvent( + user_id=request.user.id if request.user and request.user.id else None, + organization_id=organization_id, + project_ids=project_ids, + user_agent=request.META.get("HTTP_USER_AGENT", ""), + ) ) diff --git a/src/sentry/api/endpoints/codeowners/index.py b/src/sentry/api/endpoints/codeowners/index.py index c511c72a759e7c..efe40333e4c9d5 100644 --- a/src/sentry/api/endpoints/codeowners/index.py +++ b/src/sentry/api/endpoints/codeowners/index.py @@ -4,6 +4,7 @@ from rest_framework.response import Response from sentry import analytics +from sentry.analytics.events.codeowners_created import CodeownersCreated from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import region_silo_endpoint @@ -108,11 +109,12 @@ def post(self, request: Request, project: Project) -> Response: self.track_response_code("create", status.HTTP_201_CREATED) user_id = getattr(request.user, "id", None) or None analytics.record( - "codeowners.created", - user_id=user_id, - organization_id=project.organization_id, - project_id=project.id, - codeowners_id=project_codeowners.id, + CodeownersCreated( + user_id=user_id, + organization_id=project.organization_id, + project_id=project.id, + codeowners_id=project_codeowners.id, + ) ) expand = ["ownershipSyntax", "errors", "hasTargetingContext"] diff --git a/src/sentry/api/endpoints/group_tagkey_values.py b/src/sentry/api/endpoints/group_tagkey_values.py index f63c7ca3799334..beaefb3306319a 100644 --- a/src/sentry/api/endpoints/group_tagkey_values.py +++ b/src/sentry/api/endpoints/group_tagkey_values.py @@ -3,6 +3,7 @@ from rest_framework.response import Response from sentry import analytics, tagstore +from sentry.analytics.events.eventuser_endpoint_request import EventUserEndpointRequest from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import region_silo_endpoint @@ -68,9 +69,10 @@ def get(self, request: Request, group, key) -> Response: List a Tag's Values """ analytics.record( - "eventuser_endpoint.request", - project_id=group.project_id, - endpoint="sentry.api.endpoints.group_tagkey_values.get", + EventUserEndpointRequest( + project_id=group.project_id, + endpoint="sentry.api.endpoints.group_tagkey_values.get", + ) ) lookup_key = tagstore.backend.prefix_reserved_key(key) diff --git a/src/sentry/api/endpoints/organization_onboarding_continuation_email.py b/src/sentry/api/endpoints/organization_onboarding_continuation_email.py index c367220c42ef0e..6d4729a5f7dc60 100644 --- a/src/sentry/api/endpoints/organization_onboarding_continuation_email.py +++ b/src/sentry/api/endpoints/organization_onboarding_continuation_email.py @@ -3,6 +3,7 @@ from rest_framework.request import Request from sentry import analytics +from sentry.analytics.events.onboarding_continuation_sent import OnboardingContinuationSent from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import region_silo_endpoint @@ -64,9 +65,10 @@ def post(self, request: Request, organization: Organization): ) msg.send_async([request.user.email]) analytics.record( - "onboarding_continuation.sent", - organization_id=organization.id, - user_id=request.user.id, - providers="email", + OnboardingContinuationSent( + organization_id=organization.id, + user_id=request.user.id, + providers="email", + ) ) return self.respond(status=202) diff --git a/src/sentry/api/endpoints/project_users.py b/src/sentry/api/endpoints/project_users.py index fffd5df8ac17b0..70714ef3c87d65 100644 --- a/src/sentry/api/endpoints/project_users.py +++ b/src/sentry/api/endpoints/project_users.py @@ -2,6 +2,7 @@ from rest_framework.response import Response from sentry import analytics +from sentry.analytics.events.eventuser_endpoint_request import EventUserEndpointRequest from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import region_silo_endpoint from sentry.api.bases.project import ProjectAndStaffPermission, ProjectEndpoint @@ -41,9 +42,10 @@ def get(self, request: Request, project) -> Response: For example, ``query=email:foo@example.com`` """ analytics.record( - "eventuser_endpoint.request", - project_id=project.id, - endpoint="sentry.api.endpoints.project_users.get", + EventUserEndpointRequest( + project_id=project.id, + endpoint="sentry.api.endpoints.project_users.get", + ) ) field, identifier = None, None if request.GET.get("query"): diff --git a/src/sentry/incidents/action_handlers.py b/src/sentry/incidents/action_handlers.py index 1ea632efe8f219..9a56e65988f035 100644 --- a/src/sentry/incidents/action_handlers.py +++ b/src/sentry/incidents/action_handlers.py @@ -12,6 +12,7 @@ from django.urls import reverse from sentry import analytics, features +from sentry.analytics.events.alert_sent import AlertSentEvent from sentry.api.serializers import serialize from sentry.charts.types import ChartSize from sentry.constants import CRASH_RATE_ALERT_AGGREGATE_ALIAS @@ -103,14 +104,15 @@ def record_alert_sent_analytics( notification_uuid: str | None = None, ) -> None: analytics.record( - "alert.sent", - organization_id=organization_id, - project_id=project_id, - provider=self.provider, - alert_id=alert_id, - alert_type="metric_alert", - external_id=str(external_id) if external_id is not None else "", - notification_uuid=notification_uuid or "", + AlertSentEvent( + organization_id=organization_id, + project_id=project_id, + provider=self.provider, + alert_id=alert_id, + alert_type="metric_alert", + external_id=str(external_id) if external_id is not None else "", + notification_uuid=notification_uuid or "", + ) ) diff --git a/src/sentry/integrations/github/webhook.py b/src/sentry/integrations/github/webhook.py index 9af746c06eb253..a97bac86555fd2 100644 --- a/src/sentry/integrations/github/webhook.py +++ b/src/sentry/integrations/github/webhook.py @@ -17,6 +17,7 @@ from django.views.decorators.csrf import csrf_exempt from sentry import analytics, options +from sentry.analytics.events.webhook_repository_created import WebHookRepositoryCreatedEvent from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import Endpoint, all_silo_endpoint @@ -140,10 +141,11 @@ def __call__(self, event: Mapping[str, Any], **kwargs) -> None: continue analytics.record( - "webhook.repository_created", - organization_id=org.id, - repository_id=repo.id, - integration=IntegrationProviderSlug.GITHUB.value, + WebHookRepositoryCreatedEvent( + organization_id=org.id, + repository_id=repo.id, + integration=IntegrationProviderSlug.GITHUB.value, + ) ) metrics.incr("github.webhook.repository_created") diff --git a/src/sentry/integrations/services/integration/impl.py b/src/sentry/integrations/services/integration/impl.py index e42622ac6aec02..80472c4f78c517 100644 --- a/src/sentry/integrations/services/integration/impl.py +++ b/src/sentry/integrations/services/integration/impl.py @@ -8,6 +8,9 @@ from django.utils import timezone from sentry import analytics +from sentry.analytics.events.alert_rule_ui_component_webhook_sent import ( + AlertRuleUiComponentWebhookSentEvent, +) from sentry.api.paginator import OffsetPaginator from sentry.constants import SentryAppInstallationStatus from sentry.hybridcloud.rpc.pagination import RpcPaginationArgs, RpcPaginationResult @@ -422,10 +425,11 @@ def send_incident_alert_notification( if alert_rule_action_ui_component: analytics.record( - "alert_rule_ui_component_webhook.sent", - organization_id=organization_id, - sentry_app_id=sentry_app.id, - event=f"{app_platform_event.resource}.{app_platform_event.action}", + AlertRuleUiComponentWebhookSentEvent( + organization_id=organization_id, + sentry_app_id=sentry_app.id, + event=f"{app_platform_event.resource}.{app_platform_event.action}", + ) ) return alert_rule_action_ui_component diff --git a/src/sentry/integrations/utils/commit_context.py b/src/sentry/integrations/utils/commit_context.py index 4a19e4fb13eb99..5cb34894304450 100644 --- a/src/sentry/integrations/utils/commit_context.py +++ b/src/sentry/integrations/utils/commit_context.py @@ -9,6 +9,10 @@ from django.utils.datastructures import OrderedSet from sentry import analytics +from sentry.analytics.events.integration_commit_context_all_frames import ( + IntegrationsFailedToFetchCommitContextAllFrames, + IntegrationsSuccessfullyFetchedCommitContextAllFrames, +) from sentry.constants import ObjectStatus from sentry.integrations.base import IntegrationInstallation from sentry.integrations.models.repository_project_path_config import RepositoryProjectPathConfig @@ -362,14 +366,15 @@ def _record_commit_context_all_frames_analytics( }, ) analytics.record( - "integrations.failed_to_fetch_commit_context_all_frames", - organization_id=organization_id, - project_id=project_id, - group_id=extra["group"], - event_id=extra["event"], - num_frames=len(frames), - num_successfully_mapped_frames=num_successfully_mapped_frames, - reason=reason, + IntegrationsFailedToFetchCommitContextAllFrames( + organization_id=organization_id, + project_id=project_id, + group_id=extra["group"], + event_id=extra["event"], + num_frames=len(frames), + num_successfully_mapped_frames=num_successfully_mapped_frames, + reason=reason, + ) ) return @@ -392,18 +397,19 @@ def _record_commit_context_all_frames_analytics( ) analytics.record( - "integrations.successfully_fetched_commit_context_all_frames", - organization_id=organization_id, - project_id=project_id, - group_id=extra["group"], - event_id=extra["event"], - num_frames=len(frames), - num_unique_commits=len(unique_commit_ids), - num_unique_commit_authors=len(unique_author_emails), - num_successfully_mapped_frames=num_successfully_mapped_frames, - selected_frame_index=selected_frame_index, - selected_provider=selected_provider, - selected_code_mapping_id=selected_blame.code_mapping.id, + IntegrationsSuccessfullyFetchedCommitContextAllFrames( + organization_id=organization_id, + project_id=project_id, + group_id=extra["group"], + event_id=extra["event"], + num_frames=len(frames), + num_unique_commits=len(unique_commit_ids), + num_unique_commit_authors=len(unique_author_emails), + num_successfully_mapped_frames=num_successfully_mapped_frames, + selected_frame_index=selected_frame_index, + selected_provider=selected_provider, + selected_code_mapping_id=selected_blame.code_mapping.id, + ) ) diff --git a/src/sentry/issues/endpoints/organization_release_previous_commits.py b/src/sentry/issues/endpoints/organization_release_previous_commits.py index d09e70ea22fe51..27dbd4b68543f1 100644 --- a/src/sentry/issues/endpoints/organization_release_previous_commits.py +++ b/src/sentry/issues/endpoints/organization_release_previous_commits.py @@ -2,6 +2,7 @@ from rest_framework.response import Response from sentry import analytics +from sentry.analytics.events.release_get_previous_commits import ReleaseGetPreviousCommitsEvent from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import region_silo_endpoint @@ -57,11 +58,12 @@ def get(self, request: Request, organization: Organization, version: str) -> Res ) analytics.record( - "release.get_previous_commits", - user_id=request.user.id if request.user and request.user.id else None, - organization_id=organization.id, - project_ids=[project.id for project in release.projects.all()], - user_agent=request.META.get("HTTP_USER_AGENT", ""), + ReleaseGetPreviousCommitsEvent( + user_id=request.user.id if request.user and request.user.id else None, + organization_id=organization.id, + project_ids=[project.id for project in release.projects.all()], + user_agent=request.META.get("HTTP_USER_AGENT", ""), + ) ) if not prev_release_with_commits: diff --git a/src/sentry/monitors/processing_errors/manager.py b/src/sentry/monitors/processing_errors/manager.py index ed29dbf676f140..30f9712e630807 100644 --- a/src/sentry/monitors/processing_errors/manager.py +++ b/src/sentry/monitors/processing_errors/manager.py @@ -11,6 +11,7 @@ from rediscluster import RedisCluster from sentry import analytics +from sentry.analytics.events.checkin_processing_error_stored import CheckinProcessingErrorStored from sentry.models.organization import Organization from sentry.models.project import Project from sentry.monitors.models import Monitor @@ -193,11 +194,14 @@ def handle_processing_errors(item: CheckinItem, error: ProcessingErrorsException if random.random() < ANALYTICS_SAMPLING_RATE: analytics.record( - "checkin_processing_error.stored", - organization_id=organization.id, - project_id=project.id, - monitor_slug=item.payload["monitor_slug"], - error_types=[process_error["type"] for process_error in error.processing_errors], + CheckinProcessingErrorStored( + organization_id=organization.id, + project_id=project.id, + monitor_slug=item.payload["monitor_slug"], + error_types=[ + process_error["type"] for process_error in error.processing_errors + ], + ) ) checkin_processing_error = CheckinProcessingError(error.processing_errors, item) diff --git a/src/sentry/notifications/notifications/digest.py b/src/sentry/notifications/notifications/digest.py index 38d38972c6a1ce..531d22117fc4c3 100644 --- a/src/sentry/notifications/notifications/digest.py +++ b/src/sentry/notifications/notifications/digest.py @@ -7,6 +7,7 @@ from urllib.parse import urlencode from sentry import analytics, features +from sentry.analytics.events.alert_sent import AlertSentEvent from sentry.db.models import Model from sentry.digests.notifications import DigestInfo from sentry.digests.utils import ( @@ -255,12 +256,13 @@ def record_notification_sent(self, recipient: Actor, provider: ExternalProviders super().record_notification_sent(recipient, provider) log_params = self.get_log_params(recipient) analytics.record( - "alert.sent", - organization_id=self.organization.id, - project_id=self.project.id, - provider=provider.name, - alert_id=log_params["alert_id"] if log_params["alert_id"] else "", - alert_type="issue_alert", - external_id=str(recipient.id), - notification_uuid=self.notification_uuid, + AlertSentEvent( + organization_id=self.organization.id, + project_id=self.project.id, + provider=provider.name, + alert_id=log_params["alert_id"] if log_params["alert_id"] else "", + alert_type="issue_alert", + external_id=str(recipient.id), + notification_uuid=self.notification_uuid, + ) ) diff --git a/src/sentry/notifications/notifications/rules.py b/src/sentry/notifications/notifications/rules.py index a0739f303b0dbd..da721f558a1f4a 100644 --- a/src/sentry/notifications/notifications/rules.py +++ b/src/sentry/notifications/notifications/rules.py @@ -7,6 +7,7 @@ from typing import Any from sentry import analytics, features +from sentry.analytics.events.alert_sent import AlertSentEvent from sentry.db.models import Model from sentry.eventstore.models import GroupEvent from sentry.integrations.issue_alert_image_builder import IssueAlertImageBuilder @@ -354,12 +355,13 @@ def record_notification_sent(self, recipient: Actor, provider: ExternalProviders super().record_notification_sent(recipient, provider) log_params = self.get_log_params(recipient) analytics.record( - "alert.sent", - organization_id=self.organization.id, - project_id=self.project.id, - provider=provider.name, - alert_id=log_params["alert_id"] if log_params["alert_id"] else "", - alert_type="issue_alert", - external_id=str(recipient.id), - notification_uuid=self.notification_uuid, + AlertSentEvent( + organization_id=self.organization.id, + project_id=self.project.id, + provider=provider.name, + alert_id=log_params["alert_id"] if log_params["alert_id"] else "", + alert_type="issue_alert", + external_id=str(recipient.id), + notification_uuid=self.notification_uuid, + ) ) diff --git a/src/sentry/receivers/onboarding.py b/src/sentry/receivers/onboarding.py index f2ad6b7158cd74..887c9f3b891ba6 100644 --- a/src/sentry/receivers/onboarding.py +++ b/src/sentry/receivers/onboarding.py @@ -7,6 +7,11 @@ from django.db.models import F from sentry import analytics +from sentry.analytics.events.first_cron_checkin_sent import FirstCronCheckinSent +from sentry.analytics.events.first_insight_span_sent import FirstInsightSpanSentEvent +from sentry.analytics.events.first_transaction_sent import FirstTransactionSentEvent +from sentry.analytics.events.member_invited import MemberInvitedEvent +from sentry.analytics.events.project_transferred import ProjectTransferredEvent from sentry.integrations.base import IntegrationDomain, get_integration_types from sentry.integrations.services.integration import RpcIntegration, integration_service from sentry.models.organization import Organization @@ -171,11 +176,12 @@ def record_first_transaction(project, event, **kwargs): date_completed=event.datetime, ) analytics.record( - "first_transaction.sent", - default_user_id=get_owner_id(project), - organization_id=project.organization_id, - project_id=project.id, - platform=project.platform, + FirstTransactionSentEvent( + default_user_id=get_owner_id(project), + organization_id=project.organization_id, + project_id=project.id, + platform=project.platform, + ) ) @@ -272,11 +278,12 @@ def record_cron_monitor_created(project, user, from_upsert, **kwargs): ) def record_first_cron_checkin(project, monitor_id, **kwargs): analytics.record( - "first_cron_checkin.sent", - user_id=get_owner_id(project), - organization_id=project.organization_id, - project_id=project.id, - monitor_id=monitor_id, + FirstCronCheckinSent( + user_id=get_owner_id(project), + organization_id=project.organization_id, + project_id=project.id, + monitor_id=monitor_id, + ) ) @@ -285,12 +292,13 @@ def record_first_cron_checkin(project, monitor_id, **kwargs): ) def record_first_insight_span(project, module, **kwargs): analytics.record( - "first_insight_span.sent", - user_id=get_owner_id(project), - organization_id=project.organization_id, - project_id=project.id, - platform=project.platform, - module=module, + FirstInsightSpanSentEvent( + user_id=get_owner_id(project), + organization_id=project.organization_id, + project_id=project.id, + platform=project.platform, + module=module, + ) ) @@ -303,11 +311,12 @@ def record_member_invited(member, user, **kwargs): ) analytics.record( - "member.invited", - invited_member_id=member.id, - inviter_user_id=user.id if user else None, - organization_id=member.organization_id, - referrer=kwargs.get("referrer"), + MemberInvitedEvent( + invited_member_id=member.id, + inviter_user_id=user.id if user else None, + organization_id=member.organization_id, + referrer=kwargs.get("referrer"), + ) ) @@ -475,11 +484,12 @@ def record_integration_added( def record_project_transferred(old_org_id: int, project: Project, **kwargs): analytics.record( - "project.transferred", - old_organization_id=old_org_id, - new_organization_id=project.organization.id, - project_id=project.id, - platform=project.platform, + ProjectTransferredEvent( + old_organization_id=old_org_id, + new_organization_id=project.organization.id, + project_id=project.id, + platform=project.platform, + ) ) transfer_onboarding_tasks( diff --git a/src/sentry/rules/actions/integrations/base.py b/src/sentry/rules/actions/integrations/base.py index e805b9866fbee1..83a5867fe34329 100644 --- a/src/sentry/rules/actions/integrations/base.py +++ b/src/sentry/rules/actions/integrations/base.py @@ -7,6 +7,7 @@ import sentry_sdk from sentry import analytics +from sentry.analytics.events.alert_sent import AlertSentEvent from sentry.eventstore.models import GroupEvent from sentry.integrations.services.integration import ( RpcIntegration, @@ -121,12 +122,13 @@ def record_notification_sent( alert_id=rule.id if rule else None, ) analytics.record( - "alert.sent", - provider=self.provider, - alert_id=rule.id if rule else "", - alert_type="issue_alert", - organization_id=event.organization.id, - project_id=event.project_id, - external_id=external_id, - notification_uuid=notification_uuid if notification_uuid else "", + AlertSentEvent( + provider=self.provider, + alert_id=rule.id if rule else "", + alert_type="issue_alert", + organization_id=event.organization.id, + project_id=event.project_id, + external_id=external_id, + notification_uuid=notification_uuid if notification_uuid else "", + ) ) diff --git a/src/sentry/sentry_apps/tasks/sentry_apps.py b/src/sentry/sentry_apps/tasks/sentry_apps.py index 1ab3a6fd96d3d9..6512f8c2cb692a 100644 --- a/src/sentry/sentry_apps/tasks/sentry_apps.py +++ b/src/sentry/sentry_apps/tasks/sentry_apps.py @@ -10,6 +10,9 @@ from requests.exceptions import RequestException from sentry import analytics, features, nodestore +from sentry.analytics.events.alert_rule_ui_component_webhook_sent import ( + AlertRuleUiComponentWebhookSentEvent, +) from sentry.api.serializers import serialize from sentry.api.serializers.models.group import BaseGroupSerializerResponse from sentry.constants import SentryAppInstallationStatus @@ -242,10 +245,11 @@ def send_alert_webhook_v2( # On success, record analytic event for Alert Rule UI Component if request_data.data.get("issue_alert"): analytics.record( - "alert_rule_ui_component_webhook.sent", - organization_id=organization.id, - sentry_app_id=sentry_app_id, - event=SentryAppEventType.EVENT_ALERT_TRIGGERED, + AlertRuleUiComponentWebhookSentEvent( + organization_id=organization.id, + sentry_app_id=sentry_app_id, + event=SentryAppEventType.EVENT_ALERT_TRIGGERED, + ) ) diff --git a/src/sentry/tasks/commit_context.py b/src/sentry/tasks/commit_context.py index 8f8901943eb8a0..cc887f9036bb38 100644 --- a/src/sentry/tasks/commit_context.py +++ b/src/sentry/tasks/commit_context.py @@ -10,6 +10,9 @@ from sentry_sdk import set_tag from sentry import analytics +from sentry.analytics.events.integration_commit_context_all_frames import ( + IntegrationsFailedToFetchCommitContextAllFrames, +) from sentry.api.serializers.models.release import get_users_for_authors from sentry.integrations.source_code_management.commit_context import CommitContextIntegration from sentry.integrations.utils.commit_context import ( @@ -121,14 +124,15 @@ def process_commit_context( sdk_name=sdk_name, ) analytics.record( - "integrations.failed_to_fetch_commit_context_all_frames", - organization_id=project.organization_id, - project_id=project_id, - group_id=basic_logging_details["group"], - event_id=basic_logging_details["event"], - num_frames=0, - num_successfully_mapped_frames=0, - reason="could_not_find_in_app_stacktrace_frame", + IntegrationsFailedToFetchCommitContextAllFrames( + organization_id=project.organization_id, + project_id=project_id, + group_id=basic_logging_details["group"], + event_id=basic_logging_details["event"], + num_frames=0, + num_successfully_mapped_frames=0, + reason="could_not_find_in_app_stacktrace_frame", + ) ) return diff --git a/src/sentry/tasks/unmerge.py b/src/sentry/tasks/unmerge.py index 3afb03d2aa4c3d..680913b781edbd 100644 --- a/src/sentry/tasks/unmerge.py +++ b/src/sentry/tasks/unmerge.py @@ -11,6 +11,7 @@ from django.db.models.base import Model from sentry import analytics, eventstore, features, similarity, tsdb +from sentry.analytics.events.eventuser_endpoint_request import EventUserEndpointRequest from sentry.constants import DEFAULT_LOGGER_NAME, LOG_LEVELS_MAP from sentry.culprit import generate_culprit from sentry.eventstore.models import BaseEvent @@ -380,9 +381,10 @@ def repair_group_release_data(caches, project, events): def get_event_user_from_interface(value, project): analytics.record( - "eventuser_endpoint.request", - project_id=project.id, - endpoint="sentry.tasks.unmerge.get_event_user_from_interface", + EventUserEndpointRequest( + project_id=project.id, + endpoint="sentry.tasks.unmerge.get_event_user_from_interface", + ) ) return EventUser( user_ident=value.get("id"),