End-to-End Tracking in Azure Functions
In today’s cloud-native applications, services are increasingly distributed—events trigger processes, APIs invoke background jobs, and messages flow asynchronously via queues or topics.
While this decoupling improves scalability, it makes observability more challenging. When something goes wrong, how do you trace the full journey of a single user’s request across dozens of services?
The answer: End-to-End Tracking using a correlation ID or TrackingID. In this blog, you’ll learn how to implement request tracking in Azure Functions that communicate through Azure Service Bus using the modern SDK.
Architecture at a Glance
- Producer Function: Receives an HTTP request, generates or extracts a TrackingID, sends a message to Service Bus with this ID as a custom property.
- Consumer Function: Listens to the Service Bus queue or topic, reads the TrackingID, and usesBeginScopefor structured logging. It can optionally pass the ID further as needed.
1. Producer Function – Sending a Message with TrackingID
 1public static class ProducerFunction
 2{
 3    [FunctionName("ProducerFunction")]
 4    public static async Task<IActionResult> Run(
 5        [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
 6        [ServiceBus("myqueue", Connection = "ServiceBusConnection")] IAsyncCollector<ServiceBusMessage> outputMessages,
 7        ILogger log)
 8    {
 9        string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
10
11        // Extract or generate a TrackingID
12        string trackingId = req.Headers.ContainsKey("TrackingID")
13            ? req.Headers["TrackingID"].ToString()
14            : Guid.NewGuid().ToString();
15
16        // Create and send the Service Bus message with custom property
17        var message = new ServiceBusMessage(Encoding.UTF8.GetBytes(requestBody));
18        message.ApplicationProperties["TrackingID"] = trackingId;
19
20        log.LogInformation("Sending message with TrackingID: {TrackingID}", trackingId);
21
22        await outputMessages.AddAsync(message);
23
24        return new OkObjectResult(new
25        {
26            Status = "Message sent",
27            TrackingID = trackingId
28        });
29    }
30}Highlights:
- Uses the Azure.Messaging.ServiceBusSDK.
- Attaches TrackingIDas a custom application property.
- Logs the TrackingIDfor traceability.
2. Consumer Function – Receiving and Logging with TrackingID
This consumer reads from the queue/topic, extracts the TrackingID, and uses BeginScope to enrich logs across the entire scope.
 1public static class ConsumerFunction
 2{
 3    [FunctionName("ConsumerFunction")]
 4    public static async Task Run(
 5        [ServiceBusTrigger("myqueue", Connection = "ServiceBusConnection")] ServiceBusReceivedMessage message,
 6        ILogger log)
 7    {
 8        // Extract TrackingID from the message properties
 9        string trackingId = message.ApplicationProperties.ContainsKey("TrackingID")
10            ? message.ApplicationProperties["TrackingID"].ToString()
11            : "N/A";
12
13        // Begin a logging scope that includes the TrackingID
14        using (log.BeginScope(new Dictionary<string, object> { ["TrackingID"] = trackingId }))
15        {
16            log.LogInformation("Received message with TrackingID: {TrackingID}", trackingId);
17
18            // Simulate message processing
19            string body = Encoding.UTF8.GetString(message.Body);
20            log.LogInformation("Processing message body: {Body}", body);
21
22            // Pass trackingId downstream if needed
23            // For example, add it to outgoing HTTP headers or service calls
24        }
25
26        await Task.CompletedTask;
27    }
28}Scope Logging Bonus:
By using log.BeginScope, every log inside that scope gets auto-tagged with TrackingID. This is powerful with log aggregators like Application Insights, Elastic Stack, or Datadog.
Propagating TrackingID to Downstream Services
Once you’ve got the TrackingID, you can:
- Send it as a header in HTTP requests:httpClient.DefaultRequestHeaders.Add("TrackingID", trackingId);
- Use it in telemetry or performance metrics
- Store it in databases or audit trails
Keeping track of the ID across services provides unified visibility, even on large-scale systems.
Conclusion
With just a few lines of code, you can significantly improve observability in your distributed Azure solution:
- Use TrackingIDas a unique correlation ID
- Forward it through Service Bus with ApplicationProperties
- Use BeginScopeto enrich logs at every stage
This lightweight pattern is easy to add, doesn’t impact performance, and unlocks powerful traceability across your event-driven architecture.
What’s Next?
- Integrate Application Insights for centralized tracking
- Extend this pattern to handle retries and dead-letter messages
- Use Middleware or DelegatingHandlers to forward TrackingIDin outgoing HTTP requests automatically
Happy tracing!
#azure-functions #service-bus #tracking #logging #distributed-systems