From f21557788b8e2815737c805df7d0dc3595f32ba6 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Fri, 21 Nov 2025 15:46:11 +0100 Subject: [PATCH] feat(symbolication): Return event_id on error This adds a sentry event ID to the error message when symbolication fails because of an internal error. That should make it easier to track down the specific problem in Sentry. --- src/sentry/lang/java/processing.py | 26 ++---------------- src/sentry/lang/javascript/processing.py | 22 ++------------- src/sentry/lang/native/error.py | 7 ++++- src/sentry/lang/native/processing.py | 24 +++-------------- src/sentry/lang/native/symbolicator.py | 34 ++++++++++++++++++++++-- 5 files changed, 46 insertions(+), 67 deletions(-) diff --git a/src/sentry/lang/java/processing.py b/src/sentry/lang/java/processing.py index b73e5c1ed5dc21..41759d5a88ac31 100644 --- a/src/sentry/lang/java/processing.py +++ b/src/sentry/lang/java/processing.py @@ -5,8 +5,7 @@ from sentry.lang.java.exceptions import Exceptions from sentry.lang.java.utils import JAVA_PLATFORMS, get_jvm_images, get_proguard_images from sentry.lang.java.view_hierarchies import ViewHierarchies -from sentry.lang.native.error import SymbolicationFailed, write_error -from sentry.lang.native.symbolicator import FrameOrder, Symbolicator +from sentry.lang.native.symbolicator import FrameOrder, Symbolicator, handle_response_status from sentry.models.eventerror import EventError from sentry.models.project import Project from sentry.models.release import Release @@ -105,27 +104,6 @@ def _normalize_frame(raw_frame: dict[str, Any], index: int) -> dict[str, Any]: return frame -def _handle_response_status(event_data: Any, response_json: dict[str, Any]) -> bool | None: - """Checks the response from Symbolicator and reports errors. - Returns `True` on success.""" - - if not response_json: - error = SymbolicationFailed(type=EventError.NATIVE_INTERNAL_FAILURE) - elif response_json["status"] == "completed": - return True - elif response_json["status"] == "failed": - error = SymbolicationFailed( - message=response_json.get("message") or None, - type=EventError.NATIVE_SYMBOLICATOR_FAILED, - ) - else: - logger.error("Unexpected symbolicator status: %s", response_json["status"]) - error = SymbolicationFailed(type=EventError.NATIVE_INTERNAL_FAILURE) - - write_error(error, event_data) - return None - - def _get_release_package(project: Project, release_name: str | None) -> str | None: """Gets the release package for the given project and release.""" @@ -225,7 +203,7 @@ def process_jvm_stacktraces(symbolicator: Symbolicator, data: Any) -> Any: frame_order=FrameOrder.caller_first, ) - if not _handle_response_status(data, response): + if not handle_response_status(data, response): return processing_errors = response.get("errors", []) diff --git a/src/sentry/lang/javascript/processing.py b/src/sentry/lang/javascript/processing.py index c21ad13ec9320c..3449a4b68c4b62 100644 --- a/src/sentry/lang/javascript/processing.py +++ b/src/sentry/lang/javascript/processing.py @@ -4,8 +4,7 @@ from sentry.debug_files.artifact_bundles import maybe_renew_artifact_bundles_from_processing from sentry.lang.javascript.utils import JAVASCRIPT_PLATFORMS -from sentry.lang.native.error import SymbolicationFailed, write_error -from sentry.lang.native.symbolicator import FrameOrder, Symbolicator +from sentry.lang.native.symbolicator import FrameOrder, Symbolicator, handle_response_status from sentry.models.eventerror import EventError from sentry.stacktraces.processing import find_stacktraces_in_data from sentry.utils import metrics @@ -74,23 +73,6 @@ def _merge_frame(new_frame, symbolicated): return new_frame -# TODO: Change this error handling to be JS-specific? -def _handle_response_status(event_data, response_json): - if not response_json: - error = SymbolicationFailed(type=EventError.NATIVE_INTERNAL_FAILURE) - elif response_json["status"] == "completed": - return True - elif response_json["status"] == "failed": - error = SymbolicationFailed( - message=response_json.get("message") or None, type=EventError.NATIVE_SYMBOLICATOR_FAILED - ) - else: - logger.error("Unexpected symbolicator status: %s", response_json["status"]) - error = SymbolicationFailed(type=EventError.NATIVE_INTERNAL_FAILURE) - - write_error(error, event_data) - - def is_sourcemap_image(image): return ( bool(image) @@ -257,7 +239,7 @@ def process_js_stacktraces(symbolicator: Symbolicator, data: Any) -> Any: frame_order=FrameOrder.caller_first, ) - if not _handle_response_status(data, response): + if not handle_response_status(data, response): return data used_artifact_bundles = response.get("used_artifact_bundles", []) diff --git a/src/sentry/lang/native/error.py b/src/sentry/lang/native/error.py index fe6bc5e63fbb3c..350e76af9f2d65 100644 --- a/src/sentry/lang/native/error.py +++ b/src/sentry/lang/native/error.py @@ -34,10 +34,11 @@ class SymbolicationFailed(Exception): - def __init__(self, message=None, type=None, obj=None): + def __init__(self, message=None, type=None, event_id=None, obj=None): Exception.__init__(self) self.message = str(message) self.type = type + self.event_id = event_id self.image_name: str | None = None self.image_path: str | None = None if obj is not None: @@ -68,6 +69,8 @@ def is_sdk_failure(self): def get_data(self): """Returns the event data.""" rv = {"message": self.message, "type": self.type} + if self.event_id is not None: + rv["sentry_event_id"] = self.event_id if self.image_path is not None: rv["image_path"] = self.image_path if self.image_uuid is not None: @@ -85,6 +88,8 @@ def __str__(self) -> str: rv.append(" image-uuid=%s" % self.image_uuid) if self.image_name is not None: rv.append(" image-name=%s" % self.image_name) + if self.event_id is not None: + rv.append(" sentry-event-id=%s" % self.event_id) return "".join(rv) diff --git a/src/sentry/lang/native/processing.py b/src/sentry/lang/native/processing.py index 7a523cd006986c..a98c224ad5ec80 100644 --- a/src/sentry/lang/native/processing.py +++ b/src/sentry/lang/native/processing.py @@ -11,7 +11,7 @@ from sentry import options from sentry.lang.native.error import SymbolicationFailed, write_error -from sentry.lang.native.symbolicator import FrameOrder, Symbolicator +from sentry.lang.native.symbolicator import FrameOrder, Symbolicator, handle_response_status from sentry.lang.native.utils import ( get_event_attachment, get_os_from_event, @@ -198,22 +198,6 @@ def _merge_image(raw_image, complete_image, os, data): _handle_image_status(status, raw_image, os, data) -def _handle_response_status(event_data, response_json): - if not response_json: - error = SymbolicationFailed(type=EventError.NATIVE_INTERNAL_FAILURE) - elif response_json["status"] == "completed": - return True - elif response_json["status"] == "failed": - error = SymbolicationFailed( - message=response_json.get("message") or None, type=EventError.NATIVE_SYMBOLICATOR_FAILED - ) - else: - logger.error("Unexpected symbolicator status: %s", response_json["status"]) - error = SymbolicationFailed(type=EventError.NATIVE_INTERNAL_FAILURE) - - write_error(error, event_data) - - def _merge_system_info(data, system_info): set_path(data, "contexts", "os", "type", value="os") # Required by "get_os_from_event" @@ -334,7 +318,7 @@ def process_minidump(symbolicator: Symbolicator, data: Any) -> Any: metrics.incr("process.native.symbolicate.request") response = symbolicator.process_minidump(data.get("platform"), minidump, rewrite_first_module) - if _handle_response_status(data, response): + if handle_response_status(data, response): _merge_full_response(data, response) # Emit Apple symbol stats @@ -357,7 +341,7 @@ def process_applecrashreport(symbolicator: Symbolicator, data: Any) -> Any: metrics.incr("process.native.symbolicate.request") response = symbolicator.process_applecrashreport(data.get("platform"), report) - if _handle_response_status(data, response): + if handle_response_status(data, response): _merge_full_response(data, response) # Emit Apple symbol stats @@ -480,7 +464,7 @@ def process_native_stacktraces(symbolicator: Symbolicator, data: Any) -> Any: signal=signal, ) - if not _handle_response_status(data, response): + if not handle_response_status(data, response): return data # Emit Apple symbol stats diff --git a/src/sentry/lang/native/symbolicator.py b/src/sentry/lang/native/symbolicator.py index 3d6fa28fc3e84e..618f929e6109bd 100644 --- a/src/sentry/lang/native/symbolicator.py +++ b/src/sentry/lang/native/symbolicator.py @@ -17,6 +17,7 @@ from sentry import options from sentry.attachments.base import CachedAttachment +from sentry.lang.native.error import SymbolicationFailed, write_error from sentry.lang.native.sources import ( get_internal_artifact_lookup_source, get_internal_source, @@ -24,6 +25,7 @@ sources_for_symbolication, ) from sentry.lang.native.utils import Backoff +from sentry.models.eventerror import EventError from sentry.models.project import Project from sentry.net.http import Session from sentry.objectstore import get_attachments_session @@ -396,6 +398,30 @@ def process_jvm( return self._process("symbolicate_jvm_stacktraces", "symbolicate-jvm", json=json) +def handle_response_status(event_data: Any, response_json: dict[str, Any]) -> bool | None: + """Checks the response from Symbolicator and reports errors. + Returns `True` on success.""" + + if not response_json: + error = SymbolicationFailed(type=EventError.NATIVE_INTERNAL_FAILURE) + elif response_json["status"] == "completed": + return True + elif response_json["status"] == "failed": + error = SymbolicationFailed( + message=response_json.get("message") or None, + type=EventError.NATIVE_SYMBOLICATOR_FAILED, + event_id=response_json.get("event_id"), + ) + else: + logger.error("Unexpected symbolicator status: %s", response_json["status"]) + error = SymbolicationFailed( + type=EventError.NATIVE_INTERNAL_FAILURE, event_id=response_json.get("event_id") + ) + + write_error(error, event_data) + return None + + class TaskIdNotFound(Exception): pass @@ -496,9 +522,13 @@ def _request(self, method, path, **kwargs): else: with sentry_sdk.isolation_scope(): sentry_sdk.set_extra("symbolicator_response", response.text) - sentry_sdk.capture_message("Symbolicator request failed") + event_id = sentry_sdk.capture_message("Symbolicator request failed") - json = {"status": "failed", "message": "internal server error"} + json = { + "status": "failed", + "message": "internal server error", + "event_id": event_id, + } return json except (OSError, RequestException) as e: