@@ -192,46 +192,47 @@ public void startAndBlock() {
192192 ? startedEvent .getParentTraceContext () : null ;
193193 String orchName = startedEvent != null ? startedEvent .getName () : "" ;
194194
195- TaskOrchestratorResult taskOrchestratorResult ;
196- try {
197- taskOrchestratorResult = taskOrchestrationExecutor .execute (
198- orchestratorRequest .getPastEventsList (),
199- orchestratorRequest .getNewEventsList (),
200- null );
201- } catch (Throwable e ) {
202- if (e instanceof Error ) {
203- throw (Error ) e ;
204- }
205- throw new RuntimeException (e );
206- }
207-
208- // Emit orchestration span only on completion/termination.
209- // Uses ExecutionStartedEvent timestamp as span start time for full lifecycle coverage.
210- // Note: Java OTel doesn't support SetSpanId(), so we can't make this span
211- // the parent of child spans across dispatches like .NET does. Child spans
212- // (activities, timers) are siblings under the create_orchestration span.
213- boolean isCompleting = taskOrchestratorResult .getActions ().stream ()
214- .anyMatch (a -> a .getOrchestratorActionTypeCase () == OrchestratorAction .OrchestratorActionTypeCase .COMPLETEORCHESTRATION
215- || a .getOrchestratorActionTypeCase () == OrchestratorAction .OrchestratorActionTypeCase .TERMINATEORCHESTRATION );
216-
217- if (isCompleting && orchTraceCtx != null ) {
195+ // Start the orchestration span BEFORE execution so child spans
196+ // (activities, timers) are nested under it in the trace hierarchy.
197+ // Each dispatch creates its own orchestration span (matching JS/dotnet behavior).
198+ Span orchestrationSpan = null ;
199+ TraceContext orchestrationSpanContext = null ;
200+ if (orchTraceCtx != null ) {
218201 Map <String , String > orchSpanAttrs = new HashMap <>();
219202 orchSpanAttrs .put (TracingHelper .ATTR_TYPE , TracingHelper .TYPE_ORCHESTRATION );
220203 orchSpanAttrs .put (TracingHelper .ATTR_TASK_NAME , orchName );
221204 orchSpanAttrs .put (TracingHelper .ATTR_INSTANCE_ID , orchestratorRequest .getInstanceId ());
222205
223206 Instant spanStartTime = null ;
224- if (startedHistoryEvent .hasTimestamp ()) {
207+ if (startedHistoryEvent != null && startedHistoryEvent .hasTimestamp ()) {
225208 spanStartTime = DataConverter .getInstantFromTimestamp (
226209 startedHistoryEvent .getTimestamp ());
227210 }
228211
229- Span orchestrationSpan = TracingHelper .startSpanWithStartTime (
212+ orchestrationSpan = TracingHelper .startSpanWithStartTime (
230213 TracingHelper .TYPE_ORCHESTRATION + ":" + orchName ,
231214 orchTraceCtx ,
232215 SpanKind .SERVER ,
233216 orchSpanAttrs ,
234217 spanStartTime );
218+ orchestrationSpanContext = TracingHelper .getCurrentTraceContext (orchestrationSpan );
219+ }
220+
221+ TaskOrchestratorResult taskOrchestratorResult ;
222+ try {
223+ taskOrchestratorResult = taskOrchestrationExecutor .execute (
224+ orchestratorRequest .getPastEventsList (),
225+ orchestratorRequest .getNewEventsList (),
226+ orchestrationSpanContext );
227+ } catch (Throwable e ) {
228+ if (e instanceof Error ) {
229+ throw (Error ) e ;
230+ }
231+ throw new RuntimeException (e );
232+ }
233+
234+ // End the orchestration span for every dispatch
235+ if (orchestrationSpan != null ) {
235236 orchestrationSpan .end ();
236237 }
237238
0 commit comments