Skip to content

Commit 510dbd2

Browse files
committed
wip
1 parent 9bf28fb commit 510dbd2

File tree

9 files changed

+131
-97
lines changed

9 files changed

+131
-97
lines changed

Diff for: CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
### Added
1010
- Add `max_attr_value_length` support to Jaeger exporter
1111
([#1633])(https://github.com/open-telemetry/opentelemetry-python/pull/1633)
12+
- Moved `use_span` from Tracer to `opentelemetry.trace.use_span`.
13+
([#1630](https://github.com/open-telemetry/opentelemetry-python/pull/1630))
14+
- `opentelemetry.trace.use_span()` will now overwrite previously set status on span in case an
15+
exception is raised inside the context manager and `set_status_on_exception` is set to `True`.
16+
([#1630](https://github.com/open-telemetry/opentelemetry-python/pull/1630))
1217

1318
### Changed
1419
- Rename `IdsGenerator` to `IdGenerator`

Diff for: docs/examples/basic_context/async_context.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525

2626
async def async_span(span):
27-
with tracer.use_span(span):
27+
with trace.use_span(span):
2828
ctx = baggage.set_baggage("foo", "bar")
2929
return ctx
3030

Diff for: opentelemetry-api/src/opentelemetry/trace/__init__.py

+40-31
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,11 @@
8080
from logging import getLogger
8181
from typing import Iterator, Optional, Sequence, cast
8282

83+
from opentelemetry import context as context_api
8384
from opentelemetry.context.context import Context
8485
from opentelemetry.environment_variables import OTEL_PYTHON_TRACER_PROVIDER
8586
from opentelemetry.trace.propagation import (
87+
SPAN_KEY,
8688
get_current_span,
8789
set_span_in_context,
8890
)
@@ -101,7 +103,7 @@
101103
format_span_id,
102104
format_trace_id,
103105
)
104-
from opentelemetry.trace.status import Status
106+
from opentelemetry.trace.status import Status, StatusCode
105107
from opentelemetry.util import types
106108
from opentelemetry.util._providers import _load_provider
107109

@@ -324,7 +326,7 @@ def start_as_current_span(
324326
is equivalent to::
325327
326328
span = tracer.start_span(name)
327-
with tracer.use_span(span, end_on_exit=True):
329+
with opentelemetry.trace.use_span(span, end_on_exit=True):
328330
do_work()
329331
330332
Args:
@@ -350,28 +352,6 @@ def start_as_current_span(
350352
The newly-created span.
351353
"""
352354

353-
@contextmanager # type: ignore
354-
@abstractmethod
355-
def use_span(
356-
self, span: "Span", end_on_exit: bool = False,
357-
) -> Iterator[None]:
358-
"""Context manager for setting the passed span as the
359-
current span in the context, as well as resetting the
360-
context back upon exiting the context manager.
361-
362-
Set the given span as the current span in this tracer's context.
363-
364-
On exiting the context manager set the span that was previously active
365-
as the current span (this is usually but not necessarily the parent of
366-
the given span). If ``end_on_exit`` is ``True``, then the span is also
367-
ended when exiting the context manager.
368-
369-
Args:
370-
span: The span to start and make current.
371-
end_on_exit: Whether to end the span automatically when leaving the
372-
context manager.
373-
"""
374-
375355

376356
class DefaultTracer(Tracer):
377357
"""The default Tracer, used when no Tracer implementation is available.
@@ -409,13 +389,6 @@ def start_as_current_span(
409389
# pylint: disable=unused-argument,no-self-use
410390
yield INVALID_SPAN
411391

412-
@contextmanager # type: ignore
413-
def use_span(
414-
self, span: "Span", end_on_exit: bool = False,
415-
) -> Iterator[None]:
416-
# pylint: disable=unused-argument,no-self-use
417-
yield
418-
419392

420393
_TRACER_PROVIDER = None
421394

@@ -466,6 +439,41 @@ def get_tracer_provider() -> TracerProvider:
466439
return _TRACER_PROVIDER
467440

468441

442+
@contextmanager # type: ignore
443+
def use_span(
444+
span: Span,
445+
end_on_exit: bool = False,
446+
record_exception: bool = True,
447+
set_status_on_exception: bool = True,
448+
) -> Iterator[Span]:
449+
try:
450+
token = context_api.attach(context_api.set_value(SPAN_KEY, span))
451+
try:
452+
yield span
453+
finally:
454+
context_api.detach(token)
455+
456+
except Exception as exc: # pylint: disable=broad-except
457+
if isinstance(span, Span) and span.is_recording():
458+
# Record the exception as an event
459+
if record_exception:
460+
span.record_exception(exc)
461+
462+
# Set status in case exception was raised
463+
if set_status_on_exception:
464+
span.set_status(
465+
Status(
466+
status_code=StatusCode.ERROR,
467+
description="{}: {}".format(type(exc).__name__, exc),
468+
)
469+
)
470+
raise
471+
472+
finally:
473+
if end_on_exit:
474+
span.end()
475+
476+
469477
__all__ = [
470478
"DEFAULT_TRACE_OPTIONS",
471479
"DEFAULT_TRACE_STATE",
@@ -492,5 +500,6 @@ def get_tracer_provider() -> TracerProvider:
492500
"get_tracer_provider",
493501
"set_tracer_provider",
494502
"set_span_in_context",
503+
"use_span",
495504
"Status",
496505
]

Diff for: opentelemetry-api/src/opentelemetry/trace/propagation/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414
from typing import Optional
1515

16-
from opentelemetry.context import get_value, set_value
16+
from opentelemetry.context import attach, get_value, set_value
1717
from opentelemetry.context.context import Context
1818
from opentelemetry.trace.span import INVALID_SPAN, Span
1919

Diff for: opentelemetry-api/tests/trace/test_globals.py

+71
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,27 @@
22
from unittest.mock import patch
33

44
from opentelemetry import context, trace
5+
from opentelemetry.trace.status import Status, StatusCode
6+
7+
8+
class TestSpan(trace.NonRecordingSpan):
9+
has_ended = False
10+
recorded_exception = None
11+
recorded_status = Status(status_code=StatusCode.UNSET)
12+
13+
def set_status(self, status):
14+
self.recorded_status = status
15+
16+
def end(self, end_time=None):
17+
self.has_ended = True
18+
19+
def is_recording(self):
20+
return not self.has_ended
21+
22+
def record_exception(
23+
self, exception, attributes=None, timestamp=None, escaped=False
24+
):
25+
self.recorded_exception = exception
526

627

728
class TestGlobals(unittest.TestCase):
@@ -38,3 +59,53 @@ def test_get_current_span(self):
3859
finally:
3960
context.detach(token)
4061
self.assertEqual(trace.get_current_span(), trace.INVALID_SPAN)
62+
63+
64+
class TestUseTracer(unittest.TestCase):
65+
def test_use_span(self):
66+
self.assertEqual(trace.get_current_span(), trace.INVALID_SPAN)
67+
span = trace.NonRecordingSpan(trace.INVALID_SPAN_CONTEXT)
68+
with trace.use_span(span):
69+
self.assertIs(trace.get_current_span(), span)
70+
self.assertEqual(trace.get_current_span(), trace.INVALID_SPAN)
71+
72+
def test_use_span_end_on_exit(self):
73+
74+
test_span = TestSpan(trace.INVALID_SPAN_CONTEXT)
75+
76+
with trace.use_span(test_span):
77+
pass
78+
self.assertFalse(test_span.has_ended)
79+
80+
with trace.use_span(test_span, end_on_exit=True):
81+
pass
82+
self.assertTrue(test_span.has_ended)
83+
84+
def test_use_span_exception(self):
85+
class TestUseSpanException(Exception):
86+
pass
87+
88+
test_span = TestSpan(trace.INVALID_SPAN_CONTEXT)
89+
exception = TestUseSpanException("test exception")
90+
with self.assertRaises(TestUseSpanException):
91+
with trace.use_span(test_span):
92+
raise exception
93+
94+
self.assertEqual(test_span.recorded_exception, exception)
95+
96+
def test_use_span_set_status(self):
97+
class TestUseSpanException(Exception):
98+
pass
99+
100+
test_span = TestSpan(trace.INVALID_SPAN_CONTEXT)
101+
with self.assertRaises(TestUseSpanException):
102+
with trace.use_span(test_span):
103+
raise TestUseSpanException("test error")
104+
105+
self.assertEqual(
106+
test_span.recorded_status.status_code, StatusCode.ERROR
107+
)
108+
self.assertEqual(
109+
test_span.recorded_status.description,
110+
"TestUseSpanException: test error",
111+
)

Diff for: opentelemetry-api/tests/trace/test_tracer.py

-5
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,6 @@ def test_start_as_current_span(self):
2929
with self.tracer.start_as_current_span("") as span:
3030
self.assertIsInstance(span, trace.Span)
3131

32-
def test_use_span(self):
33-
span = trace.NonRecordingSpan(trace.INVALID_SPAN_CONTEXT)
34-
with self.tracer.use_span(span):
35-
pass
36-
3732
def test_get_current_span(self):
3833
with self.tracer.start_as_current_span("test") as span:
3934
trace.get_current_span().set_attribute("test", "test")

Diff for: opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py

+6-39
Original file line numberDiff line numberDiff line change
@@ -869,7 +869,12 @@ def start_as_current_span(
869869
record_exception=record_exception,
870870
set_status_on_exception=set_status_on_exception,
871871
)
872-
with self.use_span(span, end_on_exit=end_on_exit) as span_context:
872+
with trace_api.use_span(
873+
span,
874+
end_on_exit=end_on_exit,
875+
record_exception=record_exception,
876+
set_status_on_exception=set_status_on_exception,
877+
) as span_context:
873878
yield span_context
874879

875880
def start_span( # pylint: disable=too-many-locals
@@ -950,44 +955,6 @@ def start_span( # pylint: disable=too-many-locals
950955
span = trace_api.NonRecordingSpan(context=span_context)
951956
return span
952957

953-
@contextmanager
954-
def use_span(
955-
self, span: trace_api.Span, end_on_exit: bool = False,
956-
) -> Iterator[trace_api.Span]:
957-
try:
958-
token = context_api.attach(context_api.set_value(SPAN_KEY, span))
959-
try:
960-
yield span
961-
finally:
962-
context_api.detach(token)
963-
964-
except Exception as exc: # pylint: disable=broad-except
965-
# Record the exception as an event
966-
if isinstance(span, Span) and span.is_recording():
967-
# pylint:disable=protected-access
968-
if span._record_exception:
969-
span.record_exception(exc)
970-
971-
# Records status if use_span is used
972-
# i.e. with tracer.start_as_current_span() as span:
973-
if (
974-
span._status.status_code is StatusCode.UNSET
975-
and span._set_status_on_exception
976-
):
977-
span.set_status(
978-
Status(
979-
status_code=StatusCode.ERROR,
980-
description="{}: {}".format(
981-
type(exc).__name__, exc
982-
),
983-
)
984-
)
985-
raise
986-
987-
finally:
988-
if end_on_exit:
989-
span.end()
990-
991958

992959
class TracerProvider(trace_api.TracerProvider):
993960
"""See `opentelemetry.trace.TracerProvider`."""

Diff for: opentelemetry-sdk/tests/trace/test_trace.py

+2-14
Original file line numberDiff line numberDiff line change
@@ -122,18 +122,6 @@ def run_general_code(shutdown_on_exit, explicit_shutdown):
122122
out = run_general_code(False, False)
123123
self.assertTrue(out.startswith(b"0"))
124124

125-
def test_use_span_exception(self):
126-
class TestUseSpanException(Exception):
127-
pass
128-
129-
default_span = trace_api.NonRecordingSpan(
130-
trace_api.INVALID_SPAN_CONTEXT
131-
)
132-
tracer = new_tracer()
133-
with self.assertRaises(TestUseSpanException):
134-
with tracer.use_span(default_span):
135-
raise TestUseSpanException()
136-
137125
def test_tracer_provider_accepts_concurrent_multi_span_processor(self):
138126
span_processor = trace.ConcurrentMultiSpanProcessor(2)
139127
tracer_provider = trace.TracerProvider(
@@ -307,7 +295,7 @@ def test_start_span_implicit(self):
307295
self.assertIsNone(root.end_time)
308296
self.assertEqual(root.kind, trace_api.SpanKind.INTERNAL)
309297

310-
with tracer.use_span(root, True):
298+
with trace_api.use_span(root, True):
311299
self.assertIs(trace_api.get_current_span(), root)
312300

313301
with tracer.start_span(
@@ -364,7 +352,7 @@ def test_start_span_explicit(self):
364352
self.assertIsNone(root.end_time)
365353

366354
# Test with the implicit root span
367-
with tracer.use_span(root, True):
355+
with trace_api.use_span(root, True):
368356
self.assertIs(trace_api.get_current_span(), root)
369357

370358
with tracer.start_span("stepchild", other_parent_context) as child:

Diff for: shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/__init__.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
TracerProvider,
113113
get_current_span,
114114
set_span_in_context,
115+
use_span,
115116
)
116117
from opentelemetry.util.types import Attributes
117118

@@ -322,7 +323,7 @@ class ScopeShim(Scope):
322323
It is necessary to have both ways for constructing `ScopeShim` objects
323324
because in some cases we need to create the object from an OpenTelemetry
324325
`opentelemetry.trace.Span` context manager (as returned by
325-
:meth:`opentelemetry.trace.Tracer.use_span`), in which case our only way of
326+
:meth:`opentelemetry.trace.use_span`), in which case our only way of
326327
retrieving a `opentelemetry.trace.Span` object is by calling the
327328
``__enter__()`` method on the context manager, which makes the span active
328329
in the OpenTelemetry tracer; whereas in other cases we need to accept a
@@ -365,7 +366,7 @@ def from_context_manager(cls, manager: "ScopeManagerShim", span_cm):
365366
Example usage::
366367
367368
span = otel_tracer.start_span("TestSpan")
368-
span_cm = otel_tracer.use_span(span)
369+
span_cm = opentelemetry.trace.use_span(span)
369370
scope_shim = ScopeShim.from_context_manager(
370371
scope_manager_shim,
371372
span_cm=span_cm,
@@ -375,7 +376,7 @@ def from_context_manager(cls, manager: "ScopeManagerShim", span_cm):
375376
manager: The :class:`ScopeManagerShim` that created this
376377
:class:`ScopeShim`.
377378
span_cm: A context manager as returned by
378-
:meth:`opentelemetry.trace.Tracer.use_span`.
379+
:meth:`opentelemetry.trace.use_span`.
379380
"""
380381

381382
otel_span = span_cm.__enter__()
@@ -452,9 +453,7 @@ def activate(self, span: SpanShim, finish_on_close: bool) -> "ScopeShim":
452453
A :class:`ScopeShim` representing the activated span.
453454
"""
454455

455-
span_cm = self._tracer.unwrap().use_span(
456-
span.unwrap(), end_on_exit=finish_on_close
457-
)
456+
span_cm = use_span(span.unwrap(), end_on_exit=finish_on_close)
458457
return ScopeShim.from_context_manager(self, span_cm=span_cm)
459458

460459
@property

0 commit comments

Comments
 (0)