From fba3fc24800ebcdb265e1ad1efaeaabb94e4e0fd Mon Sep 17 00:00:00 2001 From: Peter Uittenbroek Date: Wed, 14 Jun 2023 14:56:07 +0200 Subject: [PATCH] Read MAX_STRING_LENGTH from client options (#2121) --- sentry_sdk/client.py | 5 ++++- sentry_sdk/consts.py | 1 + sentry_sdk/integrations/logging.py | 5 ++++- sentry_sdk/serializer.py | 7 ++++-- sentry_sdk/utils.py | 34 ++++++++++++++++++++---------- tests/test_serializer.py | 17 +++++++++++++++ 6 files changed, 54 insertions(+), 15 deletions(-) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index 9ebc177158..c0e73881ba 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -286,7 +286,10 @@ def _prepare_event( "values": [ { "stacktrace": current_stacktrace( - self.options["include_local_variables"] + self.options["max_string_length"], + include_local_variables=self.options[ + "include_local_variables" + ], ), "crashed": False, "current": True, diff --git a/sentry_sdk/consts.py b/sentry_sdk/consts.py index ebe5719471..853c80e5d1 100644 --- a/sentry_sdk/consts.py +++ b/sentry_sdk/consts.py @@ -197,6 +197,7 @@ def __init__( ], # type: Optional[Sequence[str]] functions_to_trace=[], # type: Sequence[Dict[str, str]] # noqa: B006 event_scrubber=None, # type: Optional[sentry_sdk.scrubber.EventScrubber] + max_string_length=1024, # type: int ): # type: (...) -> None pass diff --git a/sentry_sdk/integrations/logging.py b/sentry_sdk/integrations/logging.py index d4f34d085c..664f51fd59 100644 --- a/sentry_sdk/integrations/logging.py +++ b/sentry_sdk/integrations/logging.py @@ -205,7 +205,10 @@ def _emit(self, record): "values": [ { "stacktrace": current_stacktrace( - client_options["include_local_variables"] + client_options["max_string_length"], + include_local_variables=client_options[ + "include_local_variables" + ], ), "crashed": False, "current": True, diff --git a/sentry_sdk/serializer.py b/sentry_sdk/serializer.py index b3f8012c28..122895ce52 100644 --- a/sentry_sdk/serializer.py +++ b/sentry_sdk/serializer.py @@ -121,6 +121,7 @@ def serialize(event, **kwargs): meta_stack = [] # type: List[Dict[str, Any]] keep_request_bodies = kwargs.pop("request_bodies", None) == "always" # type: bool + max_string_length = kwargs.pop("max_string_length", None) # type: Optional[int] def _annotate(**meta): # type: (**Any) -> None @@ -293,7 +294,9 @@ def _serialize_node_impl( if remaining_depth is not None and remaining_depth <= 0: _annotate(rem=[["!limit", "x"]]) if is_databag: - return _flatten_annotated(strip_string(safe_repr(obj))) + return _flatten_annotated( + strip_string(safe_repr(obj), max_length=max_string_length) + ) return None if is_databag and global_repr_processors: @@ -394,7 +397,7 @@ def _serialize_node_impl( if is_span_description: return obj - return _flatten_annotated(strip_string(obj)) + return _flatten_annotated(strip_string(obj, max_string_length)) # # Start of serialize() function diff --git a/sentry_sdk/utils.py b/sentry_sdk/utils.py index 5c43fa3cc6..282e1a83c3 100644 --- a/sentry_sdk/utils.py +++ b/sentry_sdk/utils.py @@ -468,6 +468,7 @@ def iter_stacks(tb): def get_lines_from_file( filename, # type: str lineno, # type: int + max_length, # type: int loader=None, # type: Optional[Any] module=None, # type: Optional[str] ): @@ -496,11 +497,12 @@ def get_lines_from_file( try: pre_context = [ - strip_string(line.strip("\r\n")) for line in source[lower_bound:lineno] + strip_string(line.strip("\r\n"), max_length=max_length) + for line in source[lower_bound:lineno] ] - context_line = strip_string(source[lineno].strip("\r\n")) + context_line = strip_string(source[lineno].strip("\r\n"), max_length=max_length) post_context = [ - strip_string(line.strip("\r\n")) + strip_string(line.strip("\r\n"), max_length=max_length) for line in source[(lineno + 1) : upper_bound] ] return pre_context, context_line, post_context @@ -512,6 +514,7 @@ def get_lines_from_file( def get_source_context( frame, # type: FrameType tb_lineno, # type: int + max_string_length, # type: int ): # type: (...) -> Tuple[List[Annotated[str]], Optional[Annotated[str]], List[Annotated[str]]] try: @@ -528,7 +531,9 @@ def get_source_context( loader = None lineno = tb_lineno - 1 if lineno is not None and abs_path: - return get_lines_from_file(abs_path, lineno, loader, module) + return get_lines_from_file( + abs_path, lineno, max_string_length, loader=loader, module=module + ) return [], None, [] @@ -602,7 +607,11 @@ def filename_for_module(module, abs_path): def serialize_frame( - frame, tb_lineno=None, include_local_variables=True, include_source_context=True + frame, + tb_lineno=None, + include_local_variables=True, + include_source_context=True, + max_string_length=None, ): # type: (FrameType, Optional[int], bool, bool) -> Dict[str, Any] f_code = getattr(frame, "f_code", None) @@ -630,7 +639,7 @@ def serialize_frame( if include_source_context: rv["pre_context"], rv["context_line"], rv["post_context"] = get_source_context( - frame, tb_lineno + frame, tb_lineno, max_string_length ) if include_local_variables: @@ -639,7 +648,9 @@ def serialize_frame( return rv -def current_stacktrace(include_local_variables=True, include_source_context=True): +def current_stacktrace( + max_string_length, include_local_variables=True, include_source_context=True +): # type: (bool, bool) -> Any __tracebackhide__ = True frames = [] @@ -652,6 +663,7 @@ def current_stacktrace(include_local_variables=True, include_source_context=True f, include_local_variables=include_local_variables, include_source_context=include_source_context, + max_string_length=max_string_length, ) ) f = f.f_back @@ -724,9 +736,11 @@ def single_exception_from_error_tuple( if client_options is None: include_local_variables = True include_source_context = True + max_string_length = 1024 else: include_local_variables = client_options["include_local_variables"] include_source_context = client_options["include_source_context"] + max_string_length = client_options["max_string_length"] frames = [ serialize_frame( @@ -734,6 +748,7 @@ def single_exception_from_error_tuple( tb_lineno=tb.tb_lineno, include_local_variables=include_local_variables, include_source_context=include_source_context, + max_string_length=max_string_length, ) for tb in iter_stacks(tb) ] @@ -819,9 +834,7 @@ def exceptions_from_error( parent_id = exception_id exception_id += 1 - should_supress_context = ( - hasattr(exc_value, "__suppress_context__") and exc_value.__suppress_context__ # type: ignore - ) + should_supress_context = hasattr(exc_value, "__suppress_context__") and exc_value.__suppress_context__ # type: ignore if should_supress_context: # Add direct cause. # The field `__cause__` is set when raised with the exception (using the `from` keyword). @@ -1082,7 +1095,6 @@ def _is_in_project_root(abs_path, project_root): def strip_string(value, max_length=None): # type: (str, Optional[int]) -> Union[AnnotatedValue, str] - # TODO: read max_length from config if not value: return value diff --git a/tests/test_serializer.py b/tests/test_serializer.py index cc62c4663d..2e71f68cca 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -144,3 +144,20 @@ def test_no_trimming_if_request_bodies_is_always(body_normalizer): result = body_normalizer(data, request_bodies="always") assert result == data + + +def test_max_string_length_default(body_normalizer): + data = {"key": "a" * 2000} + + result = body_normalizer(data) + + assert len(result["key"]) == 1024 # fallback max length + + +def test_max_string_length(body_normalizer): + data = {"key": "a" * 2000} + + max_string_length = 2000 + result = body_normalizer(data, max_string_length=max_string_length) + + assert len(result["key"]) == max_string_length