Exception Handling In MVC With Filters And Application Insights

In this post, we will see how to add Application Insights telemetry into an existing MVC application and track the exceptions in an Azure portal easily.

Introduction

Application Insights is an extensible Application Performance Management (APM) service for web developers on multiple platforms. This is used to monitor the performance of your live web applications. It includes powerful analytics tools to help you diagnose issues and to understand what users do with your app.

You can check this URL to get more details about Application Insights.
 
Prerequisites for Adding Application Insights
We can create an MVC application. I am using Visual Studio 2017 for creating the application.
 
Choose ASP.NET Web application template and select MVC option to create an application.
 
 
We can create an “Employees” table in SQL server using below script.
 
CREATE_EMPLOYEE_TABLE.sql
  1. USE [SarathlalDB]    
  2. GO    
  3.     
  4. CREATE TABLE [dbo].[Employees](    
  5.     [Id] [nvarchar](50) NOT NULL,    
  6.     [Name] [nvarchar](50) NULL,    
  7.     [Gender] [nvarchar](10) NULL,    
  8.     [Company] [nvarchar](50) NULL,    
  9.     [Designation] [nvarchar](50) NULL,    
  10.     [City] [nvarchar](50) NULL,   
  11.  CONSTRAINT [PK_Employees] PRIMARY KEY CLUSTERED     
  12.     (    
  13.         [Id] ASC    
  14.     )    
  15. )    
  16. GO    

Now we can add an Employee class inside “Models” folder.

Employees.cs
  1. namespace AppInsightSample1.Models  
  2. {  
  3.     public class Employee  
  4.     {  
  5.         public string Id { getset; }  
  6.         public string Name { getset; }  
  7.         public string Gender { getset; }  
  8.         public string Company { getset; }  
  9.         public string Designation { getset; }  
  10.         public string City { getset; }   
  11.     }  
  12. }  

Create an Employees Controller with views for CRUD operations using scaffolding template.

 
Choose the existing “Employee” class as a model class and click the “+” button to create a new data context class. This class will be used by entity framework for ORM (Object Relational Mapping) purposes.
 
 
This scaffolding process will create an EmployeesController class along with 5 views under Employees folder inside Views folder.
 
 
You can see that a new connection string is created in Web.Config file as well. This connection will be used in entity framework for database connectivity. We can change the connection string according to our SQL server and database details.
 
 

We need a small change in _Layout.cshtml partial view to add Employees controller and Index action inside it.

_Layout.cshtml
  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4.     <meta charset="utf-8" />  
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">  
  6.     <title>@ViewBag.Title - Application Insights Sample</title>  
  7.     @Styles.Render("~/Content/css")  
  8.     @Scripts.Render("~/bundles/modernizr")  
  9.     <script type = 'text/javascript' >  
  10.         var appInsights=window.appInsights||function(config)  
  11.         {  
  12.             function r(config){ t[config] = function(){ var i = arguments; t.queue.push(function(){ t[config].apply(t, i)})} }  
  13.             var t = { config:config},u=document,e=window,o='script',s=u.createElement(o),i,f;for(s.src=config.url||'//az416426.vo.msecnd.net/scripts/a/ai.0.js',u.getElementsByTagName(o)[0].parentNode.appendChild(s),t.cookie=u.cookie,t.queue=[],i=['Event','Exception','Metric','PageView','Trace','Ajax'];i.length;)r('track'+i.pop());return r('setAuthenticatedUserContext'),r('clearAuthenticatedUserContext'),config.disableExceptionTracking||(i='onerror',r('_'+i),f=e[i],e[i]=function(config, r, u, e, o) { var s = f && f(config, r, u, e, o); return s !== !0 && t['_' + i](config, r, u, e, o),s}),t  
  14.         }({  
  15.             instrumentationKey:'ac363ab6-204d-4a79-9821-e49ada70b232'  
  16.         });  
  17.           
  18.         window.appInsights=appInsights;  
  19.         appInsights.trackPageView();  
  20.     </script>  
  21. </head>  
  22. <body>  
  23.     <div class="navbar navbar-inverse navbar-fixed-top">  
  24.         <div class="container">  
  25.             <div class="navbar-header">  
  26.                 <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">  
  27.                     <span class="icon-bar"></span>  
  28.                     <span class="icon-bar"></span>  
  29.                     <span class="icon-bar"></span>  
  30.                 </button>  
  31.                 @Html.ActionLink("Application Insights""Index""Home"new { area = "" }, new { @class = "navbar-brand" })  
  32.             </div>  
  33.             <div class="navbar-collapse collapse">  
  34.                 <ul class="nav navbar-nav">  
  35.                     <li>@Html.ActionLink("Home""Index""Home")</li>  
  36.                     <li>@Html.ActionLink("Employees""Index""Employees")</li>  
  37.                     <li>@Html.ActionLink("Contact""Contact""Home")</li>  
  38.                 </ul>  
  39.             </div>  
  40.         </div>  
  41.     </div>  
  42.     <div class="container body-content">  
  43.         @RenderBody()  
  44.         <hr />  
  45.         <footer>  
  46.             <p>© @DateTime.Now.Year - By Sarath Lal</p>  
  47.         </footer>  
  48.     </div>  
  49.   
  50.     @Scripts.Render("~/bundles/jquery")  
  51.     @Scripts.Render("~/bundles/bootstrap")  
  52.     @RenderSection("scripts", required: false)  
  53. </body>  
  54. </html>  

We have completed all the coding part for creating simple CRUD actions for Employee details.

We can add Application Insights to this project easily.
 
Right click the solution and click “Add” and choose “Application Insight Telemetry”
 
 
Click “Get Started” button to proceed.
 
 
You can create the Application Insights and register the application now. You must have an active Azure account for that.
 
 
I am giving you the current pricing details for Application Insights. It will be varying from time to time.
 
 

Please check this URL to get the latest pricing details of Application Insights.

It will take some time to add Application Insights to our project.
 
 
If you look at the packages.config file, you can notice that the below new packages are added to our project now. 
 
 
packages.config
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <packages>  
  3.   <package id="Antlr" version="3.5.0.2" targetFramework="net45" />  
  4.   <package id="bootstrap" version="3.3.7" targetFramework="net45" />  
  5.   <package id="EntityFramework" version="6.1.3" targetFramework="net45" />  
  6.   <package id="jQuery" version="3.3.1" targetFramework="net45" />  
  7.   <package id="jQuery.Validation" version="1.17.0" targetFramework="net45" />  
  8.   <package id="Microsoft.ApplicationInsights" version="2.4.0" targetFramework="net45" />  
  9.   <package id="Microsoft.ApplicationInsights.Agent.Intercept" version="2.4.0" targetFramework="net45" />  
  10.   <package id="Microsoft.ApplicationInsights.DependencyCollector" version="2.4.1" targetFramework="net45" />  
  11.   <package id="Microsoft.ApplicationInsights.PerfCounterCollector" version="2.4.1" targetFramework="net45" />  
  12.   <package id="Microsoft.ApplicationInsights.Web" version="2.4.1" targetFramework="net45" />  
  13.   <package id="Microsoft.ApplicationInsights.WindowsServer" version="2.4.1" targetFramework="net45" />  
  14.   <package id="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel" version="2.4.0" targetFramework="net45" />  
  15.   <package id="Microsoft.AspNet.Mvc" version="5.2.4" targetFramework="net45" />  
  16.   <package id="Microsoft.AspNet.Razor" version="3.2.4" targetFramework="net45" />  
  17.   <package id="Microsoft.AspNet.TelemetryCorrelation" version="1.0.0" targetFramework="net45" />  
  18.   <package id="Microsoft.AspNet.Web.Optimization" version="1.1.3" targetFramework="net45" />  
  19.   <package id="Microsoft.AspNet.WebPages" version="3.2.4" targetFramework="net45" />  
  20.   <package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="2.0.0" targetFramework="net45" />  
  21.   <package id="Microsoft.jQuery.Unobtrusive.Validation" version="3.2.4" targetFramework="net45" />  
  22.   <package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />  
  23.   <package id="Modernizr" version="2.8.3" targetFramework="net45" />  
  24.   <package id="Newtonsoft.Json" version="11.0.1" targetFramework="net45" />  
  25.   <package id="System.Diagnostics.DiagnosticSource" version="4.4.1" targetFramework="net45" />  
  26.   <package id="WebGrease" version="1.6.0" targetFramework="net45" />  
  27. </packages>  
There are 7 new packages related to Application Insights added to project.
 
You can also see a “ApplicationInsights.config” file in the root folder. This file contains many configuration details along with the instrumentation key of Application Insights.
 

You can also see a “AiHandleErrorAttribute.cs” filter class (which is added to project automatically with Application Insights) to track the exceptions in the project level.

AiHandleErrorAttribute.cs
  1. using System;  
  2. using System.Web.Mvc;  
  3. using Microsoft.ApplicationInsights;  
  4.   
  5. namespace AppInsightSample1.ErrorHandler  
  6. {  
  7.     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]   
  8.     public class AiHandleErrorAttribute : HandleErrorAttribute  
  9.     {  
  10.         public override void OnException(ExceptionContext filterContext)  
  11.         {  
  12.             if (filterContext != null && filterContext.HttpContext != null && filterContext.Exception != null)  
  13.             {  
  14.                 //If customError is Off, then AI HTTPModule will report the exception  
  15.                 if (filterContext.HttpContext.IsCustomErrorEnabled)  
  16.                 {     
  17.                     var ai = new TelemetryClient();  
  18.                     ai.TrackException(filterContext.Exception);  
  19.                 }   
  20.             }  
  21.             base.OnException(filterContext);  
  22.         }  
  23.     }  
  24. }  

Whenever an error occurs in application, it will be caught in this attribute class and will be written to Application Insights immediately.

You must enable custom error mode in Web.Config to get the exception details in Application Insights.
 

Once we added all Application Insights related packages to the project, this will capture all the requests and page views along with exceptions too.

We can run the application to check the Application Insights logs.
 
Select the Employees menu and create a new Employee record now.
 
 
We can open the Azure Portal and open Application Insights resource. You can see an “Analytics” blade there. Please click that blade to open the Application Insights logs.
 
 
You can see various types of Application Insights schemas there.
 
 
You can choose “requests” from the list and click the “Run” button. It will show the log details about all requests that happened from the application.
 
 
We can export these logs as CSV file very easily.
 

I am going to stop the SQL server service and create an exception in the application forcefully so that, we can check the exception logs in Application Insights.

I have already put a breakpoint in the exception filter. The current exception is caught there, and it will write the exception to Application Insights.
 

We can go to the Azure portal and open the Analytics blade.

You can choose “exceptions” from the list and click the “Run” button to get the log details. You will get entire exception details in the log.
 

We can start the SQL server service again and rename the “Employees” table to “EmployeesChanged” in the database. Once again, click the Employees tab in the application. It will again create an exception and will write to Application Insights.

You can run the query again and get the second exception as well.
 
 
You will get the details about the exception by expanding the arrow key.
 
 
As expected, we have received the actual exception from log details.
 

Conclusion

We have created a simple MVC application in Visual Studio 2017 and we have added Application Insights SDK to this project. This SDK automatically added an exception filter to handle all the exceptions in the project level. We have seen all the requests came from application in Azure Portal using Application Insights. Later we have stopped the SQL server service and generated an exception in the application. We have seen this exception log in Application Insights as well.

In a nutshell, Application insights is the easy diagnostic tool to track the performance and exception details that occurred in any application (Azure Web Apps or on-premise Apps) anywhere from the Azure Portal. This will reduce the nightmare of any application developer to catch the exact root cause of exceptions that occurred in their application.