diff --git a/aws_xray_sdk/core/async_recorder.py b/aws_xray_sdk/core/async_recorder.py index db697f82..952c5762 100644 --- a/aws_xray_sdk/core/async_recorder.py +++ b/aws_xray_sdk/core/async_recorder.py @@ -1,9 +1,9 @@ import time -import traceback import wrapt from aws_xray_sdk.core.recorder import AWSXRayRecorder +from aws_xray_sdk.core.utils import stacktrace class AsyncAWSXRayRecorder(AWSXRayRecorder): @@ -47,7 +47,7 @@ async def record_subsegment_async(self, wrapped, instance, args, kwargs, name, return return_value except Exception as e: exception = e - stack = traceback.extract_stack(limit=self._max_trace_back) + stack = stacktrace.get_stacktrace(limit=self._max_trace_back) raise finally: # No-op if subsegment is `None` due to `LOG_ERROR`. diff --git a/aws_xray_sdk/core/recorder.py b/aws_xray_sdk/core/recorder.py index 6a33eee8..fe579615 100644 --- a/aws_xray_sdk/core/recorder.py +++ b/aws_xray_sdk/core/recorder.py @@ -4,7 +4,6 @@ import os import platform import time -import traceback import wrapt @@ -23,6 +22,7 @@ from .lambda_launcher import check_in_lambda from .exceptions.exceptions import SegmentNameMissingException from .utils.compat import string_types +from .utils import stacktrace log = logging.getLogger(__name__) @@ -398,7 +398,7 @@ def record_subsegment(self, wrapped, instance, args, kwargs, name, return return_value except Exception as e: exception = e - stack = traceback.extract_stack(limit=self.max_trace_back) + stack = stacktrace.get_stacktrace(limit=self.max_trace_back) raise finally: # No-op if subsegment is `None` due to `LOG_ERROR`. diff --git a/aws_xray_sdk/core/utils/stacktrace.py b/aws_xray_sdk/core/utils/stacktrace.py new file mode 100644 index 00000000..3c0c8f26 --- /dev/null +++ b/aws_xray_sdk/core/utils/stacktrace.py @@ -0,0 +1,51 @@ +import sys +import traceback + + +def get_stacktrace(limit=None): + """ + Get a full stacktrace for the current state of execution. + + Include the current state of the stack, minus this function. + If there is an active exception, include the stacktrace information from + the exception as well. + + :param int limit: + Optionally limit stack trace size results. This parmaeters has the same + meaning as the `limit` parameter in `traceback.print_stack`. + :returns: + List of stack trace objects, in the same form as + `traceback.extract_stack`. + """ + if limit is not None and limit == 0: + # Nothing to return. This is consistent with the behavior of the + # functions in the `traceback` module. + return [] + + stack = traceback.extract_stack() + # Remove this `get_stacktrace()` function call from the stack info. + # For what we want to report, this is superfluous information and arguably + # adds garbage to the report. + # Also drop the `traceback.extract_stack()` call above from the returned + # stack info, since this is also superfluous. + stack = stack[:-2] + + _exc_type, _exc, exc_traceback = sys.exc_info() + if exc_traceback is not None: + # If and only if there is a currently triggered exception, combine the + # exception traceback information with the current stack state to get a + # complete trace. + exc_stack = traceback.extract_tb(exc_traceback) + stack += exc_stack + + # Limit the stack trace size, if a limit was specified: + if limit is not None: + # Copy the behavior of `traceback` functions with a `limit` argument. + # See https://docs.python.org/3/library/traceback.html. + if limit > 0: + # limit > 0: include the last `limit` items + stack = stack[-limit:] + else: + # limit < 0: include the first `abs(limit)` items + stack = stack[:abs(limit)] + return stack diff --git a/aws_xray_sdk/ext/aiohttp/client.py b/aws_xray_sdk/ext/aiohttp/client.py index 4843bfeb..951d511e 100644 --- a/aws_xray_sdk/ext/aiohttp/client.py +++ b/aws_xray_sdk/ext/aiohttp/client.py @@ -2,12 +2,12 @@ AioHttp Client tracing, only compatible with Aiohttp 3.X versions """ import aiohttp -import traceback from types import SimpleNamespace from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.core.models import http +from aws_xray_sdk.core.utils import stacktrace from aws_xray_sdk.ext.util import inject_trace_header, strip_url # All aiohttp calls will entail outgoing HTTP requests, only in some ad-hoc @@ -51,7 +51,7 @@ async def end_subsegment_with_exception(session, trace_config_ctx, params): subsegment = xray_recorder.current_subsegment() subsegment.add_exception( params.exception, - traceback.extract_stack(limit=xray_recorder._max_trace_back) + stacktrace.get_stacktrace(limit=xray_recorder._max_trace_back) ) if isinstance(params.exception, LOCAL_EXCEPTIONS): diff --git a/aws_xray_sdk/ext/aiohttp/middleware.py b/aws_xray_sdk/ext/aiohttp/middleware.py index cc163aaf..a58316fc 100644 --- a/aws_xray_sdk/ext/aiohttp/middleware.py +++ b/aws_xray_sdk/ext/aiohttp/middleware.py @@ -1,12 +1,12 @@ """ AioHttp Middleware """ -import traceback from aiohttp import web from aiohttp.web_exceptions import HTTPException from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.core.models import http +from aws_xray_sdk.core.utils import stacktrace from aws_xray_sdk.ext.util import calculate_sampling_decision, \ calculate_segment_name, construct_xray_header, prepare_response_header @@ -69,7 +69,7 @@ async def middleware(request, handler): # Store exception information including the stacktrace to the segment response = None segment.put_http_meta(http.STATUS, 500) - stack = traceback.extract_stack(limit=xray_recorder.max_trace_back) + stack = stacktrace.get_stacktrace(limit=xray_recorder.max_trace_back) segment.add_exception(err, stack) raise finally: diff --git a/aws_xray_sdk/ext/django/middleware.py b/aws_xray_sdk/ext/django/middleware.py index af37a638..9f071de9 100644 --- a/aws_xray_sdk/ext/django/middleware.py +++ b/aws_xray_sdk/ext/django/middleware.py @@ -1,8 +1,8 @@ import logging -import traceback from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.core.models import http +from aws_xray_sdk.core.utils import stacktrace from aws_xray_sdk.ext.util import calculate_sampling_decision, \ calculate_segment_name, construct_xray_header, prepare_response_header @@ -87,5 +87,5 @@ def process_exception(self, request, exception): segment = xray_recorder.current_segment() segment.put_http_meta(http.STATUS, 500) - stack = traceback.extract_stack(limit=xray_recorder._max_trace_back) + stack = stacktrace.get_stacktrace(limit=xray_recorder._max_trace_back) segment.add_exception(exception, stack) diff --git a/aws_xray_sdk/ext/flask/middleware.py b/aws_xray_sdk/ext/flask/middleware.py index 01139556..9e0b4877 100644 --- a/aws_xray_sdk/ext/flask/middleware.py +++ b/aws_xray_sdk/ext/flask/middleware.py @@ -1,9 +1,8 @@ -import traceback - import flask.templating from flask import request from aws_xray_sdk.core.models import http +from aws_xray_sdk.core.utils import stacktrace from aws_xray_sdk.ext.util import calculate_sampling_decision, \ calculate_segment_name, construct_xray_header, prepare_response_header @@ -86,7 +85,7 @@ def _handle_exception(self, exception): return segment.put_http_meta(http.STATUS, 500) - stack = traceback.extract_stack(limit=self._recorder._max_trace_back) + stack = stacktrace.get_stacktrace(limit=self._recorder._max_trace_back) segment.add_exception(exception, stack) self._recorder.end_segment()