@@ -55,15 +55,11 @@ def get_tracer(tracer_provider=None):
55
55
return tracer_provider .get_tracer (TRACER_NAME , TRACER_VERSION )
56
56
57
57
58
- @contextmanager
59
- def trace_call (name , session = None , extra_attributes = None , observability_options = None ):
60
- if session :
61
- session ._last_use_time = datetime .now ()
62
-
63
- if not (HAS_OPENTELEMETRY_INSTALLED and name ):
64
- # Empty context manager. Users will have to check if the generated value is None or a span
65
- yield None
66
- return
58
+ def _make_tracer_and_span_attributes (
59
+ session = None , extra_attributes = None , observability_options = None
60
+ ):
61
+ if not HAS_OPENTELEMETRY_INSTALLED :
62
+ return None , None
67
63
68
64
tracer_provider = None
69
65
@@ -103,9 +99,77 @@ def trace_call(name, session=None, extra_attributes=None, observability_options=
103
99
104
100
if not enable_extended_tracing :
105
101
attributes .pop ("db.statement" , False )
102
+ attributes .pop ("sql" , False )
103
+ else :
104
+ # Otherwise there are places where the annotated sql was inserted
105
+ # directly from the arguments as "sql", and transform those into "db.statement".
106
+ db_statement = attributes .get ("db.statement" , None )
107
+ if not db_statement :
108
+ sql = attributes .get ("sql" , None )
109
+ if sql :
110
+ attributes = attributes .copy ()
111
+ attributes .pop ("sql" , False )
112
+ attributes ["db.statement" ] = sql
113
+
114
+ return tracer , attributes
115
+
116
+
117
+ def trace_call_end_lazily (
118
+ name , session = None , extra_attributes = None , observability_options = None
119
+ ):
120
+ """
121
+ trace_call_end_lazily is used in situations where you don't want a context managed
122
+ span in a with statement to end as soon as a block exits. This is useful for example
123
+ after a Database.batch or Database.snapshot but without a context manager.
124
+ If you need to directly invoke tracing with a context manager, please invoke
125
+ `trace_call` with which you can invoke
126
+  `with trace_call(...) as span:`
127
+ It is the caller's responsibility to explicitly invoke the returned ending function.
128
+ """
129
+ if not name :
130
+ return None
131
+
132
+ tracer , span_attributes = _make_tracer_and_span_attributes (
133
+ session , extra_attributes , observability_options
134
+ )
135
+ if not tracer :
136
+ return None
137
+
138
+ span = tracer .start_span (
139
+ name , kind = trace .SpanKind .CLIENT , attributes = span_attributes
140
+ )
141
+ ctx_manager = trace .use_span (span , end_on_exit = True , record_exception = True )
142
+ ctx_manager .__enter__ ()
143
+
144
+ def discard (exc_type = None , exc_value = None , exc_traceback = None ):
145
+ if not exc_type :
146
+ span .set_status (Status (StatusCode .OK ))
147
+
148
+ ctx_manager .__exit__ (exc_type , exc_value , exc_traceback )
149
+
150
+ return discard
151
+
152
+
153
+ @contextmanager
154
+ def trace_call (name , session = None , extra_attributes = None , observability_options = None ):
155
+ """
156
+  trace_call is used in situations where you need to end a span with a context manager
157
+  or after a scope is exited. If you need to keep a span alive and lazily end it, please
158
+  invoke `trace_call_end_lazily`.
159
+ """
160
+ if not name :
161
+ yield None
162
+ return
163
+
164
+ tracer , span_attributes = _make_tracer_and_span_attributes (
165
+ session , extra_attributes , observability_options
166
+ )
167
+ if not tracer :
168
+ yield None
169
+ return
106
170
107
171
with tracer .start_as_current_span (
108
- name , kind = trace .SpanKind .CLIENT , attributes = attributes
172
+ name , kind = trace .SpanKind .CLIENT , attributes = span_attributes
109
173
) as span :
110
174
try :
111
175
yield span
@@ -135,3 +199,16 @@ def get_current_span():
135
199
def add_span_event (span , event_name , event_attributes = None ):
136
200
if span :
137
201
span .add_event (event_name , event_attributes )
202
+
203
+
204
+ def add_event_on_current_span (event_name , event_attributes = None , span = None ):
205
+ if not span :
206
+ span = get_current_span ()
207
+
208
+ add_span_event (span , event_name , event_attributes )
209
+
210
+
211
+ def record_span_exception_and_status (span , exc ):
212
+ if span :
213
+ span .set_status (Status (StatusCode .ERROR , str (exc )))
214
+ span .record_exception (exc )
0 commit comments