Skip to content

feat(issues): infer project platform if not set #95402

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion src/sentry/event_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
from django.core.cache import cache
from django.core.exceptions import ValidationError
from django.db import IntegrityError, OperationalError, connection, router, transaction
from django.db.models import Max
from django.db.models import Max, Q
from django.db.models.signals import post_save
from django.utils.encoding import force_str
from urllib3.exceptions import MaxRetryError, TimeoutError
from usageaccountant import UsageUnit

from sentry import (
audit_log,
eventstore,
eventstream,
eventtypes,
Expand All @@ -37,6 +38,7 @@
LOG_LEVELS_MAP,
MAX_TAG_VALUE_LENGTH,
PLACEHOLDER_EVENT_TITLES,
VALID_PLATFORMS,
DataCategory,
InsightModules,
)
Expand Down Expand Up @@ -127,6 +129,7 @@
from sentry.types.group import GroupSubStatus, PriorityLevel
from sentry.usage_accountant import record
from sentry.utils import metrics
from sentry.utils.audit import create_system_audit_entry
from sentry.utils.cache import cache_key_for_event
from sentry.utils.circuit_breaker import (
ERROR_COUNT_CACHE_KEY,
Expand All @@ -137,6 +140,7 @@
from sentry.utils.event import has_event_minified_stack_trace, has_stacktrace, is_handled
from sentry.utils.eventuser import EventUser
from sentry.utils.metrics import MutableTags
from sentry.utils.options import sample_modulo
from sentry.utils.outcomes import Outcome, track_outcome
from sentry.utils.projectflags import set_project_flag_and_signal
from sentry.utils.safe import get_path, safe_execute, setdefault_path, trim
Expand Down Expand Up @@ -464,6 +468,11 @@ def save(
# After calling _pull_out_data we get some keys in the job like the platform
_pull_out_data([job], projects)

# Sometimes projects get created without a platform (e.g. through the API), in which case we
# attempt to set it based on the first event
if sample_modulo("sentry:infer_project_platform", project.id):
_set_project_platform_if_needed(project, job["event"])

event_type = self._data.get("type")
if event_type == "transaction":
job["data"]["project"] = project.id
Expand Down Expand Up @@ -690,6 +699,26 @@ def _pull_out_data(jobs: Sequence[Job], projects: ProjectsMapping) -> None:
job["groups"] = []


def _set_project_platform_if_needed(project: Project, event: Event) -> None:
if project.platform:
return

if event.platform not in VALID_PLATFORMS or event.get_tag("sample_event") == "yes":
return
Comment on lines +703 to +707
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Could combine this into the same if


updated = Project.objects.filter(
Q(id=project.id) & (Q(platform__isnull=True) | Q(platform=""))
).update(platform=event.platform)

if updated:
create_system_audit_entry(
organization=project.organization,
target_object=project.id,
event=audit_log.get_event_id("PROJECT_EDIT"),
data={**project.get_audit_log_data(), "platform": event.platform},
)
Comment on lines +709 to +719
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth try/catch around this so that if anything fails we don't crash out



@sentry_sdk.tracing.trace
def _get_or_create_release_many(jobs: Sequence[Job], projects: ProjectsMapping) -> None:
for job in jobs:
Expand Down
8 changes: 8 additions & 0 deletions src/sentry/options/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -3480,3 +3480,11 @@
default=False,
flags=FLAG_AUTOMATOR_MODIFIABLE,
)

# Rollout for inferring project platform from events received
register(
"sentry:infer_project_platform",
type=Float,
default=0.0,
flags=FLAG_AUTOMATOR_MODIFIABLE,
)
23 changes: 23 additions & 0 deletions tests/sentry/event_manager/test_project_platform_infer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from sentry.testutils.cases import TestCase
from sentry.testutils.helpers import override_options
from sentry.testutils.helpers.eventprocessing import save_new_event


class ProjectPlatformInferTest(TestCase):
@override_options({"sentry:infer_project_platform": 1.0})
def test_platform_inferred_on_event(self):
project = self.create_project()

save_new_event({"message": "test", "platform": "javascript"}, project)

project.refresh_from_db()
assert project.platform == "javascript"

@override_options({"sentry:infer_project_platform": 1.0})
def test_platform_does_not_override_existing_platform(self):
project = self.create_project(platform="python")

save_new_event({"message": "test", "platform": "javascript"}, project)

project.refresh_from_db()
assert project.platform == "python"
Loading