13
13
class BacktraceReport :
14
14
def __init__ (self ):
15
15
self .fault_thread = threading .current_thread ()
16
+ self .faulting_thread_id = str (self .fault_thread .ident )
16
17
self .source_code = {}
17
18
self .source_path_dict = {}
18
19
self .attachments = []
19
20
21
+ stack_trace = self .__generate_stack_trace ()
22
+
20
23
attributes , annotations = attribute_manager .get ()
21
24
attributes .update ({"error.type" : "Exception" })
22
25
@@ -29,16 +32,19 @@ def __init__(self):
29
32
"langVersion" : python_version ,
30
33
"agent" : "backtrace-python" ,
31
34
"agentVersion" : version_string ,
32
- "mainThread" : str ( self .fault_thread . ident ) ,
35
+ "mainThread" : self .faulting_thread_id ,
33
36
"attributes" : attributes ,
34
37
"annotations" : annotations ,
35
- "threads" : self . generate_stack_trace () ,
38
+ "threads" : stack_trace ,
36
39
}
37
40
38
41
def set_exception (self , garbage , ex_value , ex_traceback ):
39
42
self .report ["classifiers" ] = [ex_value .__class__ .__name__ ]
40
43
self .report ["attributes" ]["error.message" ] = str (ex_value )
41
44
45
+ # reset faulting thread id and make sure the faulting thread is not listed twice
46
+ self .report ["threads" ][self .faulting_thread_id ]["fault" ] = False
47
+
42
48
# update faulting thread with information from the error
43
49
fault_thread_id = str (self .fault_thread .ident )
44
50
if not fault_thread_id in self .report ["threads" ]:
@@ -50,42 +56,92 @@ def set_exception(self, garbage, ex_value, ex_traceback):
50
56
51
57
faulting_thread = self .report ["threads" ][fault_thread_id ]
52
58
53
- faulting_thread ["stack" ] = self .convert_stack_trace (
54
- self .traverse_exception_stack (ex_traceback ), False
59
+ faulting_thread ["stack" ] = self .__convert_stack_trace (
60
+ self .__traverse_exception_stack (ex_traceback ), False
55
61
)
56
62
faulting_thread ["fault" ] = True
63
+ self .faulting_thread_id = fault_thread_id
64
+ self .report ["mainThread" ] = self .faulting_thread_id
65
+
66
+ def capture_last_exception (self ):
67
+ self .set_exception (* sys .exc_info ())
68
+
69
+ def set_attribute (self , key , value ):
70
+ self .report ["attributes" ][key ] = value
71
+
72
+ def set_dict_attributes (self , target_dict ):
73
+ self .report ["attributes" ].update (target_dict )
74
+
75
+ def set_annotation (self , key , value ):
76
+ self .report ["annotations" ][key ] = value
77
+
78
+ def get_annotations (self ):
79
+ return self .report ["annotations" ]
80
+
81
+ def get_attributes (self ):
82
+ return self .report ["attributes" ]
83
+
84
+ def set_dict_annotations (self , target_dict ):
85
+ self .report ["annotations" ].update (target_dict )
86
+
87
+ def log (self , line ):
88
+ self .log_lines .append (
89
+ {
90
+ "ts" : time .time (),
91
+ "msg" : line ,
92
+ }
93
+ )
94
+
95
+ def add_attachment (self , attachment_path ):
96
+ self .attachments .append (attachment_path )
97
+
98
+ def get_attachments (self ):
99
+ return self .attachments
100
+
101
+ def get_data (self ):
102
+ return self .report
103
+
104
+ def send (self ):
105
+ if len (self .log_lines ) != 0 and "Log" not in self .report ["annotations" ]:
106
+ self .report ["annotations" ]["Log" ] = self .log_lines
107
+ from backtracepython .client import send
108
+
109
+ send (self )
57
110
58
- def generate_stack_trace (self ):
111
+ def __generate_stack_trace (self ):
59
112
current_frames = sys ._current_frames ()
60
113
threads = {}
61
114
for thread in threading .enumerate ():
62
115
thread_frame = current_frames .get (thread .ident )
63
116
is_main_thread = thread .name == "MainThread"
64
- threads [str (thread .ident )] = {
117
+ thread_id = str (thread .ident )
118
+ threads [thread_id ] = {
65
119
"name" : thread .name ,
66
- "stack" : self .convert_stack_trace (
67
- self .traverse_process_thread_stack (thread_frame ), is_main_thread
120
+ "stack" : self .__convert_stack_trace (
121
+ self .__traverse_process_thread_stack (thread_frame ), is_main_thread
68
122
),
69
123
"fault" : is_main_thread ,
70
124
}
125
+ if is_main_thread :
126
+ self .faulting_thread_id = thread_id
71
127
72
128
return threads
73
129
74
- def traverse_exception_stack (self , traceback ):
130
+ def __traverse_exception_stack (self , traceback ):
75
131
stack = []
76
132
while traceback :
77
133
stack .append ({"frame" : traceback .tb_frame , "line" : traceback .tb_lineno })
78
134
traceback = traceback .tb_next
79
135
return reversed (stack )
80
136
81
- def traverse_process_thread_stack (self , thread_frame ):
137
+ def __traverse_process_thread_stack (self , thread_frame ):
82
138
stack = []
83
139
while thread_frame :
84
140
stack .append ({"frame" : thread_frame , "line" : thread_frame .f_lineno })
85
141
thread_frame = thread_frame .f_back
86
142
return stack
87
143
88
- def convert_stack_trace (self , thread_stack_trace , skip_backtrace_module ):
144
+ def __convert_stack_trace (self , thread_stack_trace , skip_backtrace_module ):
89
145
stack_trace = []
90
146
91
147
for thread_stack_frame in thread_stack_trace :
@@ -109,48 +165,3 @@ def convert_stack_trace(self, thread_stack_trace, skip_backtrace_module):
109
165
)
110
166
111
167
return stack_trace
112
-
113
- def capture_last_exception (self ):
114
- self .set_exception (* sys .exc_info ())
115
-
116
- def set_attribute (self , key , value ):
117
- self .report ["attributes" ][key ] = value
118
-
119
- def set_dict_attributes (self , target_dict ):
120
- self .report ["attributes" ].update (target_dict )
121
-
122
- def set_annotation (self , key , value ):
123
- self .report ["annotations" ][key ] = value
124
-
125
- def get_annotations (self ):
126
- return self .report ["annotations" ]
127
-
128
- def get_attributes (self ):
129
- return self .report ["attributes" ]
130
-
131
- def set_dict_annotations (self , target_dict ):
132
- self .report ["annotations" ].update (target_dict )
133
-
134
- def log (self , line ):
135
- self .log_lines .append (
136
- {
137
- "ts" : time .time (),
138
- "msg" : line ,
139
- }
140
- )
141
-
142
- def add_attachment (self , attachment_path ):
143
- self .attachments .append (attachment_path )
144
-
145
- def get_attachments (self ):
146
- return self .attachments
147
-
148
- def get_data (self ):
149
- return self .report
150
-
151
- def send (self ):
152
- if len (self .log_lines ) != 0 and "Log" not in self .report ["annotations" ]:
153
- self .report ["annotations" ]["Log" ] = self .log_lines
154
- from backtracepython .client import send
155
-
156
- send (self )
0 commit comments