Skip to content

Commit 1a84076

Browse files
committed
feat: Deep vars
1 parent 30eddcf commit 1a84076

File tree

3 files changed

+127
-0
lines changed

3 files changed

+127
-0
lines changed

sentry_sdk/events.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
def _filter_none(value):
2+
if value is not None:
3+
yield value
4+
5+
6+
def _iter_values(values):
7+
if isinstance(values, list):
8+
return values
9+
elif isinstance(values, dict):
10+
return values.get("values") or ()
11+
else:
12+
return ()
13+
14+
15+
def iter_stacktraces(event):
16+
x = event.get("stacktrace")
17+
if x is not None:
18+
yield x
19+
20+
exception_values = event.get("exception")
21+
if exception_values is not None:
22+
for exception in _iter_values(exception_values):
23+
x = exception.get("stacktrace")
24+
if x is not None:
25+
yield x
26+
27+
thread_values = event.get("threads")
28+
if thread_values is not None:
29+
for exception in _iter_values(thread_values):
30+
x = exception.get("stacktrace")
31+
if x is not None:
32+
yield x

sentry_sdk/integrations/deep_vars.py

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from sentry_sdk.utils import add_global_repr_processor, safe_repr
2+
from sentry_sdk.scope import add_global_event_processor
3+
from sentry_sdk.events import iter_stacktraces
4+
5+
from sentry_sdk.integrations import Integration
6+
7+
8+
class DeepVarsIntegration(Integration):
9+
identifier = "deep_vars"
10+
11+
@staticmethod
12+
def setup_once():
13+
add_global_repr_processor(_deep_vars_repr)
14+
add_global_event_processor(_deep_vars_processor)
15+
16+
17+
class DeepVar(object):
18+
__slots__ = ("obj", "fields")
19+
20+
def __init__(self, obj, fields):
21+
self.obj = obj
22+
self.fields = fields
23+
24+
25+
def _deep_vars_repr(obj, hint):
26+
if type(obj) in (list, dict, str, float, bool):
27+
return NotImplemented
28+
29+
try:
30+
return DeepVar(
31+
obj, {k: safe_repr(obj.__dict__[k]) for k in dir(obj) if k in obj.__dict__}
32+
)
33+
except Exception:
34+
pass
35+
36+
return NotImplemented
37+
38+
39+
def _deep_vars_processor(event, hint):
40+
for stacktrace in iter_stacktraces(event):
41+
_process_stacktrace(stacktrace)
42+
43+
return event
44+
45+
46+
def _process_stacktrace(stacktrace):
47+
for frame in stacktrace.get("frames") or ():
48+
vars = frame.get("vars")
49+
if not vars:
50+
continue
51+
52+
new_vars = {}
53+
54+
for key, value in vars.items():
55+
if isinstance(value, DeepVar):
56+
for key2, value2 in value.fields.items():
57+
new_vars["{}.{}".format(key, key2)] = value2
58+
new_vars[key] = safe_repr(value.obj)
59+
else:
60+
new_vars[key] = value
61+
62+
frame["vars"] = new_vars
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from sentry_sdk import capture_exception
2+
from sentry_sdk.integrations.deep_vars import DeepVarsIntegration
3+
4+
5+
def test_basic(sentry_init, capture_events):
6+
sentry_init(integrations=[DeepVarsIntegration()])
7+
events = capture_events()
8+
9+
class Foo(object):
10+
y = 2
11+
12+
@property
13+
def bogus(self):
14+
1 / 0
15+
16+
try:
17+
foo = Foo()
18+
foo.x = 2
19+
20+
1 / 0
21+
22+
except Exception:
23+
capture_exception()
24+
25+
event, = events
26+
27+
exception, = event["exception"]["values"]
28+
29+
assert exception["type"] == "ZeroDivisionError"
30+
frame, = exception["stacktrace"]["frames"]
31+
32+
assert "Foo" in frame["vars"]["foo"]
33+
assert frame["vars"]["foo.x"] == "2"

0 commit comments

Comments
 (0)