Skip to content

Commit 8548544

Browse files
committed
feat(java): Pass exception to symbolicate request
1 parent 01650a8 commit 8548544

File tree

3 files changed

+75
-0
lines changed

3 files changed

+75
-0
lines changed

src/sentry/lang/java/processing.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,11 @@ def process_jvm_stacktraces(symbolicator: Symbolicator, data: Any) -> Any:
182182
stacktrace_infos = find_stacktraces_in_data(data)
183183
stacktraces = [
184184
{
185+
**(
186+
{"exception": {"type": sinfo.exception_type, "module": sinfo.exception_module}}
187+
if sinfo.get_exception()
188+
else {}
189+
),
185190
"frames": [
186191
_normalize_frame(frame, index)
187192
for index, frame in enumerate(sinfo.stacktrace.get("frames") or ())

src/sentry/stacktraces/processing.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ class StacktraceInfo(NamedTuple):
4040
container: dict[str, Any]
4141
platforms: set[str]
4242
is_exception: bool
43+
exception_type: str | None
44+
exception_module: str | None
4345

4446
def __hash__(self) -> int:
4547
return id(self)
@@ -50,6 +52,14 @@ def __eq__(self, other: object) -> bool:
5052
def __ne__(self, other: object) -> bool:
5153
return self is not other
5254

55+
def get_exception(self) -> str | None:
56+
"""Returns the fully qualified exception name (module.type) or None if not an exception."""
57+
if self.exception_type is None:
58+
return None
59+
if self.exception_module:
60+
return f"{self.exception_module}.{self.exception_type}"
61+
return self.exception_type
62+
5363
def get_frames(self) -> Sequence[dict[str, Any]]:
5464
return _safe_get_frames(self.stacktrace)
5565

@@ -225,6 +235,10 @@ def _append_stacktrace(
225235
container: Any = None,
226236
# Whether or not the container is from `exception.values`
227237
is_exception: bool = False,
238+
# The exception type (e.g., "ValueError") if this stacktrace belongs to an exception
239+
exception_type: str | None = None,
240+
# The exception module (e.g., "__builtins__") if this stacktrace belongs to an exception
241+
exception_module: str | None = None,
228242
# Prevent skipping empty/null stacktraces from `exception.values` (other empty/null
229243
# stacktraces are always skipped)
230244
include_empty_exceptions: bool = False,
@@ -244,6 +258,8 @@ def _append_stacktrace(
244258
container=container,
245259
platforms=platforms,
246260
is_exception=is_exception,
261+
exception_type=exception_type,
262+
exception_module=exception_module,
247263
)
248264
)
249265

@@ -253,6 +269,8 @@ def _append_stacktrace(
253269
exc.get("stacktrace"),
254270
container=exc,
255271
is_exception=True,
272+
exception_type=exc.get("type"),
273+
exception_module=exc.get("module"),
256274
include_empty_exceptions=include_empty_exceptions,
257275
)
258276

tests/sentry/test_stacktraces.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ def test_stacktraces_basics(self) -> None:
3636
assert len(infos) == 1
3737
assert len(infos[0].stacktrace["frames"]) == 2
3838
assert infos[0].platforms == {"javascript", "native"}
39+
# Top-level stacktraces are not exceptions
40+
assert infos[0].is_exception is False
41+
assert infos[0].exception_type is None
42+
assert infos[0].exception_module is None
43+
assert infos[0].get_exception() is None
3944

4045
def test_stacktraces_exception(self) -> None:
4146
data: dict[str, Any] = {
@@ -69,6 +74,42 @@ def test_stacktraces_exception(self) -> None:
6974
infos = find_stacktraces_in_data(data)
7075
assert len(infos) == 1
7176
assert len(infos[0].stacktrace["frames"]) == 2
77+
# Exception stacktraces have type but no module in this case
78+
assert infos[0].is_exception is True
79+
assert infos[0].exception_type == "Error"
80+
assert infos[0].exception_module is None
81+
assert infos[0].get_exception() == "Error"
82+
83+
def test_stacktraces_exception_with_module(self) -> None:
84+
data: dict[str, Any] = {
85+
"message": "hello",
86+
"platform": "java",
87+
"exception": {
88+
"values": [
89+
{
90+
"type": "RuntimeException",
91+
"module": "java.lang",
92+
"stacktrace": {
93+
"frames": [
94+
{
95+
"function": "main",
96+
"module": "com.example.App",
97+
"filename": "App.java",
98+
"lineno": 10,
99+
},
100+
]
101+
},
102+
}
103+
]
104+
},
105+
}
106+
107+
infos = find_stacktraces_in_data(data)
108+
assert len(infos) == 1
109+
assert infos[0].is_exception is True
110+
assert infos[0].exception_type == "RuntimeException"
111+
assert infos[0].exception_module == "java.lang"
112+
assert infos[0].get_exception() == "java.lang.RuntimeException"
72113

73114
def test_stacktraces_threads(self) -> None:
74115
data: dict[str, Any] = {
@@ -102,6 +143,11 @@ def test_stacktraces_threads(self) -> None:
102143
infos = find_stacktraces_in_data(data)
103144
assert len(infos) == 1
104145
assert len(infos[0].stacktrace["frames"]) == 2
146+
# Thread stacktraces are not exceptions
147+
assert infos[0].is_exception is False
148+
assert infos[0].exception_type is None
149+
assert infos[0].exception_module is None
150+
assert infos[0].get_exception() is None
105151

106152
def test_find_stacktraces_skip_none(self) -> None:
107153
# This tests:
@@ -147,13 +193,19 @@ def test_find_stacktraces_skip_none(self) -> None:
147193
assert len(infos) == 4
148194
assert sum(1 for x in infos if x.stacktrace) == 3
149195
assert sum(1 for x in infos if x.is_exception) == 4
196+
# All exceptions have type "Error" and no module
197+
assert all(x.exception_type == "Error" for x in infos)
198+
assert all(x.exception_module is None for x in infos)
199+
assert all(x.get_exception() == "Error" for x in infos)
150200
# XXX: The null frame is still part of this stack trace!
151201
assert len(infos[3].stacktrace["frames"]) == 3
152202

153203
infos = find_stacktraces_in_data(data)
154204
assert len(infos) == 1
155205
# XXX: The null frame is still part of this stack trace!
156206
assert len(infos[0].stacktrace["frames"]) == 3
207+
assert infos[0].exception_type == "Error"
208+
assert infos[0].get_exception() == "Error"
157209

158210

159211
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)