diff --git a/ddtrace/debugging/_safety.py b/ddtrace/debugging/_safety.py index 50142fc87bc..118deddef40 100644 --- a/ddtrace/debugging/_safety.py +++ b/ddtrace/debugging/_safety.py @@ -32,7 +32,7 @@ def get_locals(frame: FrameType) -> Iterator[Tuple[str, Any]]: def get_globals(frame: FrameType) -> Iterator[Tuple[str, Any]]: nonlocal_names = frame.f_code.co_names - _globals = globals() + _globals = frame.f_globals return ((name, _globals[name]) for name in nonlocal_names if name in _globals) diff --git a/ddtrace/debugging/_signal/snapshot.py b/ddtrace/debugging/_signal/snapshot.py index ed0bd042098..d9f5ec99cf3 100644 --- a/ddtrace/debugging/_signal/snapshot.py +++ b/ddtrace/debugging/_signal/snapshot.py @@ -38,6 +38,7 @@ def _capture_context( arguments: List[Tuple[str, Any]], _locals: List[Tuple[str, Any]], + _globals: List[Tuple[str, Any]], throwable: ExcInfoType, limits: CaptureLimits = DEFAULT_CAPTURE_LIMITS, ) -> Dict[str, Any]: @@ -50,18 +51,29 @@ def timeout(_): "arguments": utils.capture_pairs( arguments, limits.max_level, limits.max_len, limits.max_size, limits.max_fields, timeout ) - if arguments is not None + if arguments else {}, "locals": utils.capture_pairs( _locals, limits.max_level, limits.max_len, limits.max_size, limits.max_fields, timeout ) - if _locals is not None + if _locals + else {}, + "staticFields": utils.capture_pairs( + _globals, limits.max_level, limits.max_len, limits.max_size, limits.max_fields, timeout + ) + if _globals else {}, "throwable": utils.capture_exc_info(throwable), } -_EMPTY_CAPTURED_CONTEXT = _capture_context([], [], (None, None, None), DEFAULT_CAPTURE_LIMITS) +_EMPTY_CAPTURED_CONTEXT = _capture_context( + arguments=[], + _locals=[], + _globals=[], + throwable=(None, None, None), + limits=DEFAULT_CAPTURE_LIMITS, +) @attr.s @@ -121,6 +133,7 @@ def enter(self): self.entry_capture = _capture_context( _args, [], + [], (None, None, None), limits=probe.limits, ) @@ -154,7 +167,7 @@ def exit(self, retval, exc_info, duration): if probe.take_snapshot: self.return_capture = _capture_context( - self.args or _safety.get_args(self.frame), _locals, exc_info, limits=probe.limits + self.args or _safety.get_args(self.frame), _locals, [], exc_info, limits=probe.limits ) self.duration = duration self.state = SignalState.DONE @@ -179,6 +192,7 @@ def line(self): self.line_capture = _capture_context( self.args or _safety.get_args(frame), _safety.get_locals(frame), + _safety.get_globals(frame), sys.exc_info(), limits=probe.limits, ) diff --git a/tests/debugging/test_debugger.py b/tests/debugging/test_debugger.py index a2be8b87529..22dbe1060ea 100644 --- a/tests/debugging/test_debugger.py +++ b/tests/debugging/test_debugger.py @@ -1197,6 +1197,7 @@ def test_debugger_redacted_identifiers(): "size": 3, }, }, + "staticFields": {"SensitiveData": {"type": "type", "value": ""}}, "throwable": None, } diff --git a/tests/debugging/test_encoding.py b/tests/debugging/test_encoding.py index 4c5b2e45170..b7a13d25e6e 100644 --- a/tests/debugging/test_encoding.py +++ b/tests/debugging/test_encoding.py @@ -136,13 +136,13 @@ def c(): def test_capture_context_default_level(): - context = _capture_context([("self", tree)], [], (None, None, None), CaptureLimits(max_level=0)) + context = _capture_context([("self", tree)], [], [], (None, None, None), CaptureLimits(max_level=0)) self = context["arguments"]["self"] assert self["fields"]["root"]["notCapturedReason"] == "depth" def test_capture_context_one_level(): - context = _capture_context([("self", tree)], [], (None, None, None), CaptureLimits(max_level=1)) + context = _capture_context([("self", tree)], [], [], (None, None, None), CaptureLimits(max_level=1)) self = context["arguments"]["self"] assert self["fields"]["root"]["fields"]["left"] == {"notCapturedReason": "depth", "type": "Node"} @@ -152,13 +152,13 @@ def test_capture_context_one_level(): def test_capture_context_two_level(): - context = _capture_context([("self", tree)], [], (None, None, None), CaptureLimits(max_level=2)) + context = _capture_context([("self", tree)], [], [], (None, None, None), CaptureLimits(max_level=2)) self = context["arguments"]["self"] assert self["fields"]["root"]["fields"]["left"]["fields"]["right"] == {"notCapturedReason": "depth", "type": "Node"} def test_capture_context_three_level(): - context = _capture_context([("self", tree)], [], (None, None, None), CaptureLimits(max_level=3)) + context = _capture_context([("self", tree)], [], [], (None, None, None), CaptureLimits(max_level=3)) self = context["arguments"]["self"] assert self["fields"]["root"]["fields"]["left"]["fields"]["right"]["fields"]["right"]["isNull"], context assert self["fields"]["root"]["fields"]["left"]["fields"]["right"]["fields"]["left"]["isNull"], context @@ -169,11 +169,12 @@ def test_capture_context_exc(): try: raise Exception("test", "me") except Exception: - context = _capture_context([], [], sys.exc_info()) + context = _capture_context([], [], [], sys.exc_info()) exc = context.pop("throwable") assert context == { "arguments": {}, "locals": {}, + "staticFields": {}, } assert exc["message"] == "'test', 'me'" assert exc["type"] == "Exception" @@ -190,7 +191,7 @@ def test_batch_json_encoder(): # to test that we can handle unicode strings. cake = "After the test there will be ✨ 🍰 ✨ in the annex" - buffer_size = 30 * (1 << 10) + buffer_size = 30 * (1 << 20) queue = SignalQueue(encoder=LogSignalJsonEncoder(None), buffer_size=buffer_size) s.line() diff --git a/tests/debugging/test_safety.py b/tests/debugging/test_safety.py index e0f1b858719..3acb0288924 100644 --- a/tests/debugging/test_safety.py +++ b/tests/debugging/test_safety.py @@ -7,6 +7,9 @@ from ddtrace.debugging import _safety +GLOBAL_VALUE = 42 + + def test_get_args(): def assert_args(args): assert set(dict(_safety.get_args(inspect.currentframe().f_back)).keys()) == args @@ -14,6 +17,9 @@ def assert_args(args): def assert_locals(_locals): assert set(dict(_safety.get_locals(inspect.currentframe().f_back)).keys()) == _locals + def assert_globals(_globals): + assert set(dict(_safety.get_globals(inspect.currentframe().f_back)).keys()) == _globals + def arg_and_kwargs(a, **kwargs): assert_args({"a", "kwargs"}) assert_locals(set()) @@ -30,10 +36,17 @@ def args(*ars): assert_args({"ars"}) assert_locals(set()) + def referenced_globals(): + global GLOBAL_VALUE + a = GLOBAL_VALUE >> 1 # noqa + + assert_globals({"GLOBAL_VALUE"}) + arg_and_kwargs(1, b=2) arg_and_args_and_kwargs(1, 42, b=2) args_and_kwargs() args() + referenced_globals() # ---- Side effects ----