1
1
import logging
2
+ import inspect
3
+ import traceback
4
+ import sys
2
5
from logging import getLogger
3
6
from typing import Optional
4
7
from opentelemetry .instrumentation .distro import BaseDistro
9
12
from middleware .log import create_logger_handler
10
13
from middleware .profiler import collect_profiling
11
14
from opentelemetry import trace
12
- from opentelemetry .trace import Tracer , get_current_span , get_tracer
15
+ from opentelemetry .trace import Tracer , get_current_span , get_tracer , Span
16
+
13
17
14
18
_logger = getLogger (__name__ )
15
19
@@ -80,6 +84,63 @@ def mw_tracker(
80
84
81
85
mw_tracker_called = True
82
86
87
+ def extract_function_code (tb_frame ):
88
+ """Extracts the full function body where the exception occurred."""
89
+ try :
90
+ source_lines , _ = inspect .getsourcelines (tb_frame )
91
+ return "" .join (source_lines ) # Convert to a string
92
+ except Exception :
93
+ return "Could not retrieve source code."
94
+
95
+ # Replacement of span.record_exception to include function source code
96
+ def custom_record_exception (span : Span , exc : Exception ):
97
+ """Custom exception recording that captures function source code."""
98
+ exc_type , exc_value , exc_tb = exc .__class__ , str (exc ), exc .__traceback__
99
+
100
+ if exc_tb is None :
101
+ span .set_attribute ("exception.warning" , "No traceback available" )
102
+ span .record_exception (exc )
103
+ return
104
+
105
+ tb_details = traceback .extract_tb (exc_tb )
106
+
107
+ if not tb_details :
108
+ span .set_attribute ("exception.warning" , "Traceback is empty" )
109
+ span .record_exception (exc )
110
+ return
111
+
112
+ last_tb = tb_details [- 1 ] # Get the last traceback entry (where exception occurred)
113
+ filename , lineno , func_name , _ = last_tb
114
+
115
+ # Extract the correct frame from the traceback
116
+ tb_frame = None
117
+ for frame , _ in traceback .walk_tb (exc_tb ):
118
+ if frame .f_code .co_name == func_name :
119
+ tb_frame = frame
120
+ break
121
+
122
+
123
+
124
+ function_code = extract_function_code (tb_frame ) if tb_frame else "Function source not found."
125
+
126
+ # Determine if the exception is escaping
127
+ current_exc = sys .exc_info ()[1 ] # Get the currently active exception
128
+ exception_escaped = current_exc is exc # True if it's still propagating
129
+
130
+ # Add extra details in the existing "exception" event
131
+ span .add_event (
132
+ "exception" , # Keep the event name as "exception"
133
+ {
134
+ "exception.type" : str (exc_type .__name__ ),
135
+ "exception.message" : exc_value ,
136
+ "exception.stacktrace" : traceback .format_exc (),
137
+ "exception.function_name" : func_name ,
138
+ "exception.file" : filename ,
139
+ "exception.line" : lineno ,
140
+ "exception.function_body" : function_code ,
141
+ "exception.escaped" : exception_escaped ,
142
+ }
143
+ )
83
144
84
145
def record_exception (exc : Exception , span_name : Optional [str ] = None ) -> None :
85
146
"""
@@ -102,15 +163,15 @@ def record_exception(exc: Exception, span_name: Optional[str] = None) -> None:
102
163
103
164
span = get_current_span ()
104
165
if span .is_recording ():
105
- span . record_exception ( exc )
166
+ custom_record_exception ( span , exc )
106
167
return
107
168
108
169
tracer : Tracer = get_tracer ("mw-tracer" )
109
170
if span_name is None :
110
171
span_name = type (exc ).__name__
111
172
112
173
span = tracer .start_span (span_name )
113
- span . record_exception ( exc )
174
+ custom_record_exception ( span , exc )
114
175
span .set_status (trace .Status (trace .StatusCode .ERROR , str (exc )))
115
176
span .end ()
116
177
0 commit comments