Skip to content

Commit 33d0808

Browse files
torosentCopilot
andcommitted
Fix timer span parent context and update screenshots
- Timer span now correctly uses parentTraceContext as parent, linking it to the orchestration trace instead of creating a separate trace - Updated sample to include 1-second durable timer before fan-out - Updated screenshots showing 15 spans with timer span in hierarchy - Updated README with timer span documentation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3539919 commit 33d0808

File tree

9 files changed

+25
-13
lines changed

9 files changed

+25
-13
lines changed

client/src/main/java/com/microsoft/durabletask/DurableTaskGrpcClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public void raiseEvent(String instanceId, String eventName, Object eventPayload)
150150
Helpers.throwIfArgumentNull(instanceId, "instanceId");
151151
Helpers.throwIfArgumentNull(eventName, "eventName");
152152

153-
// Emit an event span matching .NET SDK's StartActivityForNewEventRaisedFromClient
153+
// Emit an event span StartActivityForNewEventRaisedFromClient
154154
TracingHelper.emitEventRaisedFromClientSpan(eventName, instanceId);
155155

156156
RaiseEventRequest.Builder builder = RaiseEventRequest.newBuilder()

client/src/main/java/com/microsoft/durabletask/TaskOrchestrationExecutor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ public void sendEvent(String instanceId, String eventName, Object eventData) {
397397
id,
398398
serializedEventData != null ? serializedEventData : "(null)"));
399399

400-
// Emit an event span matching .NET SDK's StartTraceActivityForEventRaisedFromWorker
400+
// Emit an event span StartTraceActivityForEventRaisedFromWorker
401401
TracingHelper.emitEventRaisedFromWorkerSpan(eventName, this.instanceId, instanceId);
402402
}
403403
}
@@ -744,7 +744,8 @@ public void handleTimerFired(HistoryEvent e) {
744744
this.getName(),
745745
this.instanceId,
746746
timerEventId,
747-
fireAt);
747+
fireAt,
748+
this.parentTraceContext);
748749
}
749750

750751
CompletableTask<?> task = record.getTask();

client/src/main/java/com/microsoft/durabletask/TracingHelper.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,12 +278,14 @@ static void endSpan(@Nullable Span span, @Nullable Throwable error) {
278278
* @param instanceId The orchestration instance ID.
279279
* @param timerId The timer event ID.
280280
* @param fireAt The ISO-8601 formatted fire time.
281+
* @param parentContext The parent trace context, may be {@code null}.
281282
*/
282283
static void emitTimerSpan(
283284
String orchestrationName,
284285
@Nullable String instanceId,
285286
int timerId,
286-
@Nullable String fireAt) {
287+
@Nullable String fireAt,
288+
@Nullable TraceContext parentContext) {
287289
Tracer tracer = GlobalOpenTelemetry.getTracer(TRACER_NAME);
288290
SpanBuilder spanBuilder = tracer.spanBuilder(
289291
TYPE_ORCHESTRATION + ":" + orchestrationName + ":" + TYPE_TIMER)
@@ -292,6 +294,11 @@ static void emitTimerSpan(
292294
.setAttribute(ATTR_TASK_NAME, orchestrationName)
293295
.setAttribute(ATTR_TASK_ID, String.valueOf(timerId));
294296

297+
Context parentCtx = extractTraceContext(parentContext);
298+
if (parentCtx != null) {
299+
spanBuilder.setParent(parentCtx);
300+
}
301+
295302
if (instanceId != null) {
296303
spanBuilder.setAttribute(ATTR_INSTANCE_ID, instanceId);
297304
}

client/src/test/java/com/microsoft/durabletask/TracingHelperTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ void createClientSpan_withNullParent_returnsNull() {
293293

294294
@Test
295295
void emitTimerSpan_createsInternalSpanWithFireAt() {
296-
TracingHelper.emitTimerSpan("MyOrchestration", "instance-1", 5, "2026-01-01T00:00:00Z");
296+
TracingHelper.emitTimerSpan("MyOrchestration", "instance-1", 5, "2026-01-01T00:00:00Z", null);
297297

298298
List<SpanData> spans = spanExporter.getFinishedSpanItems();
299299
assertEquals(1, spans.size());

samples/README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ The `TracingPattern` sample demonstrates the **Fan-Out/Fan-In** pattern with dis
4444
1. Configures OpenTelemetry with an OTLP exporter pointing to Jaeger
4545
2. Connects a worker and client to the DTS emulator using a connection string
4646
3. Creates a parent span (`create_orchestration:FanOutFanIn`) and schedules an orchestration
47-
4. The orchestration fans out 5 parallel `GetWeather` activities (Seattle, Tokyo, London, Paris, Sydney), fans in the results, then calls `CreateSummary` to aggregate
47+
4. The orchestration waits on a 1-second **durable timer**, then fans out 5 parallel `GetWeather` activities (Seattle, Tokyo, London, Paris, Sydney), fans in the results, then calls `CreateSummary` to aggregate
4848
5. The SDK automatically propagates trace context through the full execution chain
4949

5050
## Screenshots
@@ -60,22 +60,23 @@ Shows the trace from `durabletask-java-tracing-sample` service with spans coveri
6060
Full span hierarchy showing the fan-out/fan-in pattern with paired Client+Server spans (matching .NET SDK):
6161
- `create_orchestration:FanOutFanIn` (root, internal)
6262
- `orchestration:FanOutFanIn` (server — orchestration execution)
63+
- `orchestration:FanOutFanIn:timer` (internal — durable timer wait)
6364
- `activity:GetWeather` ×5 (client — scheduling) → `activity:GetWeather` ×5 (server — execution)
6465
- `activity:CreateSummary` (client) → `activity:CreateSummary` (server)
6566

66-
14 spans total, Depth 3 — aligned with the .NET SDK trace structure.
67+
15 spans total, Depth 3 — aligned with the .NET SDK trace structure.
6768

6869
![Jaeger trace detail](images/jaeger-full-trace-detail.png)
6970

70-
### Jaeger — Span Attributes
71+
### Jaeger — Span Attributes (Timer)
7172

72-
Activity span showing attributes aligned with the .NET SDK schema:
73-
- `durabletask.type=activity`
74-
- `durabletask.task.name=GetWeather`
73+
Timer span showing attributes aligned with the .NET SDK schema:
74+
- `durabletask.type=timer`
75+
- `durabletask.task.name=FanOutFanIn`
76+
- `durabletask.fire_at=2026-03-03T21:13:15.142Z`
7577
- `durabletask.task.instance_id=<orchestrationId>`
76-
- `durabletask.task.task_id=0`
7778
- `otel.scope.name=Microsoft.DurableTask`
78-
- `span.kind=server`
79+
- `span.kind=internal`
7980

8081
![Jaeger span detail](images/jaeger-span-detail.png)
8182

3.21 KB
Loading
3.17 KB
Loading
-2.73 KB
Loading

samples/src/main/java/io/durabletask/samples/TracingPattern.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ public static void main(String[] args) throws IOException, InterruptedException,
9595
@Override
9696
public TaskOrchestration create() {
9797
return ctx -> {
98+
// Timer: wait briefly (demonstrates timer span)
99+
ctx.createTimer(Duration.ofSeconds(1)).await();
100+
98101
// Fan-out: schedule multiple parallel activities
99102
List<Task<String>> parallelTasks = new java.util.ArrayList<>();
100103
String[] cities = {"Seattle", "Tokyo", "London", "Paris", "Sydney"};

0 commit comments

Comments
 (0)