|
25 | 25 | import opentelemetry.trace |
26 | 26 | import opentelemetry.trace.propagation.tracecontext |
27 | 27 | import opentelemetry.util.types |
| 28 | +from opentelemetry.context import Context |
| 29 | +from opentelemetry.trace import SpanKind, _Links, Span, StatusCode, Status |
| 30 | +from opentelemetry.util import types |
28 | 31 | from typing_extensions import Protocol, TypeAlias, TypedDict |
29 | 32 |
|
30 | 33 | import temporalio.activity |
|
34 | 37 | import temporalio.exceptions |
35 | 38 | import temporalio.worker |
36 | 39 | import temporalio.workflow |
| 40 | +from temporalio.exceptions import ApplicationError, ApplicationErrorCategory |
37 | 41 |
|
38 | 42 | # OpenTelemetry dynamically, lazily chooses its context implementation at |
39 | 43 | # runtime. When first accessed, they use pkg_resources.iter_entry_points + load. |
@@ -661,6 +665,50 @@ def start_local_activity( |
661 | 665 | return super().start_local_activity(input) |
662 | 666 |
|
663 | 667 |
|
| 668 | +class TemporalTracer(opentelemetry.trace.Tracer): |
| 669 | + def __init__(self, tracer: opentelemetry.trace.Tracer): |
| 670 | + self._tracer = tracer |
| 671 | + super().__init__() |
| 672 | + |
| 673 | + def start_span(self, name: str, context: Optional[Context] = None, kind: SpanKind = SpanKind.INTERNAL, |
| 674 | + attributes: types.Attributes = None, links: _Links = None, start_time: Optional[int] = None, |
| 675 | + record_exception: bool = True, set_status_on_exception: bool = True) -> Span: |
| 676 | + return self._tracer.start_span(name, context, kind, attributes, links, start_time, record_exception, set_status_on_exception) |
| 677 | + |
| 678 | + @staticmethod |
| 679 | + def handle_exception(exc: Exception, span: Span, record_exception: bool, set_status_on_exception: bool) -> None: |
| 680 | + if record_exception: |
| 681 | + span.record_exception(exc) |
| 682 | + |
| 683 | + # Set status in case exception was raised |
| 684 | + if set_status_on_exception: |
| 685 | + span.set_status( |
| 686 | + Status( |
| 687 | + status_code=StatusCode.ERROR, |
| 688 | + description=f"{type(exc).__name__}: {exc}", |
| 689 | + ) |
| 690 | + ) |
| 691 | + |
| 692 | + @contextmanager |
| 693 | + def start_as_current_span(self, name: str, context: Optional[Context] = None, kind: SpanKind = SpanKind.INTERNAL, |
| 694 | + attributes: types.Attributes = None, links: _Links = None, |
| 695 | + start_time: Optional[int] = None, record_exception: bool = True, |
| 696 | + set_status_on_exception: bool = True, end_on_exit: bool = True) -> Iterator[Span]: |
| 697 | + |
| 698 | + with self._tracer.start_as_current_span(name, context, kind, attributes, links, start_time, False, False, end_on_exit) as span: |
| 699 | + try: |
| 700 | + yield span |
| 701 | + |
| 702 | + # TODO: Catch base exception and handle cancellation errors |
| 703 | + except ApplicationError as exc: |
| 704 | + if exc.category != ApplicationErrorCategory.BENIGN: |
| 705 | + self.handle_exception(exc, span, record_exception, set_status_on_exception) |
| 706 | + raise |
| 707 | + except Exception as exc: |
| 708 | + self.handle_exception(exc, span, record_exception, set_status_on_exception) |
| 709 | + raise |
| 710 | + |
| 711 | + |
664 | 712 | class workflow: |
665 | 713 | """Contains static methods that are safe to call from within a workflow. |
666 | 714 |
|
|
0 commit comments