1111from opentelemetry .util import types as otel_types
1212from typing_extensions import LiteralString , ParamSpec
1313
14+ from .ast_utils import has_current_span_call
1415from .constants import ATTRIBUTES_MESSAGE_TEMPLATE_KEY , ATTRIBUTES_TAGS_KEY
1516from .stack_info import get_filepath_attribute
1617from .utils import safe_repr , uniquify_sequence
@@ -61,7 +62,8 @@ def decorator(func: Callable[P, R]) -> Callable[P, R]:
6162 )
6263
6364 attributes = get_attributes (func , msg_template , tags )
64- open_span = get_open_span (logfire , attributes , span_name , extract_args , func )
65+ uses_current_span = has_current_span_call (func )
66+ open_span = get_open_span (logfire , attributes , span_name , extract_args , uses_current_span , func )
6567
6668 if inspect .isgeneratorfunction (func ):
6769 if not allow_generator :
@@ -90,21 +92,31 @@ async def wrapper(*func_args: P.args, **func_kwargs: P.kwargs): # type: ignore
9092
9193 async def wrapper (* func_args : P .args , ** func_kwargs : P .kwargs ) -> R : # type: ignore
9294 with open_span (* func_args , ** func_kwargs ) as span :
95+ token = None
96+ if uses_current_span :
97+ token = logfire ._current_span_var .set (span ) # type: ignore[protected-access]
9398 result = await func (* func_args , ** func_kwargs )
9499 if record_return :
95100 # open_span returns a FastLogfireSpan, so we can't use span.set_attribute for complex types.
96101 # This isn't great because it has to parse the JSON schema.
97102 # Not sure if making get_open_span return a LogfireSpan when record_return is True
98103 # would be faster overall or if it would be worth the added complexity.
99104 set_user_attributes_on_raw_span (span ._span , {'return' : result })
105+ if token :
106+ logfire ._current_span_var .reset (token ) # type: ignore[protected-access]
100107 return result
101108 else :
102109 # Same as the above, but without the async/await
103110 def wrapper (* func_args : P .args , ** func_kwargs : P .kwargs ) -> R :
104111 with open_span (* func_args , ** func_kwargs ) as span :
112+ token = None
113+ if uses_current_span :
114+ token = logfire ._current_span_var .set (span ) # type: ignore[protected-access]
105115 result = func (* func_args , ** func_kwargs )
106116 if record_return :
107117 set_user_attributes_on_raw_span (span ._span , {'return' : result })
118+ if token :
119+ logfire ._current_span_var .reset (token ) # type: ignore[protected-access]
108120 return result
109121
110122 wrapper = functools .wraps (func )(wrapper ) # type: ignore
@@ -118,12 +130,15 @@ def get_open_span(
118130 attributes : dict [str , otel_types .AttributeValue ],
119131 span_name : str | None ,
120132 extract_args : bool | Iterable [str ],
133+ uses_current_span : bool ,
121134 func : Callable [P , R ],
122135) -> Callable [P , AbstractContextManager [Any ]]:
123136 final_span_name : str = span_name or attributes [ATTRIBUTES_MESSAGE_TEMPLATE_KEY ] # type: ignore
124137
125138 # This is the fast case for when there are no arguments to extract
126139 def open_span (* _ : P .args , ** __ : P .kwargs ): # type: ignore
140+ if uses_current_span :
141+ return logfire ._span (final_span_name , attributes ) # type: ignore[protected-access]
127142 return logfire ._fast_span (final_span_name , attributes ) # type: ignore
128143
129144 if extract_args is True :
@@ -134,6 +149,9 @@ def open_span(*func_args: P.args, **func_kwargs: P.kwargs):
134149 bound = sig .bind (* func_args , ** func_kwargs )
135150 bound .apply_defaults ()
136151 args_dict = bound .arguments
152+ if uses_current_span :
153+ return logfire ._span (final_span_name , {** attributes , ** args_dict }) # type: ignore[protected-access]
154+
137155 return logfire ._instrument_span_with_args ( # type: ignore
138156 final_span_name , attributes , args_dict
139157 )
@@ -165,6 +183,9 @@ def open_span(*func_args: P.args, **func_kwargs: P.kwargs):
165183 # This line is the only difference from the extract_args=True case
166184 args_dict = {k : args_dict [k ] for k in extract_args_final }
167185
186+ if uses_current_span :
187+ return logfire ._span (final_span_name , {** attributes , ** args_dict }) # type: ignore[protected-access]
188+
168189 return logfire ._instrument_span_with_args ( # type: ignore
169190 final_span_name , attributes , args_dict
170191 )
0 commit comments