Wednesday, July 23, 2025

.NET Isolated Azure Durable Functions: Distributed Tracing

In this post, let's have a look at the power of Distributed Tracing in .NET Isolated Azure Durable Functions. This is one of the new features that got GA like few weeks ago.

First let's have a look at a simple Durable Function and see how it's logged in Application Insights.
public static class Function
{
    [Function(nameof(HttpStart))]
    public static async Task<HttpResponseData> HttpStart(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req,
        [DurableClient] DurableTaskClient client,
        FunctionContext executionContext)
    {
        string instanceId =
            await client.ScheduleNewOrchestrationInstanceAsync(nameof(RunOrchestrator));

        return await client.CreateCheckStatusResponseAsync(reqinstanceId);
    }

    [Function(nameof(RunOrchestrator))]
    public static async Task<List<string>> RunOrchestrator(
        [OrchestrationTrigger] TaskOrchestrationContext context)
    {
        EntityInstanceId entityId = new(nameof(HelloHistoryEntity)"helloHistory");

        await context.Entities.CallEntityAsync<string>(entityId,
            nameof(HelloHistoryEntity.Reset));

        string result = await context.CallActivityAsync<string>(nameof(SayHello)"Tokyo");
        await context.Entities.CallEntityAsync<string>(entityId,
            nameof(HelloHistoryEntity.Add),
            result);

        result = await context.CallActivityAsync<string>(nameof(SayHello)"Seattle");
        await context.Entities.CallEntityAsync<string>(entityId,
            nameof(HelloHistoryEntity.Add),
            result);

        result = await context.CallActivityAsync<string>(nameof(SayHello)"London");
        await context.Entities.CallEntityAsync<string>(entityId,
            nameof(HelloHistoryEntity.Add),
            result);

        List<string> outputs = await context.Entities.CallEntityAsync<List<string>>(entityId,
            nameof(HelloHistoryEntity.Get));
        return outputs;
    }

    [Function(nameof(SayHello))]
    public static string SayHello([ActivityTrigger] string nameFunctionContext executionContext)
    {
        return $"Hello {name}!";
    }
}

public class HelloHistoryEntity : TaskEntity<List<string>>
{
    public void Add(string message) => State.Add(message);

    public void Reset() => State = [];

    public List<string> Get() => State;

    [Function(nameof(HelloHistoryEntity))]
    public Task RunEntityAsync([EntityTrigger] TaskEntityDispatcher dispatcher)
    {
        return dispatcher.DispatchAsync(this);
    }
}
Here I have a single Orchestrator that gets triggered by a HTTP function, and the Orchestrator calls an Activity and a Entity few times.

Once I trigger the HTTP function, the logs for HTTP Request looks like below.
Without Distributed Tracing
And this isn't quite helpful.

Now let's enable Distributed Tracing. For .NET Isolated Durable Functions, Distributed Tracing V2 is supported with Microsoft.Azure.Functions.Worker.Extensions.DurableTask >= v1.4.0. Make sure to update your packages before doing the next step.

Now modify the host.json as follows.
{
  "version""2.0",
  "extensions": {
    "durableTask": {
      "tracing": {
        "distributedTracingEnabled"true,
        "version""V2"
      }
    }
  }
}
And that's about it.

Now if I I trigger the HTTP function, the logs for HTTP Request looks like below.
Distributed Tracing
Now we can see the full execution, the call to the Orchestrator and all related Activity and Entity calls.

Isn't it just nice.

Happy Coding.

Regards,
Jaliya

No comments:

Post a Comment