Search code examples
c#azureasp.net-mvc-5azure-application-insightstelemetry

API Application Insights good practice to use


I read this documentation: https://learn.microsoft.com/en-us/azure/application-insights/app-insights-api-custom-events-metrics

There are many different API method to track exceptions, track trace etc..

I have a ASP.NET MVC 5 application. For example, I have the following controller method (called by ajax):

    [AjaxErrorHandling]
    [HttpPost]
    public async Task SyncDriverToVistracks(int DriverID)
    {
            if ([condition])
            {
                // some actions here

                try
                {
                    driver.VistrackId = await _vistracksService.AddNewDriverToVistrackAsync(domain);
                    await db.SaveChangesAsync();
                }
                catch (VistracksApiException api_ex)
                {
                    // external service throws exception type VistracksApiException 
                    throw new AjaxException("vistracksApiClient", api_ex.Response.Message);
                }
                catch (VistracksApiCommonException common_ex)
                {
                    // external service throws exception type VistracksApiCommonException 
                    throw new AjaxException("vistracksApiServer", "3MD HOS server is not available");
                }
                catch (Exception ex)
                {
                    // something wrong at all
                    throw new AjaxException("General", ex.Message);
                }
            }
            else
            {
                // condition is not valid
                throw new AjaxException("General", "AccountId is not found");
            }
    }

this method throws AjaxException if something wrong (which catch by AjaxErrorHandling and then return something json response to client).

Now I want to add telemetry for logging, analyzing exceptions and observe on client events.

So, I added the following:

    [AjaxErrorHandling]
    [HttpPost]
    public async Task SyncDriverToVistracks(int DriverID)
    {
            telemetryClient.TrackEvent("Sync driver", new Dictionary<string, string> { { "ChangedBy", User.Identity.Name }, { "DriverID", DriverID.ToString() } }, null);
            if ([condition])
            {
                // some actions here

                try
                {
                    driver.VistrackId = await _vistracksService.AddNewDriverToVistrackAsync(domain);
                    await db.SaveChangesAsync();
                }
                catch (VistracksApiException api_ex)
                {
                    // external service throws exception type VistracksApiException 
                    telemetryClient.TrackTrace("VistracksApiException", new Dictionary<string, string> {
                        { "ChangedBy", User.Identity.Name },
                        { "DriverID", DriverID.ToString() },
                        { "ResponseCode", api_ex.Response.Code.ToString() },
                        { "ResponseMessage", api_ex.Response.Message },
                        { "ResponseDescription", api_ex.Response.Description }
                    });
                    telemetryClient.TrackException(api_ex);

                    throw new AjaxException("vistracksApiClient", api_ex.Response.Message);
                }
                catch (VistracksApiCommonException common_ex)
                {
                    // external service throws exception type VistracksApiCommonException 
                    telemetryClient.TrackTrace("VistracksApiCommonException", new Dictionary<string, string> {
                        { "ChangedBy", User.Identity.Name },
                        { "DriverID", DriverID.ToString() },
                        { "Message", common_ex.Message },
                    });
                    telemetryClient.TrackException(common_ex);
                    throw new AjaxException("vistracksApiServer", "3MD HOS server is not available");
                }
                catch (Exception ex)
                {
                    // something wrong at all
                    telemetryClient.TrackTrace("Exception", new Dictionary<string, string> {
                        { "ChangedBy", User.Identity.Name },
                        { "DriverID", DriverID.ToString() },
                        { "Message", ex.Message },
                    });
                    telemetryClient.TrackException(ex);
                    throw new AjaxException("General", ex.Message);
                }
            }
            else
            {
                telemetryClient.TrackTrace("ConditionWrong", new Dictionary<string, string> {
                    { "ChangedBy", User.Identity.Name },
                    { "DriverID", DriverID.ToString() },
                    { "Message", "AccountId is not found" },
                });
                // condition is not valid
                throw new AjaxException("General", "AccountId is not found");
            }
    }

by the following line:

        telemetryClient.TrackEvent("Sync driver", new Dictionary<string, string> { { "ChangedBy", User.Identity.Name }, { "DriverID", DriverID.ToString() } }, null);

I just "log" client event, that the method was called. Just for statistics.

In each "catch" block I try to write trace with different parameters and write exception:

                    telemetryClient.TrackTrace("trace name", new Dictionary<string, string> {
                        { "ChangedBy", User.Identity.Name },
                        ....
                    });
                    telemetryClient.TrackException(ex);

Is it necessary? Or just need to track only exception? Then I lose different info, like who try to add these changes etc... When each of these methods should be used?


Solution

  • This is the best practice for 2.5.1 AI SDK. Will highlight parts which might not be required in upcoming AI SDK releases.

    The right way to do end-to-end tracing is to rely on new Activity class in .NET framework. Until AI supports Activity.Tags (https://github.com/Microsoft/ApplicationInsights-dotnet/issues/562) you need to propagate them manually using TelemetryInitializer:

    public class ActvityTagsTelemetryInitializer : ITelemetryInitializer
    {
        public void Initialize(ITelemetry telemetry)
        {
            Activity current = Activity.Current;
    
            if (current == null)
            {
                current = (Activity)HttpContext.Current?.Items["__AspnetActivity__"];
            }
    
            while (current != null)
            {
                foreach (var tag in current.Tags)
                {
                    if (!telemetry.Context.Properties.ContainsKey(tag.Key))
                    {
                        telemetry.Context.Properties.Add(tag.Key, tag.Value);
                    }
                }
    
                current = current.Parent;
            }
        }
    }
    

    Then register it in ApplicationInsights.config:

      <TelemetryInitializers>
        ...
        <Add Type="<namespace>.ActvityTagsTelemetryInitializer, <assemblyname>"/>
      </TelemetryInitializers>
    

    Then you can populate proper tags:

    [AjaxErrorHandling]
    [HttpPost]
    public async Task SyncDriverToVistracks(int DriverID)
    {
        Activity.Current.AddTag("DriverID", DriverID.ToString());
        Activity.Current.AddTag("UserID", User.Identity.Name);
    
        try
        {
            if ([condition])
            {
                // some actions here
    
                try
                {
                    // If below call is HTTP then no need to use StartOperation
                    using (telemetryClient.StartOperation<DependencyTelemetry>("AddNewDriverToVistrackAsync"))
                    {
                        driver.VistrackId = await _vistracksService.AddNewDriverToVistrackAsync(domain);
                    }
    
                    // If below call is HTTP then no need to use StartOperation
                    using (telemetryClient.StartOperation<DependencyTelemetry>("SaveChanges"))
                    {
                        await db.SaveChangesAsync();
                    }
                }
                catch (VistracksApiException api_ex)
                {
                    // external service throws exception type VistracksApiException 
                    throw new AjaxException("vistracksApiClient", api_ex.Response.Message);
                }
                catch (VistracksApiCommonException common_ex)
                {
                    // external service throws exception type VistracksApiCommonException 
                    throw new AjaxException("vistracksApiServer", "3MD HOS server is not available");
                }
                catch (Exception ex)
                {
                    // something wrong at all
                    throw new AjaxException("General", ex.Message);
                }
            }
            else
            {
                // condition is not valid
                throw new AjaxException("General", "AccountId is not found");
            }
        }
        catch (Exception ex)
        {
            // Upcoming 2.6 AI SDK will track exceptions for MVC apps automatically.
            telemetryClient.TrackException(ex);
            throw;
        }
    }
    

    You should have the following telemetry:

    1. Incoming request
    2. Outgoing requests (dependencies)
    3. Exceptions for failed requests

    All telemetry will be stamped with ChangedBy and DriverID