@@ -55,15 +55,11 @@ def get_tracer(tracer_provider=None):
5555 return tracer_provider .get_tracer (TRACER_NAME , TRACER_VERSION )
5656
5757
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
6763
6864 tracer_provider = None
6965
@@ -103,9 +99,77 @@ def trace_call(name, session=None, extra_attributes=None, observability_options=
10399
104100 if not enable_extended_tracing :
105101 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
106170
107171 with tracer .start_as_current_span (
108- name , kind = trace .SpanKind .CLIENT , attributes = attributes
172+ name , kind = trace .SpanKind .CLIENT , attributes = span_attributes
109173 ) as span :
110174 try :
111175 yield span
@@ -135,3 +199,16 @@ def get_current_span():
135199def add_span_event (span , event_name , event_attributes = None ):
136200 if span :
137201 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