Using Application Insights Custom Events in ASP.NET Core
Overview
BrowserPower is a simple game leveraging the power of Azure Cognitive Services - Vision API. The premise is simple, given clues, find the best image from the web that matches it. The clues might be a herd of sheep or a particular celebrity eating a cheeseburger. The game itself is not as important as the technology used to create it.
This wiki highlights using an Application Insights custom event to measure the performance impact of using tasks.
Note: SendGrid was highlighted in a previous TechNet Wiki article.
Note: Configuration was highlighted in a previous TechNet Wiki article.
Note: Cognitive Services Emotion API was highlighted in a previous TechNet Wiki article.
Background
The game grew from the wiki article: Getting started with Cognitive Services - Vision. Playing the game is simple: An internet search game using Azure Cognitive Services. The project has been uploaded to MSDN as it is an interesting illustration of building an ASP.NET Core Azure Web App from the ASP.NET Core Web Application template and implements several of steps in the ASP.NET Core Tutorials.
The source can be located here.
Running tasks
The game uses three Cognitive Services APIs to determine a score for a submitted image. The Vision API is used to get metadata about the image like if the picture contains celebrities, is black and white, has a man in it, and other factors. The Vision API also supports a service to conveniently generate a thumbnail. The last API is the Emotion API which gives an indication of if the person in the image is sad, scared, surprised and other emotions.
These three calls do not have to run sequentially and potentially there will be a performance improvement if they were run in parallel.
Running a method as a Task
The three tasks are shown below as defined to support being run as async:
private async Task<CelebritiesResponse> GetCelebrities(byte[] byteData)
{
return await Task.Run(() =>
{
var celebritiesResponse = _cognitiveService.AnalyseCelebrities(byteData);
return JsonConvert.DeserializeObject<CelebritiesResponse>(celebritiesResponse);
});
}
private async Task<string> GetThumbnail(byte[] byteData)
{
return await Task.Run(() => _cognitiveService.GetThumbnail(byteData));
}
private async Task<Emotion[]> GetEmotions(byte[] byteData)
{
return await Task.Run(() =>
{
var emotion = _cognitiveService.GetEmotion(byteData);
return JsonConvert.DeserializeObject<Emotion[]>(emotion);
});
}
As these have been defined as separate methods we then have the ability to run a task as a blocking action or sequentially. This is illustrated below where line numbers 7-9 are the tasks run sequentially and lines 13-15 are as parallel tasks:
01.CelebritiesResponse celebrities;
02.string thumbnail;
03.Emotion[] emotions;
04.
05.if (_cognitiveService.RunSequentially)
06.{
07. celebrities = GetCelebrities(byteData).Result;
08. thumbnail = GetThumbnail(byteData).Result;
09. emotions = GetEmotions(byteData).Result;
10.}
11.else
12.{
13. var getCelebritiesTask = GetCelebrities(byteData);
14. var getThumbnailTask = GetThumbnail(byteData);
15. var getEmotionsTask = GetEmotions(byteData);
16.
17. await Task.WhenAll(getCelebritiesTask, getThumbnailTask, getEmotionsTask);
18.
19. celebrities = getCelebritiesTask.Result;
20. thumbnail = getThumbnailTask.Result;
21. emotions = getEmotionsTask.Result;
22.}
Application Insights Custom Event
Application Insights was included as part of the ASP.NET Core package. This was included in the project.json as a reference:
The client is instantiated in the constructor of the controller as shown on line 10 below:
01.private TelemetryClient _insights;
02.
03.public HomeController(UserManager<ApplicationUser> userManager, ILoggerFactory loggerFactory, CloudStorageService cloudStorage, CognitiveService cognitiveService)
04.{
05. _logger = loggerFactory.CreateLogger<AccountController>();
06. _cloudStorage = cloudStorage;
07. _userManager = userManager;
08. _cognitiveService = cognitiveService;
09.
10. _insights = new TelemetryClient();
11.}
The custom event is then created by using the result of a StopWatch to get the elapsed time of three tasks:
01.Stopwatch sw = new Stopwatch();
02.
03.sw.Start();
04....
05.
06.if (_cognitiveService.RunSequentially)
07.{
08....
09.}
10.else
11.{
12....
13.}
14.
15.sw.Stop();
16._insights.TrackEvent("CognitiveServices",
17. new Dictionary<string, string> { { "RunSequentially", _cognitiveService.RunSequentially.ToString() } },
18. new Dictionary<string, double> { { "ElapsedMilliseconds", sw.ElapsedMilliseconds } });
Azure Portal Application Insights
In the Azure Portal Application Insights blade, the custom event can be viewed. One simple way would be to use the Metrics Explorer:
The custom event is shown when adding a new chart:
Once the events have been captured, the detail around the event can be examined. Below shows the tasks running for around 4 seconds sequentially:
While running in parallel shows a couple second improvement:
Summary
Running tasks in parallel can greatly improve performance. Hopefully, this wiki provides a good illustration of how this can be accomplished and the benefits measured using Application Insights.