|
32 | 32 | import durabletask.internal.orchestrator_service_pb2 as pb |
33 | 33 | import durabletask.internal.orchestrator_service_pb2_grpc as stubs |
34 | 34 | import durabletask.internal.shared as shared |
| 35 | +import durabletask.internal.tracing as tracing |
35 | 36 | from durabletask import task |
36 | 37 | from durabletask.internal.grpc_interceptor import DefaultClientInterceptorImpl |
37 | 38 |
|
@@ -641,6 +642,7 @@ def _execute_orchestrator( |
641 | 642 | actions=result.actions, |
642 | 643 | customStatus=ph.get_string_value(result.encoded_custom_status), |
643 | 644 | completionToken=completionToken, |
| 645 | + orchestrationTraceContext=req.orchestrationTraceContext, |
644 | 646 | ) |
645 | 647 | except pe.AbandonOrchestrationError: |
646 | 648 | self._logger.info( |
@@ -697,9 +699,20 @@ def _execute_activity( |
697 | 699 | instance_id = req.orchestrationInstance.instanceId |
698 | 700 | try: |
699 | 701 | executor = _ActivityExecutor(self._registry, self._logger) |
700 | | - result = executor.execute( |
701 | | - instance_id, req.name, req.taskId, req.input.value |
702 | | - ) |
| 702 | + with tracing.start_span( |
| 703 | + f"activity:{req.name}", |
| 704 | + trace_context=req.parentTraceContext, |
| 705 | + attributes={"durabletask.task.instance_id": instance_id, |
| 706 | + "durabletask.task.name": req.name, |
| 707 | + "durabletask.task.task_id": str(req.taskId)}, |
| 708 | + ) as span: |
| 709 | + try: |
| 710 | + result = executor.execute( |
| 711 | + instance_id, req.name, req.taskId, req.input.value |
| 712 | + ) |
| 713 | + except Exception as ex: |
| 714 | + tracing.set_span_error(span, ex) |
| 715 | + raise |
703 | 716 | res = pb.ActivityResponse( |
704 | 717 | instanceId=instance_id, |
705 | 718 | taskId=req.taskId, |
@@ -759,30 +772,41 @@ def _execute_entity_batch( |
759 | 772 |
|
760 | 773 | operation_result = None |
761 | 774 |
|
762 | | - try: |
763 | | - entity_result = executor.execute( |
764 | | - instance_id, entity_instance_id, operation.operation, entity_state, operation.input.value |
765 | | - ) |
766 | | - |
767 | | - entity_result = ph.get_string_value_or_empty(entity_result) |
768 | | - operation_result = pb.OperationResult(success=pb.OperationResultSuccess( |
769 | | - result=entity_result, |
770 | | - startTimeUtc=new_timestamp(start_time), |
771 | | - endTimeUtc=new_timestamp(datetime.now(timezone.utc)) |
772 | | - )) |
773 | | - results.append(operation_result) |
| 775 | + # Get the trace context for this operation, if available |
| 776 | + op_trace_ctx = operation.traceContext if operation.HasField("traceContext") else None |
774 | 777 |
|
775 | | - entity_state.commit() |
776 | | - except Exception as ex: |
777 | | - self._logger.exception(ex) |
778 | | - operation_result = pb.OperationResult(failure=pb.OperationResultFailure( |
779 | | - failureDetails=ph.new_failure_details(ex), |
780 | | - startTimeUtc=new_timestamp(start_time), |
781 | | - endTimeUtc=new_timestamp(datetime.now(timezone.utc)) |
782 | | - )) |
783 | | - results.append(operation_result) |
| 778 | + with tracing.start_span( |
| 779 | + f"entity:{entity_instance_id.entity}:{operation.operation}", |
| 780 | + trace_context=op_trace_ctx, |
| 781 | + attributes={"durabletask.entity.instance_id": instance_id, |
| 782 | + "durabletask.entity.name": entity_instance_id.entity, |
| 783 | + "durabletask.entity.operation": operation.operation}, |
| 784 | + ) as span: |
| 785 | + try: |
| 786 | + entity_result = executor.execute( |
| 787 | + instance_id, entity_instance_id, operation.operation, entity_state, operation.input.value |
| 788 | + ) |
784 | 789 |
|
785 | | - entity_state.rollback() |
| 790 | + entity_result = ph.get_string_value_or_empty(entity_result) |
| 791 | + operation_result = pb.OperationResult(success=pb.OperationResultSuccess( |
| 792 | + result=entity_result, |
| 793 | + startTimeUtc=new_timestamp(start_time), |
| 794 | + endTimeUtc=new_timestamp(datetime.now(timezone.utc)) |
| 795 | + )) |
| 796 | + results.append(operation_result) |
| 797 | + |
| 798 | + entity_state.commit() |
| 799 | + except Exception as ex: |
| 800 | + tracing.set_span_error(span, ex) |
| 801 | + self._logger.exception(ex) |
| 802 | + operation_result = pb.OperationResult(failure=pb.OperationResultFailure( |
| 803 | + failureDetails=ph.new_failure_details(ex), |
| 804 | + startTimeUtc=new_timestamp(start_time), |
| 805 | + endTimeUtc=new_timestamp(datetime.now(timezone.utc)) |
| 806 | + )) |
| 807 | + results.append(operation_result) |
| 808 | + |
| 809 | + entity_state.rollback() |
786 | 810 |
|
787 | 811 | batch_result = pb.EntityBatchResult( |
788 | 812 | results=results, |
@@ -847,6 +871,7 @@ def __init__(self, instance_id: str, registry: _Registry): |
847 | 871 | self._new_input: Optional[Any] = None |
848 | 872 | self._save_events = False |
849 | 873 | self._encoded_custom_status: Optional[str] = None |
| 874 | + self._parent_trace_context: Optional[pb.TraceContext] = None |
850 | 875 |
|
851 | 876 | def run(self, generator: Generator[task.Task, Any, Any]): |
852 | 877 | self._generator = generator |
@@ -1136,15 +1161,18 @@ def call_activity_function_helper( |
1136 | 1161 | if isinstance(activity_function, str) |
1137 | 1162 | else task.get_name(activity_function) |
1138 | 1163 | ) |
1139 | | - action = ph.new_schedule_task_action(id, name, encoded_input, tags) |
| 1164 | + action = ph.new_schedule_task_action( |
| 1165 | + id, name, encoded_input, tags, |
| 1166 | + parent_trace_context=self._parent_trace_context) |
1140 | 1167 | else: |
1141 | 1168 | if instance_id is None: |
1142 | 1169 | # Create a deteministic instance ID based on the parent instance ID |
1143 | 1170 | instance_id = f"{self.instance_id}:{id:04x}" |
1144 | 1171 | if not isinstance(activity_function, str): |
1145 | 1172 | raise ValueError("Orchestrator function name must be a string") |
1146 | 1173 | action = ph.new_create_sub_orchestration_action( |
1147 | | - id, activity_function, instance_id, encoded_input, version |
| 1174 | + id, activity_function, instance_id, encoded_input, version, |
| 1175 | + parent_trace_context=self._parent_trace_context |
1148 | 1176 | ) |
1149 | 1177 | self._pending_actions[id] = action |
1150 | 1178 |
|
@@ -1397,6 +1425,10 @@ def process_event( |
1397 | 1425 | if event.executionStarted.version: |
1398 | 1426 | ctx._version = event.executionStarted.version.value |
1399 | 1427 |
|
| 1428 | + # Store the parent trace context for propagation to child tasks |
| 1429 | + if event.executionStarted.HasField("parentTraceContext"): |
| 1430 | + ctx._parent_trace_context = event.executionStarted.parentTraceContext |
| 1431 | + |
1400 | 1432 | if self._registry.versioning: |
1401 | 1433 | version_failure = self.evaluate_orchestration_versioning( |
1402 | 1434 | self._registry.versioning, |
|
0 commit comments