|
2 | 2 | # Licensed under the MIT License. |
3 | 3 |
|
4 | 4 | import asyncio |
| 5 | +import contextlib |
5 | 6 | import inspect |
6 | 7 | import logging |
7 | 8 | import os |
|
26 | 27 | TInput = TypeVar("TInput") |
27 | 28 | TOutput = TypeVar("TOutput") |
28 | 29 |
|
| 30 | +# If `opentelemetry-sdk` is available, enable the tracer |
| 31 | +try: |
| 32 | + from opentelemetry import trace |
| 33 | + from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator |
| 34 | + |
| 35 | + otel_propagator = TraceContextTextMapPropagator() |
| 36 | + otel_tracer = trace.get_tracer(__name__) |
| 37 | +except ImportError: |
| 38 | + otel_tracer = None |
| 39 | + |
| 40 | + |
29 | 41 |
|
30 | 42 | def _log_all_threads(logger: logging.Logger, context: str = ""): |
31 | 43 | """Helper function to log all currently active threads for debugging.""" |
@@ -759,31 +771,46 @@ def _execute_activity( |
759 | 771 | completionToken, |
760 | 772 | ): |
761 | 773 | instance_id = req.orchestrationInstance.instanceId |
762 | | - try: |
763 | | - executor = _ActivityExecutor(self._registry, self._logger) |
764 | | - result = executor.execute(instance_id, req.name, req.taskId, req.input.value) |
765 | | - res = pb.ActivityResponse( |
766 | | - instanceId=instance_id, |
767 | | - taskId=req.taskId, |
768 | | - result=ph.get_string_value(result), |
769 | | - completionToken=completionToken, |
770 | | - ) |
771 | | - except Exception as ex: |
772 | | - res = pb.ActivityResponse( |
773 | | - instanceId=instance_id, |
774 | | - taskId=req.taskId, |
775 | | - failureDetails=ph.new_failure_details(ex), |
776 | | - completionToken=completionToken, |
777 | | - ) |
778 | 774 |
|
779 | | - try: |
780 | | - stub.CompleteActivityTask(res) |
781 | | - except grpc.RpcError as rpc_error: # type: ignore |
782 | | - self._handle_grpc_execution_error(rpc_error, "activity") |
783 | | - except Exception as ex: |
784 | | - self._logger.exception( |
785 | | - f"Failed to deliver activity response for '{req.name}#{req.taskId}' of orchestration ID '{instance_id}' to sidecar: {ex}" |
| 775 | + if otel_tracer is not None: |
| 776 | + span_context = otel_tracer.start_as_current_span( |
| 777 | + name=f'activity: {req.name}', |
| 778 | + context=otel_propagator.extract(carrier={"traceparent": req.parentTraceContext.traceParent}), |
| 779 | + attributes={ |
| 780 | + "durabletask.task.instance_id": instance_id, |
| 781 | + "durabletask.task.id": req.taskId, |
| 782 | + "durabletask.activity.name": req.name, |
| 783 | + } |
786 | 784 | ) |
| 785 | + else: |
| 786 | + span_context = contextlib.nullcontext() |
| 787 | + |
| 788 | + with span_context: |
| 789 | + try: |
| 790 | + executor = _ActivityExecutor(self._registry, self._logger) |
| 791 | + result = executor.execute(instance_id, req.name, req.taskId, req.input.value) |
| 792 | + res = pb.ActivityResponse( |
| 793 | + instanceId=instance_id, |
| 794 | + taskId=req.taskId, |
| 795 | + result=ph.get_string_value(result), |
| 796 | + completionToken=completionToken, |
| 797 | + ) |
| 798 | + except Exception as ex: |
| 799 | + res = pb.ActivityResponse( |
| 800 | + instanceId=instance_id, |
| 801 | + taskId=req.taskId, |
| 802 | + failureDetails=ph.new_failure_details(ex), |
| 803 | + completionToken=completionToken, |
| 804 | + ) |
| 805 | + |
| 806 | + try: |
| 807 | + stub.CompleteActivityTask(res) |
| 808 | + except grpc.RpcError as rpc_error: # type: ignore |
| 809 | + self._handle_grpc_execution_error(rpc_error, "activity") |
| 810 | + except Exception as ex: |
| 811 | + self._logger.exception( |
| 812 | + f"Failed to deliver activity response for '{req.name}#{req.taskId}' of orchestration ID '{instance_id}' to sidecar: {ex}" |
| 813 | + ) |
787 | 814 |
|
788 | 815 |
|
789 | 816 | class _RuntimeOrchestrationContext( |
|
0 commit comments