Exception Logging In MVC Using Elmah

What is Elmah

According to Scott Mitchell, Error Logging Modules And Handlers (ELMAH) offers another approach to log runtime errors in a production environment. ELMAH is a free, open source error logging library that includes features like error filtering and the ability to view the error log from a web page, as an RSS feed, or to download it as a comma-delimited file.

What we will achieve through Elmah

  • Logging of nearly all unhandled exceptions.

  • Email notification of each error at the time error occurs.

  • View the error log from a web page.

  • RSS feed .

Step 1: Create a MVC web Application

Open Visual Studio create new project or press CTRL +SHIFT +N. Go to Web tab, select ASP.NET Web Application template. Give name as MVCExceptionLog as in the following screenshot,
 



Step 2: Add Elmah Package from Nuget,

Right click on project solution go to Manage NuGetPackages for Solution, click on it and search elmah and install it,

 

 
Step 3: Now open web config file and add the following code inside <system.web> </system.web>,

  1. <!--add this-->  
  2.     < httpHandlers >  
  3.     < add verb = "POST,GET,HEAD"path = "elmah.axd"type = "Elmah.ErrorLogPageFactory, Elmah" / >  
  4.     < /httpHandlers>  
  5. <!--add this-->  

Also, add the following code inside <system.webServer></system.webServer>,

  1. <!--add this-->  
  2.     < handlers >  
  3.     < add name = "Elmah"verb = "POST,GET,HEAD"path = "elmah.axd"type = "Elmah.ErrorLogPageFactory, Elmah" / >  
  4.     < /handlers>  
  5. <!--add this-->  

Step 4:  Create table and Stored Procedure,

Here I am using SQL Server database to log the exception.  I have already created database SQL Server named ExceptionLog. Open SQL Server Management Studio, run the following code in ExceptionLog database one by one.

For Table

  1. CREATE TABLE[dbo].[ELMAH_Error]  
  2. (  
  3.   
  4.     [ErrorId][uniqueidentifier] NOT NULL,  
  5.   
  6.     [Application][nvarchar](60) NOT NULL,  
  7.   
  8.     [Host][nvarchar](50) NOT NULL,  
  9.   
  10.     [Type][nvarchar](100) NOT NULL,  
  11.   
  12.     [Source][nvarchar](60) NOT NULL,  
  13.   
  14.     [Message][nvarchar](500) NOT NULL,  
  15.   
  16.     [User][nvarchar](50) NOT NULL,  
  17.   
  18.     [StatusCode][intNOT NULL,  
  19.   
  20.     [TimeUtc][datetime] NOT NULL,  
  21.   
  22.     [Sequence][int] IDENTITY(1, 1) NOT NULL,  
  23.   
  24.     [AllXml][ntext] NOT NULL  
  25.   
  26. )  

Stored procedure

    1. Create PROCEDURE[dbo].[ELMAH_GetErrorsXml]  
    2.   
    3. (  
    4.     @Application NVARCHAR(60),  
    5.     @PageIndex INT = 0,  
    6.     @PageSize INT = 15,  
    7.     @TotalCount INT OUTPUT  
    8.   
    9. )  
    10.   
    11. AS  
    12.   
    13. SET NOCOUNT ON  
    14.   
    15. DECLARE @FirstTimeUTC DATETIME  
    16. DECLARE @FirstSequence INT  
    17. DECLARE @StartRow INT  
    18. DECLARE @StartRowIndex INT  
    19. SELECT  
    20.   
    21. @TotalCount = COUNT(1)  
    22.   
    23. FROM  
    24.   
    25.     [ELMAH_Error]  
    26.   
    27. WHERE  
    28.   
    29.     [Application] = @Application  
    30. SET @StartRowIndex = @PageIndex * @PageSize + 1  
    31. IF @StartRowIndex <= @TotalCount  
    32.   
    33. BEGIN  
    34.   
    35. SET ROWCOUNT @StartRowIndex  
    36.   
    37. SELECT  
    38.   
    39. @FirstTimeUTC = [TimeUtc],  
    40.   
    41.     @FirstSequence = [Sequence]  
    42.   
    43. FROM  
    44.   
    45.     [ELMAH_Error]  
    46.   
    47. WHERE  
    48.   
    49.     [Application] = @Application  
    50.   
    51. ORDER BY  
    52.   
    53.     [TimeUtc] DESC,  
    54.     [SequenceDESC  
    55.   
    56. END  
    57.   
    58. ELSE  
    59.   
    60. BEGIN  
    61.   
    62. SET @PageSize = 0  
    63.   
    64. END  
    65.   
    66. SET ROWCOUNT @PageSize  
    67.   
    68. SELECT  
    69.   
    70. errorId = [ErrorId],  
    71.   
    72.     application = [Application],  
    73.     host = [Host],  
    74.     type = [Type],  
    75.     source = [Source],  
    76.     message = [Message],  
    77.     [user] = [User],  
    78.     statusCode = [StatusCode],  
    79.     time = CONVERT(VARCHAR(50), [TimeUtc], 126) + 'Z'  
    80.   
    81. FROM  
    82.   
    83.     [ELMAH_Error] error  
    84.   
    85. WHERE  
    86.   
    87.     [Application] = @Application  
    88.   
    89. AND  
    90.   
    91.     [TimeUtc] <= @FirstTimeUTC  
    92.   
    93. AND  
    94.   
    95.     [Sequence] <= @FirstSequence  
    96.   
    97. ORDER BY  
    98.   
    99.     [TimeUtc] DESC,  
    100.   
    101.     [SequenceDESC  
    102.   
    103. FOR  
    104.   
    105. XML AUTO  

    1. Create PROCEDURE[dbo].[ELMAH_GetErrorXml]  
    2.   
    3. (  
    4.   
    5.     @Application NVARCHAR(60),  
    6.     @ErrorId UNIQUEIDENTIFIER  
    7.   
    8. )  
    9.   
    10. AS  
    11.   
    12. SET NOCOUNT ON  
    13. SELECT  
    14.   
    15.     [AllXml]  
    16. FROM  
    17.   
    18.     [ELMAH_Error]  
    19. WHERE  
    20.   
    21.     [ErrorId] = @ErrorId  
    22. AND  
    23.     [Application] = @Application  

    1. Create PROCEDURE[dbo].[ELMAH_LogError]  
    2.   
    3. (  
    4.   
    5.     @ErrorId UNIQUEIDENTIFIER,    
    6.     @Application NVARCHAR(60),    
    7.     @Host NVARCHAR(30),    
    8.     @Type NVARCHAR(100),  
    9.     @Source NVARCHAR(60),    
    10.     @Message NVARCHAR(500),  
    11.     @User NVARCHAR(50),   
    12.     @AllXml NTEXT,    
    13.     @StatusCode INT  
    14.     @TimeUtc DATETIME  
    15.   
    16. )  
    17.   
    18. AS  
    19.   
    20. SET NOCOUNT ON  
    21.   
    22. INSERT  
    23.   
    24. INTO  
    25.   
    26.     [ELMAH_Error]
    27. (  
    28.   
    29.     [ErrorId],   
    30.     [Application],   
    31.     [Host],  
    32.     [Type],  
    33.     [Source],  
    34.     [Message],    
    35.     [User],    
    36.     [AllXml],    
    37.     [StatusCode],    
    38.     [TimeUtc]  
    39.   
    40. )  
    41.   
    42. VALUES  
    43.   
    44.     (  
    45.   
    46.     @ErrorId,  
    47.     @Application,    
    48.     @Host,    
    49.     @Type,    
    50.     @Source,   
    51.     @Message,    
    52.     @User  
    53.     @AllXml,   
    54.     @StatusCode,   
    55.     @TimeUtc  
    56.   
    57. )  

After creating the database table and stored procedure. The schema will look like the following:


Now again  open web config file, add the following code inside in <configuration>, 
  1. </configuration>  
  2. <elmah>  
  3.     <!--. If allowRemoteAccess value is set to 0, then the error log web page can only be viewed locally. If allowRemoteAccess attribute is set to 1 then the error log web page is enabled for both remote and local visitors.-->  
  4.     <!--add this-->  
  5.     <security allowRemoteAccess="0" />  
  6.     <!--  DefaultConnection is the name of database connection string -->  
  7.     <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="DefaultConnection" />  
  8.     <!--add this-->  
  9. </elmah>  
Now my final webconfig file look like the following, 
  1. <?xml version="1.0" encoding="utf-8"?>  
  2.     <!--  
  3. For more information on how to configure your ASP.NET application, please visit  
  4. http://go.microsoft.com/fwlink/?LinkId=301880  
  5. -->  
  6.     <configuration>  
  7.         <configSections>  
  8.             <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->  
  9.             <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />  
  10.             <sectionGroup name="elmah">  
  11.                 <section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah" />  
  12.                 <section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />  
  13.                 <section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />  
  14.                 <section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah" />  
  15.             </sectionGroup>  
  16.         </configSections>  
  17.         <connectionStrings>  
  18.             <add name="DefaultConnection" connectionString="Data Source=.;Initial Catalog=ExceptionLog;Integrated Security=True" providerName="System.Data.SqlClient" />  
  19.         </connectionStrings>  
  20.         <appSettings>  
  21.             <add key="webpages:Version" value="3.0.0.0" />  
  22.             <add key="webpages:Enabled" value="false" />  
  23.             <add key="ClientValidationEnabled" value="true" />  
  24.             <add key="UnobtrusiveJavaScriptEnabled" value="true" />  
  25.             <add key="elmah.mvc.disableHandler" value="false" />  
  26.             <add key="elmah.mvc.disableHandleErrorFilter" value="false" />  
  27.             <add key="elmah.mvc.requiresAuthentication" value="false" />  
  28.             <add key="elmah.mvc.IgnoreDefaultRoute" value="false" />  
  29.             <add key="elmah.mvc.allowedRoles" value="*" />  
  30.             <add key="elmah.mvc.allowedUsers" value="*" />  
  31.             <add key="elmah.mvc.route" value="elmah" />  
  32.             <add key="elmah.mvc.UserAuthCaseSensitive" value="true" />  
  33.         </appSettings>  
  34.         <system.web>  
  35.             <authentication mode="None" />  
  36.             <compilation debug="true" targetFramework="4.5.1" />  
  37.             <httpRuntime targetFramework="4.5.1" />  
  38.   
  39.             <!--add this-->  
  40.             <httpHandlers>  
  41.                 <add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />  
  42.             </httpHandlers>  
  43.   
  44.             <!--add this-->  
  45.             <httpModules>  
  46.                 <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />  
  47.                 <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" />  
  48.                 <add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" />  
  49.             </httpModules>  
  50.         </system.web>  
  51.         <system.webServer>  
  52.             <!--add this-->  
  53.             <handlers>  
  54.                 <add name="Elmah" verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />  
  55.             </handlers>  
  56.             <!--add this-->  
  57.             <modules>  
  58.                 <remove name="FormsAuthentication" />  
  59.                 <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" preCondition="managedHandler" />  
  60.                 <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" preCondition="managedHandler" />  
  61.                 <add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" preCondition="managedHandler" />  
  62.             </modules>  
  63.             <validation validateIntegratedModeConfiguration="false" />  
  64.   
  65.         </system.webServer>  
  66.         <runtime>  
  67.             <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">  
  68.                 <dependentAssembly>  
  69.                     <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" />  
  70.                     <bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />  
  71.                 </dependentAssembly>  
  72.                 <dependentAssembly>  
  73.                     <assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" />  
  74.                     <bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />  
  75.                 </dependentAssembly>  
  76.                 <dependentAssembly>  
  77.                     <assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" />  
  78.                     <bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />  
  79.                 </dependentAssembly>  
  80.                 <dependentAssembly>  
  81.                     <assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" />  
  82.                     <bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />  
  83.                 </dependentAssembly>  
  84.                 <dependentAssembly>  
  85.                     <assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed" />  
  86.                     <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />  
  87.                 </dependentAssembly>  
  88.                 <dependentAssembly>  
  89.                     <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />  
  90.                     <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />  
  91.                 </dependentAssembly>  
  92.                 <dependentAssembly>  
  93.                     <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />  
  94.                     <bindingRedirect oldVersion="0.0.0.0-5.2.2.0" newVersion="5.2.2.0" />  
  95.                 </dependentAssembly>  
  96.                 <dependentAssembly>  
  97.                     <assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" />  
  98.                     <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" />  
  99.                 </dependentAssembly>  
  100.                 <dependentAssembly>  
  101.                     <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />  
  102.                     <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />  
  103.                 </dependentAssembly>  
  104.                 <dependentAssembly>  
  105.                     <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />  
  106.                     <bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" />  
  107.                 </dependentAssembly>  
  108.             </assemblyBinding>  
  109.         </runtime>  
  110.         <entityFramework>  
  111.             <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">  
  112.                 <parameters>  
  113.                     <parameter value="mssqllocaldb" />  
  114.                 </parameters>  
  115.             </defaultConnectionFactory>  
  116.             <providers>  
  117.                 <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />  
  118.             </providers>  
  119.         </entityFramework>  
  120.         <elmah>  
  121.   
  122.             <!--add this-->  
  123.             <!--. If allowRemoteAccess value is set to 0, then the error log web page can only be viewed locally. If this attribute is set to 1 then the error log web page is enabled for both remote and local visitors.-->  
  124.             <security allowRemoteAccess="0" />  
  125.             <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="DefaultConnection" />  
  126.             <!--add this-->  
  127.         </elmah>  
  128.   
  129.     </configuration>  
Step 5: Let us create some exception
  1. Suppose I want to open a page that are not in our application. Let us say About2 page: localhost:55776/Home/About2
  2. Create divide by zero exception in contact page
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Web;  
  5. using System.Web.Mvc;  
  6. namespace MVCExceptionLog.Controllers  
  7. {  
  8.     public class HomeController: Controller  
  9.     {  
  10.         public ActionResult Index()  
  11.         {  
  12.             return View();  
  13.         }  
  14.         public ActionResult About()  
  15.         {  
  16.             ViewBag.Message = "Your application description page.";  
  17.             return View();  
  18.         }  
  19.         public ActionResult Contact()  
  20.         {  
  21.             int a = 0;  
  22.             int b;  
  23.             b = 1 / a;  
  24.             ViewBag.Message = "Your contact page.";  
  25.             return View();  
  26.         }  
  27.     }  
  28. }  

 
Step 5: Run the application,

Click on F5 and register a user.
 


Step 6: View the Error Log from a Web Page,

You can view the error log web page through the url localhost:portno/elmah.axd like, localhost:55776/elmah.axd.
 

Check the error log in database.
 
 
Point of Interest

In this article we learned how to log Exception in SQL Server and  view error log details from web page.