26
26
27
27
import sentry_sdk
28
28
from sentry_sdk ._compat import PY37
29
- from sentry_sdk .consts import DEFAULT_MAX_VALUE_LENGTH , EndpointType
29
+ from sentry_sdk .consts import (
30
+ DEFAULT_ADD_FULL_STACK ,
31
+ DEFAULT_MAX_STACK_FRAMES ,
32
+ DEFAULT_MAX_VALUE_LENGTH ,
33
+ EndpointType ,
34
+ )
30
35
31
36
from typing import TYPE_CHECKING
32
37
@@ -737,6 +742,7 @@ def single_exception_from_error_tuple(
737
742
exception_id = None , # type: Optional[int]
738
743
parent_id = None , # type: Optional[int]
739
744
source = None , # type: Optional[str]
745
+ full_stack = None , # type: Optional[list[dict[str, Any]]]
740
746
):
741
747
# type: (...) -> Dict[str, Any]
742
748
"""
@@ -804,10 +810,15 @@ def single_exception_from_error_tuple(
804
810
custom_repr = custom_repr ,
805
811
)
806
812
for tb in iter_stacks (tb )
807
- ]
813
+ ] # type: List[Dict[str, Any]]
808
814
809
815
if frames :
810
- exception_value ["stacktrace" ] = {"frames" : frames }
816
+ if not full_stack :
817
+ new_frames = frames
818
+ else :
819
+ new_frames = merge_stack_frames (frames , full_stack , client_options )
820
+
821
+ exception_value ["stacktrace" ] = {"frames" : new_frames }
811
822
812
823
return exception_value
813
824
@@ -862,6 +873,7 @@ def exceptions_from_error(
862
873
exception_id = 0 , # type: int
863
874
parent_id = 0 , # type: int
864
875
source = None , # type: Optional[str]
876
+ full_stack = None , # type: Optional[list[dict[str, Any]]]
865
877
):
866
878
# type: (...) -> Tuple[int, List[Dict[str, Any]]]
867
879
"""
@@ -881,6 +893,7 @@ def exceptions_from_error(
881
893
exception_id = exception_id ,
882
894
parent_id = parent_id ,
883
895
source = source ,
896
+ full_stack = full_stack ,
884
897
)
885
898
exceptions = [parent ]
886
899
@@ -906,6 +919,7 @@ def exceptions_from_error(
906
919
mechanism = mechanism ,
907
920
exception_id = exception_id ,
908
921
source = "__cause__" ,
922
+ full_stack = full_stack ,
909
923
)
910
924
exceptions .extend (child_exceptions )
911
925
@@ -927,6 +941,7 @@ def exceptions_from_error(
927
941
mechanism = mechanism ,
928
942
exception_id = exception_id ,
929
943
source = "__context__" ,
944
+ full_stack = full_stack ,
930
945
)
931
946
exceptions .extend (child_exceptions )
932
947
@@ -943,6 +958,7 @@ def exceptions_from_error(
943
958
exception_id = exception_id ,
944
959
parent_id = parent_id ,
945
960
source = "exceptions[%s]" % idx ,
961
+ full_stack = full_stack ,
946
962
)
947
963
exceptions .extend (child_exceptions )
948
964
@@ -953,6 +969,7 @@ def exceptions_from_error_tuple(
953
969
exc_info , # type: ExcInfo
954
970
client_options = None , # type: Optional[Dict[str, Any]]
955
971
mechanism = None , # type: Optional[Dict[str, Any]]
972
+ full_stack = None , # type: Optional[list[dict[str, Any]]]
956
973
):
957
974
# type: (...) -> List[Dict[str, Any]]
958
975
exc_type , exc_value , tb = exc_info
@@ -970,14 +987,20 @@ def exceptions_from_error_tuple(
970
987
mechanism = mechanism ,
971
988
exception_id = 0 ,
972
989
parent_id = 0 ,
990
+ full_stack = full_stack ,
973
991
)
974
992
975
993
else :
976
994
exceptions = []
977
995
for exc_type , exc_value , tb in walk_exception_chain (exc_info ):
978
996
exceptions .append (
979
997
single_exception_from_error_tuple (
980
- exc_type , exc_value , tb , client_options , mechanism
998
+ exc_type = exc_type ,
999
+ exc_value = exc_value ,
1000
+ tb = tb ,
1001
+ client_options = client_options ,
1002
+ mechanism = mechanism ,
1003
+ full_stack = full_stack ,
981
1004
)
982
1005
)
983
1006
@@ -1096,6 +1119,46 @@ def exc_info_from_error(error):
1096
1119
return exc_info
1097
1120
1098
1121
1122
+ def merge_stack_frames (frames , full_stack , client_options ):
1123
+ # type: (List[Dict[str, Any]], List[Dict[str, Any]], Optional[Dict[str, Any]]) -> List[Dict[str, Any]]
1124
+ """
1125
+ Add the missing frames from full_stack to frames and return the merged list.
1126
+ """
1127
+ frame_ids = {
1128
+ (
1129
+ frame ["abs_path" ],
1130
+ frame ["context_line" ],
1131
+ frame ["lineno" ],
1132
+ frame ["function" ],
1133
+ )
1134
+ for frame in frames
1135
+ }
1136
+
1137
+ new_frames = [
1138
+ stackframe
1139
+ for stackframe in full_stack
1140
+ if (
1141
+ stackframe ["abs_path" ],
1142
+ stackframe ["context_line" ],
1143
+ stackframe ["lineno" ],
1144
+ stackframe ["function" ],
1145
+ )
1146
+ not in frame_ids
1147
+ ]
1148
+ new_frames .extend (frames )
1149
+
1150
+ # Limit the number of frames
1151
+ max_stack_frames = (
1152
+ client_options .get ("max_stack_frames" , DEFAULT_MAX_STACK_FRAMES )
1153
+ if client_options
1154
+ else None
1155
+ )
1156
+ if max_stack_frames is not None :
1157
+ new_frames = new_frames [len (new_frames ) - max_stack_frames :]
1158
+
1159
+ return new_frames
1160
+
1161
+
1099
1162
def event_from_exception (
1100
1163
exc_info , # type: Union[BaseException, ExcInfo]
1101
1164
client_options = None , # type: Optional[Dict[str, Any]]
@@ -1104,12 +1167,21 @@ def event_from_exception(
1104
1167
# type: (...) -> Tuple[Event, Dict[str, Any]]
1105
1168
exc_info = exc_info_from_error (exc_info )
1106
1169
hint = event_hint_with_exc_info (exc_info )
1170
+
1171
+ if client_options and client_options .get ("add_full_stack" , DEFAULT_ADD_FULL_STACK ):
1172
+ full_stack = current_stacktrace (
1173
+ include_local_variables = client_options ["include_local_variables" ],
1174
+ max_value_length = client_options ["max_value_length" ],
1175
+ )["frames" ]
1176
+ else :
1177
+ full_stack = None
1178
+
1107
1179
return (
1108
1180
{
1109
1181
"level" : "error" ,
1110
1182
"exception" : {
1111
1183
"values" : exceptions_from_error_tuple (
1112
- exc_info , client_options , mechanism
1184
+ exc_info , client_options , mechanism , full_stack
1113
1185
)
1114
1186
},
1115
1187
},
0 commit comments