diff --git a/ddtrace/debugging/_exception/replay.py b/ddtrace/debugging/_exception/replay.py index 080b4cbfc61..c9da69bb5b2 100644 --- a/ddtrace/debugging/_exception/replay.py +++ b/ddtrace/debugging/_exception/replay.py @@ -21,6 +21,7 @@ from ddtrace.internal.rate_limiter import BudgetRateLimiterWithJitter as RateLimiter from ddtrace.internal.rate_limiter import RateLimitExceeded from ddtrace.internal.utils.time import HourGlass +from ddtrace.settings.exception_replay import config log = get_logger(__name__) @@ -225,7 +226,9 @@ def on_span_exception( seq = count(1) # 1-based sequence number - while chain: + frames_captured = 0 + + while chain and frames_captured <= config.max_frames: exc, _tb = chain.pop() # LIFO: reverse the chain if _tb is None or _tb.tb_frame is None: @@ -233,7 +236,7 @@ def on_span_exception( continue # DEV: We go from the handler up to the root exception - while _tb: + while _tb and frames_captured <= config.max_frames: frame = _tb.tb_frame code = frame.f_code seq_nr = next(seq) @@ -263,6 +266,9 @@ def on_span_exception( # Memoize frame.f_locals[SNAPSHOT_KEY] = snapshot_id = snapshot.uuid + # Count + frames_captured += 1 + # Add correlation tags on the span span.set_tag_str(FRAME_SNAPSHOT_ID_TAG % seq_nr, snapshot_id) span.set_tag_str(FRAME_FUNCTION_TAG % seq_nr, code.co_name) diff --git a/ddtrace/settings/exception_replay.py b/ddtrace/settings/exception_replay.py index c78ae29b73d..723a63288a0 100644 --- a/ddtrace/settings/exception_replay.py +++ b/ddtrace/settings/exception_replay.py @@ -14,6 +14,13 @@ class ExceptionReplayConfig(En): help="Enable automatic capturing of exception debugging information", deprecations=[("debugging.enabled", None, "3.0")], ) + max_frames = En.v( + int, + "replay.capture_max_frames", + default=8, + help_type="int", + help="The maximum number of frames to capture for each exception", + ) config = ExceptionReplayConfig() diff --git a/tests/telemetry/test_writer.py b/tests/telemetry/test_writer.py index 8d4030c84a9..6b845760c3d 100644 --- a/tests/telemetry/test_writer.py +++ b/tests/telemetry/test_writer.py @@ -354,6 +354,7 @@ def test_app_started_event_configuration_override(test_agent_session, run_python {"name": "DD_DYNAMIC_INSTRUMENTATION_UPLOAD_FLUSH_INTERVAL", "origin": "default", "value": 1.0}, {"name": "DD_DYNAMIC_INSTRUMENTATION_UPLOAD_TIMEOUT", "origin": "default", "value": 30}, {"name": "DD_ENV", "origin": "default", "value": None}, + {"name": "DD_EXCEPTION_REPLAY_CAPTURE_MAX_FRAMES", "origin": "default", "value": 8}, {"name": "DD_EXCEPTION_REPLAY_ENABLED", "origin": "env_var", "value": True}, {"name": "DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED", "origin": "default", "value": False}, {"name": "DD_HTTP_CLIENT_TAG_QUERY_STRING", "origin": "default", "value": None},