As software engineers, understanding distributed systems is essential to enhance, maintain, and deliver business value. By implementing tracing, distributed systems can become easier to debug and diagnose.
This guide will demonstrate how to implement tracing using Microsoft Azure’s Application Insights. In this guide, tracing refers to writing events from related systems such that an end-to-end transaction log can be reconstructed. You will learn how to create child requests as part of a larger operation. This is useful for viewing all logs in context. A basic understanding of installing and using Application Insights is assumed knowledge for this guide.
Distributed systems present challenges that makes diagnosing issues harder:
Monitoring the system becomes more difficult. Finding all the logs associated with one request is a nightmare.
Application Insights can solve this problem by allowing you to construct a hierarchy of events. These events can occur within the same application process or across several application processes.
To describe an event that occurs in the same process as a child, call StartOperation
and give it a name. In the code snippet below, use RenderEmail
as an example of an in process child operation.
1public void RenderEmail(string emailType, EmailViewModel emailViewModel)
2{
3 using var operation = _telemetryClient.StartOperation<DependencyTelemetry>($"Render email {emailType}");
4 operation.Telemetry.Type = "Email";
5 operation.Telemetry.Properties["EmailType"] = emailType;
6 body = _razorViewToStringRenderer.RenderViewToString(emailViewModel);
7}
Note that the above code example uses using declarations, which are new to C# 8. When this method is called inside an ASP.NET Core API request, Application Insights constructs a hierarchy.
Traces can also be correlated across process boundaries too. If two systems collaborate in a subscriber/publisher relationship, their relationship can be inferred. This is illustrated below.
Note: when using Azure Service Bus with Azure App Services, this hierarchy is created automatically if both apps have Application Insights configured.
To achieve this in code, the message producer (System A) needs to write System.Diagnostics.Activity.Id
somewhere in the message.
On the message consumer side (System B), the following code needs to be added.
1public async Task Process(T message, CancellationToken cancellationToken)
2{
3 using var operation = _telemetryClient.StartOperation<RequestTelemetry>(new Activity($"Process message").SetParentId(message.ParentId));
4 try
5 {
6 await _inner.Process(message, cancellationToken);
7 operation.Telemetry.Success = result.Success;
8 }
9 catch (Exception ex)
10 {
11 operation.Telemetry.Success = false;
12 throw;
13 }
14}
The above describes how a hierarchy of trace events can be created using System.Diagnostics.Activity
and the StartOperation
method. This helps to find all trace events involved in the context of a single user request.
With a hierarchy of traces configured in your app, many debugging scenarios are made much simpler. Some example of these include:
Diagnosing distributed systems can be simple with these techniques. If you would like to learn more, you can read about the technical details by reading How Application Insights Correlates Telemetry.