Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@

<!-- Microsoft.Extensions.* Packages -->
<ItemGroup>
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="10.0.2" />
</ItemGroup>

<!-- Azure.* Packages -->
Expand All @@ -42,11 +42,11 @@
<PackageVersion Include="System.ComponentModel.Annotations" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<PackageVersion Include="Google.Protobuf" Version="3.33.2" />
<PackageVersion Include="Google.Protobuf" Version="3.33.5" />
<PackageVersion Include="Grpc.Core" Version="2.46.6" />
<PackageVersion Include="Grpc.Net.Client" Version="2.67.0" />
<PackageVersion Include="Grpc.Tools" Version="2.76.0" />
<PackageVersion Include="Grpc.AspNetCore.Server" Version="2.71.0" />
<PackageVersion Include="Grpc.Net.Client" Version="2.76.0" />
<PackageVersion Include="Grpc.Tools" Version="2.78.0" />
<PackageVersion Include="Grpc.AspNetCore.Server" Version="2.76.0" />
</ItemGroup>

<!-- Microsoft.CodeAnalysis.* Packages -->
Expand Down Expand Up @@ -82,9 +82,9 @@
<PackageVersion Include="DotNext" Version="4.13.1" Condition="'$(TargetFramework)' != 'net8.0' AND '$(TargetFramework)' != 'net10.0'" />
<PackageVersion Include="DotNext" Version="5.19.0" Condition="'$(TargetFramework)' == 'net8.0' OR '$(TargetFramework)' == 'net10.0'" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.1" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.2" />
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
<PackageVersion Include="System.Text.Json" Version="10.0.0" />
<PackageVersion Include="System.Text.Json" Version="10.0.2" />
<PackageVersion Include="System.Collections.Immutable" Version="8.0.0" />
</ItemGroup>

Expand Down
366 changes: 366 additions & 0 deletions Microsoft.DurableTask.sln

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion samples/AzureFunctionsApp/AzureFunctionsApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Google.Protobuf" VersionOverride="3.33.2" />
<PackageReference Include="Google.Protobuf" VersionOverride="3.33.5" />
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Google.Protobuf is centrally versioned (and already updated to 3.33.5). This VersionOverride matches the central version, so it's redundant—consider removing it and letting central package management control the version.

Suggested change
<PackageReference Include="Google.Protobuf" VersionOverride="3.33.5" />
<PackageReference Include="Google.Protobuf" />

Copilot uses AI. Check for mistakes.
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
9 changes: 6 additions & 3 deletions samples/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@

<!-- Functions sample packages -->
<ItemGroup>
<PackageVersion Update="coverlet.collector" Version="6.0.0" />
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" />
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.16.2" />
</ItemGroup>

<!-- Durable sample packages -->
Expand All @@ -15,4 +12,10 @@
<PackageVersion Include="Microsoft.DurableTask.Worker.Grpc" Version="1.5.0" />
</ItemGroup>

<!-- OpenTelemetry packages -->
<ItemGroup>
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.0" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.15.0" />
</ItemGroup>

</Project>
22 changes: 22 additions & 0 deletions samples/DistributedTracingSample/DistributedTracingSample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" />
</ItemGroup>

<ItemGroup>
<!-- Using p2p references so we can show latest changes in samples. -->
<ProjectReference Include="$(SrcRoot)Client/AzureManaged/Client.AzureManaged.csproj" />
<ProjectReference Include="$(SrcRoot)Worker/AzureManaged/Worker.AzureManaged.csproj" />
<ProjectReference Include="$(SrcRoot)Analyzers/Analyzers.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>

</Project>
104 changes: 104 additions & 0 deletions samples/DistributedTracingSample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// This sample demonstrates how to configure OpenTelemetry distributed tracing with the Durable Task SDK.
// Traces are exported to Jaeger via OTLP, allowing you to visualize orchestration execution in the Jaeger UI.

using Microsoft.DurableTask;
using Microsoft.DurableTask.Client;
using Microsoft.DurableTask.Client.AzureManaged;
using Microsoft.DurableTask.Worker;
using Microsoft.DurableTask.Worker.AzureManaged;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

// Read the DTS emulator connection string from configuration.
string connectionString = builder.Configuration.GetValue<string>("DURABLE_TASK_SCHEDULER_CONNECTION_STRING")
?? throw new InvalidOperationException("DURABLE_TASK_SCHEDULER_CONNECTION_STRING is not set.");
// Configure OpenTelemetry tracing.
// The Durable Task SDK automatically emits traces using the "Microsoft.DurableTask" ActivitySource.
// We subscribe to that source and export traces to Jaeger via OTLP.
builder.Services.AddOpenTelemetry()
.ConfigureResource(resource => resource.AddService("DistributedTracingSample"))
.WithTracing(tracing =>
{
tracing.AddSource("Microsoft.DurableTask");
tracing.AddOtlpExporter();
});

// Configure the Durable Task worker with a fan-out/fan-in orchestration and activities.
builder.Services.AddDurableTaskWorker(workerBuilder =>
{
workerBuilder.AddTasks(tasks =>
{
tasks.AddOrchestratorFunc("FanOutFanIn", async context =>
{
// Fan-out: schedule multiple activity calls in parallel.
string[] cities = ["Tokyo", "London", "Seattle", "Paris", "Sydney"];
List<Task<string>> parallelTasks = new();
foreach (string city in cities)
{
parallelTasks.Add(context.CallActivityAsync<string>("GetWeather", city));
}

// Fan-in: wait for all activities to complete.
string[] results = await Task.WhenAll(parallelTasks);

// Aggregate results in a final activity.
string summary = await context.CallActivityAsync<string>("CreateSummary", results);
return summary;
});

tasks.AddActivityFunc<string, string>("GetWeather", (context, city) =>
{
// Simulate fetching weather data for a city.
// Use uint cast to avoid negative modulo results (Math.Abs(int.MinValue) overflows).
string[] conditions = ["Sunny", "Cloudy", "Rainy", "Snowy", "Windy"];
uint hash = (uint)city.GetHashCode();
string condition = conditions[hash % conditions.Length];
int temperature = 15 + (int)(hash % 20);
return $"{city}: {condition}, {temperature}°C";
});

tasks.AddActivityFunc<string[], string>("CreateSummary", (context, forecasts) =>
{
return $"Weather report for {forecasts.Length} cities:\n" + string.Join("\n", forecasts);
});
});

workerBuilder.UseDurableTaskScheduler(connectionString);
});

// Configure the Durable Task client to connect to the DTS emulator.
builder.Services.AddDurableTaskClient(clientBuilder =>
{
clientBuilder.UseDurableTaskScheduler(connectionString);
});

IHost host = builder.Build();
await host.StartAsync();

// Schedule the orchestration and wait for it to complete.
await using DurableTaskClient client = host.Services.GetRequiredService<DurableTaskClient>();
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync("FanOutFanIn");
Console.WriteLine($"Started orchestration instance: '{instanceId}'");

using CancellationTokenSource timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(60));
OrchestrationMetadata result = await client.WaitForInstanceCompletionAsync(
instanceId,
getInputsAndOutputs: true,
timeoutCts.Token);

Console.WriteLine($"Orchestration completed with status: {result.RuntimeStatus}");
Console.WriteLine($"Output:\n{result.ReadOutputAs<string>()}");
Console.WriteLine();
Console.WriteLine("View traces in Jaeger UI at http://localhost:16686");
Console.WriteLine("View orchestrations in DTS Emulator dashboard at http://localhost:8082");
Console.WriteLine("Look for service 'DistributedTracingSample' to see the orchestration trace.");

await host.StopAsync();
86 changes: 86 additions & 0 deletions samples/DistributedTracingSample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Distributed Tracing with OpenTelemetry Sample

This sample demonstrates how to configure OpenTelemetry distributed tracing with the Durable Task SDK. Traces are exported to Jaeger via OTLP, allowing you to visualize the full execution flow of orchestrations and activities.

## Overview

The Durable Task SDK automatically emits traces using the `Microsoft.DurableTask` [ActivitySource](https://learn.microsoft.com/dotnet/api/system.diagnostics.activitysource). This sample subscribes to that source using the OpenTelemetry SDK and exports traces to Jaeger using the OTLP exporter.

The sample runs a **fan-out/fan-in** orchestration that:
1. Fans out to 5 parallel `GetWeather` activity calls (one per city)
2. Fans in by waiting for all activities to complete
3. Calls a `CreateSummary` activity to aggregate the results

This pattern produces a rich trace with multiple parallel spans, making it easy to visualize in Jaeger.

## Prerequisites

- .NET 10.0 SDK or later
- [Docker](https://www.docker.com/get-started) and [Docker Compose](https://docs.docker.com/compose/)

## Running the Sample

### 1. Start Jaeger and the DTS Emulator

From this directory, start the infrastructure containers:

```bash
docker compose up -d
```

This starts:
- **Jaeger** — UI at [http://localhost:16686](http://localhost:16686), OTLP receiver on ports 4317 (gRPC) and 4318 (HTTP)
- **DTS Emulator** — gRPC endpoint on port 8080, dashboard UI at [http://localhost:8082](http://localhost:8082)

### 2. Run the Sample

```bash
dotnet run
```

The sample connects to the DTS emulator by default using the connection string:
```
Endpoint=http://localhost:8080;Authentication=None;TaskHub=default
```

To override, set the `DURABLE_TASK_SCHEDULER_CONNECTION_STRING` environment variable:
Comment on lines +41 to +46
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section states the sample connects to the DTS emulator by default using a localhost connection string, but the current Program.cs requires DURABLE_TASK_SCHEDULER_CONNECTION_STRING to be set and will fail otherwise. Please align the instructions with the actual startup behavior (either document the requirement or change the code to have a default).

Suggested change
The sample connects to the DTS emulator by default using the connection string:
```
Endpoint=http://localhost:8080;Authentication=None;TaskHub=default
```
To override, set the `DURABLE_TASK_SCHEDULER_CONNECTION_STRING` environment variable:
Before running, configure the DTS emulator connection string using the `DURABLE_TASK_SCHEDULER_CONNECTION_STRING` environment variable. For the local emulator started via Docker Compose, use:

Copilot uses AI. Check for mistakes.
```bash
export DURABLE_TASK_SCHEDULER_CONNECTION_STRING="Endpoint=http://localhost:8080;Authentication=None;TaskHub=default"
dotnet run
```

### 3. View Traces in Jaeger

1. Open the Jaeger UI at [http://localhost:16686](http://localhost:16686)
2. Select **DistributedTracingSample** from the "Service" dropdown
3. Click **Find Traces**
4. Click on a trace to see the full execution flow

You should see a trace with spans for:
- The `FanOutFanIn` orchestration
- Five parallel `GetWeather` activity executions
- A final `CreateSummary` activity execution

### 4. Clean Up

```bash
docker compose down
```

## How It Works

The key OpenTelemetry configuration is straightforward:

```csharp
builder.Services.AddOpenTelemetry()
.ConfigureResource(resource => resource.AddService("DistributedTracingSample"))
.WithTracing(tracing =>
{
tracing.AddSource("Microsoft.DurableTask");
tracing.AddOtlpExporter();
});
```

- **`AddSource("Microsoft.DurableTask")`** subscribes to the SDK's built-in ActivitySource
- **`AddOtlpExporter()`** sends traces to the default OTLP endpoint (`http://localhost:4317`)
- **`AddService("DistributedTracingSample")`** sets the service name shown in Jaeger
15 changes: 15 additions & 0 deletions samples/DistributedTracingSample/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
services:
jaeger:
image: jaegertracing/jaeger:latest
ports:
- "16686:16686" # Jaeger UI
- "4317:4317" # OTLP gRPC receiver
- "4318:4318" # OTLP HTTP receiver
environment:
- COLLECTOR_OTLP_ENABLED=true

dts-emulator:
image: mcr.microsoft.com/dts/dts-emulator:latest
ports:
- "8080:8080" # DTS emulator gRPC endpoint
- "8082:8082" # DTS emulator dashboard UI
3 changes: 2 additions & 1 deletion src/InProcessTestHost/InProcessTestHost.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@

<ItemGroup>
<PackageReference Include="Microsoft.Azure.DurableTask.Core" />
<PackageReference Include="Grpc.AspNetCore.Server" />
<PackageReference Include="Grpc.AspNetCore.Server" VersionOverride="2.71.0" Condition="'$(TargetFramework)' == 'net6.0'" />
<PackageReference Include="Grpc.AspNetCore.Server" Condition="'$(TargetFramework)' != 'net6.0'" />
<PackageReference Include="Grpc.Net.Client" />
<PackageReference Include="Google.Protobuf" />
</ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/Worker/Grpc/Worker.Grpc.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Google.Protobuf" VersionOverride="3.33.2" />
<PackageReference Include="Google.Protobuf" VersionOverride="3.33.5" />
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Google.Protobuf is already centrally versioned in Directory.Packages.props (currently 3.33.5). Since this VersionOverride matches the central version, it's redundant and adds maintenance overhead—consider removing the VersionOverride and relying on central package management.

Suggested change
<PackageReference Include="Google.Protobuf" VersionOverride="3.33.5" />
<PackageReference Include="Google.Protobuf" />

Copilot uses AI. Check for mistakes.
<SharedSection Include="Core" />
<SharedSection Include="DependencyInjection" />
<SharedSection Include="Grpc" />
Expand Down
Loading