From d11d80d08eee3879fbb0e07eb151cb352ed591e5 Mon Sep 17 00:00:00 2001 From: Armen Zambrano G <44410+armenzg@users.noreply.github.com> Date: Fri, 25 Apr 2025 15:23:48 -0400 Subject: [PATCH] fix(stacktrace-link): Use new Java logic The stacktrace-link endpoint uses the original munging logic which has a bug. This change switches to the new logic and its gated behind a feature flag. --- src/sentry/features/temporary.py | 2 ++ .../integrations/utils/stacktrace_link.py | 3 ++ .../auto_source_code_config/code_mapping.py | 13 ++++++++ .../endpoints/project_stacktrace_link.py | 2 +- .../test_code_mapping.py | 31 +++++++++++++++++++ 5 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/sentry/features/temporary.py b/src/sentry/features/temporary.py index e29c62b9b21b52..c181cc3dca3e14 100644 --- a/src/sentry/features/temporary.py +++ b/src/sentry/features/temporary.py @@ -118,6 +118,8 @@ def register_temporary_features(manager: FeatureManager): manager.add("organizations:ds-org-recalibration", OrganizationFeature, FeatureHandlerStrategy.INTERNAL, api_expose=False) # Enable custom dynamic sampling rates manager.add("organizations:dynamic-sampling-custom", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True) + # Enable new munging logic for java frames + manager.add("organizations:java-frame-munging-new-logic", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True) # Enable issue platform deletion manager.add("organizations:issue-platform-deletion", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True) # Enable archive/escalating issue workflow features in v2 diff --git a/src/sentry/integrations/utils/stacktrace_link.py b/src/sentry/integrations/utils/stacktrace_link.py index fef2ce43e88df4..c16e8fc5b72662 100644 --- a/src/sentry/integrations/utils/stacktrace_link.py +++ b/src/sentry/integrations/utils/stacktrace_link.py @@ -10,6 +10,7 @@ from sentry.issues.auto_source_code_config.code_mapping import ( convert_stacktrace_frame_path_to_source_path, ) +from sentry.models.organization import Organization from sentry.models.repository import Repository from sentry.shared_integrations.exceptions import ApiError from sentry.utils.event_frames import EventFrame @@ -83,6 +84,7 @@ class StacktraceLinkOutcome(TypedDict): def get_stacktrace_config( configs: list[RepositoryProjectPathConfig], ctx: StacktraceLinkContext, + organization: Organization | None = None, ) -> StacktraceLinkOutcome: result: StacktraceLinkOutcome = { "source_url": None, @@ -97,6 +99,7 @@ def get_stacktrace_config( platform=ctx["platform"], sdk_name=ctx["sdk_name"], code_mapping=config, + organization=organization, ) result["src_path"] = src_path if not src_path: diff --git a/src/sentry/issues/auto_source_code_config/code_mapping.py b/src/sentry/issues/auto_source_code_config/code_mapping.py index b51f22e2594c0c..4ef14be36d33ae 100644 --- a/src/sentry/issues/auto_source_code_config/code_mapping.py +++ b/src/sentry/issues/auto_source_code_config/code_mapping.py @@ -5,6 +5,7 @@ from collections.abc import Mapping, Sequence from typing import Any, NamedTuple +from sentry import features from sentry.integrations.models.repository_project_path_config import RepositoryProjectPathConfig from sentry.integrations.source_code_management.repo_trees import ( RepoAndBranch, @@ -386,6 +387,7 @@ def convert_stacktrace_frame_path_to_source_path( code_mapping: RepositoryProjectPathConfig, platform: str | None, sdk_name: str | None, + organization: Organization | None = None, ) -> str | None: """ Applies the given code mapping to the given stacktrace frame and returns the source path. @@ -403,6 +405,17 @@ def convert_stacktrace_frame_path_to_source_path( try_munge_frame_path(frame=frame, platform=platform, sdk_name=sdk_name) or frame.filename ) + has_new_logic = ( + features.has("organizations:java-frame-munging-new-logic", organization, actor=None) + if organization + else False + ) + if has_new_logic and platform == "java" and frame.module and frame.abs_path: + try: + _, stacktrace_path = get_path_from_module(frame.module, frame.abs_path) + except Exception: + logger.warning("Investigate. Falling back to old logic.", exc_info=True) + if stacktrace_path and stacktrace_path.startswith(code_mapping.stack_root): return ( stacktrace_path.replace(stack_root, code_mapping.source_root, 1) diff --git a/src/sentry/issues/endpoints/project_stacktrace_link.py b/src/sentry/issues/endpoints/project_stacktrace_link.py index f340d31afa74b5..5fc9d9a082e7af 100644 --- a/src/sentry/issues/endpoints/project_stacktrace_link.py +++ b/src/sentry/issues/endpoints/project_stacktrace_link.py @@ -153,7 +153,7 @@ def get(self, request: Request, project: Project) -> Response: scope = Scope.get_isolation_scope() set_top_tags(scope, project, ctx, len(configs) > 0) - result = get_stacktrace_config(configs, ctx) + result = get_stacktrace_config(configs, ctx, project.organization) error = result["error"] src_path = result["src_path"] # Post-processing before exiting scope context diff --git a/tests/sentry/issues/auto_source_code_config/test_code_mapping.py b/tests/sentry/issues/auto_source_code_config/test_code_mapping.py index 6a770cbbafdc41..c88f0151dd35bd 100644 --- a/tests/sentry/issues/auto_source_code_config/test_code_mapping.py +++ b/tests/sentry/issues/auto_source_code_config/test_code_mapping.py @@ -26,6 +26,7 @@ ) from sentry.silo.base import SiloMode from sentry.testutils.cases import TestCase +from sentry.testutils.helpers import Feature from sentry.testutils.silo import assume_test_silo_mode from sentry.utils.event_frames import EventFrame @@ -533,6 +534,11 @@ def test_convert_stacktrace_frame_path_to_source_path_java_no_source_context(sel "MainFragment.java", "src/com/example/vu/android/empowerplant/MainFragment.java", ), + ( + "com.example.foo.BarImpl$invoke$bazFetch$2", + "Bar.kt", # Notice "Impl" is not included in the module above + "src/com/example/foo/BarImpl.kt", # This is incorrect; the new logic will fix this + ), ]: assert ( convert_stacktrace_frame_path_to_source_path( @@ -544,6 +550,31 @@ def test_convert_stacktrace_frame_path_to_source_path_java_no_source_context(sel == expected_path ) + def test_convert_stacktrace_frame_path_to_source_path_java_new_logic(self) -> None: + code_mapping = self.create_code_mapping( + organization_integration=self.oi, + project=self.project, + repo=self.repo, + stack_root="com/example/", + source_root="src/com/example/", + automatically_generated=False, + ) + with Feature({"organizations:java-frame-munging-new-logic": True}): + assert ( + convert_stacktrace_frame_path_to_source_path( + frame=EventFrame( + filename="Baz.java", + abs_path="Baz.java", # Notice "Impl" is not included in the module below + module="com.example.foo.BazImpl$invoke$bazFetch$2", + ), + code_mapping=code_mapping, + platform="java", + sdk_name="sentry.java.android", + organization=self.organization, + ) + == "src/com/example/foo/Baz.java" + ) + def test_convert_stacktrace_frame_path_to_source_path_backslashes(self) -> None: assert ( convert_stacktrace_frame_path_to_source_path(