Skip to content

Commit 26f7999

Browse files
authored
[Core] OpenTelemetryTracer updates (Azure#40024)
* [Core] OpenTelemetryTracer updates - Add `end_on_exit` keyword argument to `start_as_current_span` - Add `use_span` class method to activate a span in the current tracing context. Signed-off-by: Paul Van Eck <[email protected]> * Update docstrings Signed-off-by: Paul Van Eck <[email protected]> --------- Signed-off-by: Paul Van Eck <[email protected]>
1 parent a2ceed3 commit 26f7999

File tree

3 files changed

+74
-6
lines changed

3 files changed

+74
-6
lines changed

sdk/core/azure-core/CHANGELOG.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Release History
22

3-
## 1.33.0 (2025-03-10)
3+
## 1.33.0 (Unreleased)
44

55
### Features Added
66

@@ -27,6 +27,8 @@
2727
- "x-ms-client-request-id" is now "az.client_request_id"
2828
- "x-ms-request-id" is now "az.service_request_id"
2929

30+
### Bugs Fixed
31+
3032
### Other Changes
3133

3234
- Added `opentelemetry-api` as an optional dependency for tracing. This can be installed with `pip install azure-core[tracing]`. #39563

sdk/core/azure-core/azure/core/tracing/opentelemetry.py

+25-5
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,10 @@ def start_as_current_span(
114114
kind: SpanKind = _SpanKind.INTERNAL,
115115
attributes: Optional[Attributes] = None,
116116
links: Optional[Sequence[Link]] = None,
117+
end_on_exit: bool = True,
117118
) -> Iterator[Span]:
118119
"""Context manager that starts a span and sets it as the current span in the context.
119120
120-
Exiting the context manager will call the span's end method.
121-
122121
.. code:: python
123122
124123
with tracer.start_as_current_span("span_name") as span:
@@ -133,13 +132,34 @@ def start_as_current_span(
133132
:paramtype attributes: Optional[Attributes]
134133
:keyword links: Links to add to the span.
135134
:paramtype links: Optional[Sequence[Link]]
135+
:keyword end_on_exit: Whether to end the span when exiting the context manager. Defaults to True.
136+
:paramtype end_on_exit: bool
136137
:return: The span that was started
137-
:rtype: ~opentelemetry.trace.Span
138+
:rtype: Iterator[~opentelemetry.trace.Span]
138139
"""
139140
span = self.start_span(name, kind=kind, attributes=attributes, links=links)
140-
with trace.use_span(span, record_exception=False, end_on_exit=True) as span: # type: ignore[attr-defined] # pylint: disable=not-context-manager
141+
with trace.use_span( # pylint: disable=not-context-manager
142+
span, record_exception=False, end_on_exit=end_on_exit
143+
) as span:
141144
yield span
142145

146+
@classmethod
147+
@contextmanager
148+
def use_span(cls, span: Span, *, end_on_exit: bool = True) -> Iterator[Span]:
149+
"""Context manager that takes a non-active span and activates it in the current context.
150+
151+
:param span: The span to set as the current span
152+
:type span: ~opentelemetry.trace.Span
153+
:keyword end_on_exit: Whether to end the span when exiting the context manager. Defaults to True.
154+
:paramtype end_on_exit: bool
155+
:return: The span that was activated.
156+
:rtype: Iterator[~opentelemetry.trace.Span]
157+
"""
158+
with trace.use_span( # pylint: disable=not-context-manager
159+
span, record_exception=False, end_on_exit=end_on_exit
160+
) as active_span:
161+
yield active_span
162+
143163
def _parse_links(self, links: Optional[Sequence[Link]]) -> Optional[Sequence[OpenTelemetryLink]]:
144164
if not links:
145165
return None
@@ -189,7 +209,7 @@ def call_with_current_context(*args, **kwargs):
189209

190210
@classmethod
191211
def get_trace_context(cls) -> Dict[str, str]:
192-
"""Returns the Trace Context header values associated with the span.
212+
"""Returns the Trace Context header values associated with the current span.
193213
194214
These are generally the W3C Trace Context headers (i.e. "traceparent" and "tracestate").
195215

sdk/core/azure-core/tests/test_tracer_otel.py

+46
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,21 @@ def test_start_span_with_attributes(tracing_helper):
9494
assert finished_spans[0].attributes["biz"] == 123
9595

9696

97+
def test_start_as_current_span_no_exit(tracing_helper):
98+
tracer = get_tracer()
99+
assert tracer
100+
101+
with tracer.start_as_current_span(name="foo-span", kind=SpanKind.INTERNAL, end_on_exit=False) as span:
102+
assert span.is_recording()
103+
assert tracer.get_current_span() == span
104+
105+
assert span.is_recording()
106+
span.end()
107+
assert not span.is_recording()
108+
finished_spans = tracing_helper.exporter.get_finished_spans()
109+
assert len(finished_spans) == 1
110+
111+
97112
def test_tracer_get_current_span():
98113
"""Test that the tracer can get the current OpenTelemetry span instance."""
99114
tracer = get_tracer()
@@ -129,6 +144,37 @@ def test_end_span(tracing_helper):
129144
assert finished_spans[0].name == "foo-span"
130145

131146

147+
def test_use_span(tracing_helper):
148+
tracer = get_tracer()
149+
assert tracer
150+
151+
assert isinstance(tracer.get_current_span(), NonRecordingSpan)
152+
with tracer.start_as_current_span(name="root", kind=SpanKind.INTERNAL) as root:
153+
span = tracer.start_span(name="foo-span", kind=SpanKind.INTERNAL)
154+
assert span.is_recording()
155+
assert tracer.get_current_span() == root
156+
157+
with tracer.use_span(span):
158+
assert tracer.get_current_span() == span
159+
160+
assert not span.is_recording()
161+
162+
span2 = tracer.start_span(name="bar-span", kind=SpanKind.INTERNAL)
163+
assert span2.is_recording()
164+
assert tracer.get_current_span() == root
165+
with tracer.use_span(span2, end_on_exit=False):
166+
assert tracer.get_current_span() == span2
167+
assert span2.is_recording()
168+
169+
assert span2.is_recording()
170+
span2.end()
171+
assert not span2.is_recording()
172+
assert tracer.get_current_span() == root
173+
174+
finished_spans = tracing_helper.exporter.get_finished_spans()
175+
assert len(finished_spans) == 3
176+
177+
132178
def test_get_trace_context():
133179
"""Test that the tracer can get the trace context and it contains the traceparent header."""
134180
tracer = get_tracer()

0 commit comments

Comments
 (0)