<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Darren&#039;s Blog</title>
	<atom:link href="http://dotnetdarren.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://dotnetdarren.wordpress.com</link>
	<description>.NET, MVC</description>
	<lastBuildDate>Tue, 31 Jan 2012 18:59:08 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='dotnetdarren.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Darren&#039;s Blog</title>
		<link>http://dotnetdarren.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://dotnetdarren.wordpress.com/osd.xml" title="Darren&#039;s Blog" />
	<atom:link rel='hub' href='http://dotnetdarren.wordpress.com/?pushpress=hub'/>
		<item>
		<title>MVC Logging Part 6 &#8211; Controller / Views</title>
		<link>http://dotnetdarren.wordpress.com/2010/08/16/mvc-logging-part-6-controller-views/</link>
		<comments>http://dotnetdarren.wordpress.com/2010/08/16/mvc-logging-part-6-controller-views/#comments</comments>
		<pubDate>Mon, 16 Aug 2010 05:12:03 +0000</pubDate>
		<dc:creator>darrenw74</dc:creator>
				<category><![CDATA[ASP.NET MVC]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[elmah]]></category>
		<category><![CDATA[health monitoring]]></category>
		<category><![CDATA[log4net]]></category>
		<category><![CDATA[logging]]></category>

		<guid isPermaLink="false">http://dotnetdarren.wordpress.com/?p=102</guid>
		<description><![CDATA[This is part 6 of the MVC Logging series. Other articles in the series are: MVC Logging Part 1 &#8211; Elmah MVC Logging Part 2 &#8211; Health Monitoring MVC Logging Part 3 &#8211; NLog MVC Logging Part 4 &#8211; Log4Net MVC Logging Part 5 &#8211; Model and Data Layer MVC Logging Part 6 &#8211; Controller [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=dotnetdarren.wordpress.com&amp;blog=14830143&amp;post=102&amp;subd=dotnetdarren&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<blockquote><p>This is part 6 of the MVC Logging series. Other articles in the series are:</p>
<ul>
<li><a href="http://dotnetdarren.wordpress.com/logging-on-mvc-part-1" target="_self">MVC Logging Part 1 &#8211; Elmah</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-2-health-monitoring" target="_self">MVC Logging Part 2 &#8211; Health Monitoring</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-3-–-nlog" target="_self">MVC Logging Part 3 &#8211; NLog</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-4-log4net" target="_self">MVC Logging Part 4 &#8211; Log4Net</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-5-the-model-and-data-layer" target="_self">MVC Logging Part 5 &#8211; Model and Data Layer</a></li>
<li><a href="http://dotnetdarren.wordpress.com/mvc-logging-part-6-controller-views" target="_self">MVC Logging Part 6 &#8211; Controller / Views</a></li>
</ul>
</blockquote>
<h4>Introduction</h4>
<p>In the previous article we set up our model and the repositories that we need to fetch the data from the database. Now we need to create the necessary controllers and views so that we can present all of the consolidated logging messages on our log reporting dashboard page.</p>
<h4>Creating our Logging Controller</h4>
<p>The first thing we need to do is create a controller for our log reporting pages. Here are the steps to follow:</p>
<p>1. In the solution explorer, right-click the &#8220;Controllers&#8221; directory and select &#8220;Add -&gt; Controller&#8221;.</p>
<p>2. Type in the name &#8220;LoggingController&#8221;, leave the check-box unticked and click &#8220;Add&#8221;.</p>
<p>At this point let&#8217;s take a minute to think about what we want to display on our Logging landing page.</p>
<p>We would like to:</p>
<p>* Display a filterable grid of all the log messages. The user should be able to filter by date, the name of the log provider (eg. NLog, Elmah etc), and the log level (eg. Debug, Info, Error etc)<br />
* Allow the user to page through the results<br />
* Allow the user to choose how many records to display per page.<br />
* Allow the user to click on a row in the grid to view more detailed information about the log message.</p>
<p>OK, great! So it looks like we will need the following parameters for our view:</p>
<p>* Date Start<br />
* Date End<br />
* Log Provider Name<br />
* Log Level<br />
* Current Page Index<br />
* Page Size</p>
<p>Let&#8217;s create a ViewModel called &#8220;LoggingIndexModel&#8221; to store all of these details. We&#8217;ll also combine the Start and End Dates into a string based representation called Period which will contain values like &#8220;Today&#8221;, Yesterday&#8221;, &#8220;Last Week&#8221; etc.</p>
<p>1. Create a new directory in the root of your website and name it &#8220;ViewModels&#8221;.</p>
<p>2. Add a new file called, &#8220;LoggingIndexModel.cs&#8221;.</p>
<p>3. Add the following code into the class:</p>
<p><pre class="brush: csharp;">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using MvcLoggingDemo.Models;
using MvcLoggingDemo.Services.Paging;

namespace MvcLoggingDemo.ViewModels
{
 public class LoggingIndexModel
 {
 public IPagedList&lt;LogEvent&gt; LogEvents { get; set; }

 public string LoggerProviderName { get; set; }
 public string LogLevel { get; set; }
 public string Period { get; set; }

 public int CurrentPageIndex { get; set; }
 public int PageSize { get; set; }

 public LoggingIndexModel()
 {
 CurrentPageIndex = 0;
 PageSize = 20;
 }
 }
}
</pre></p>
<p>Go ahead and modify our controller class so that it looks like the following :</p>
<p><pre class="brush: csharp;">

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Syndication;
using System.Web;
using System.Web.Mvc;

using MvcLoggingDemo.Helpers;
using MvcLoggingDemo.Models.Repository;
using MvcLoggingDemo.Models;
using MvcLoggingDemo.ViewModels;

using MvcLoggingDemo.Services.Paging;

namespace MySampleApp.Controllers
{
 [Authorize]
 public class LoggingController : Controller
 {
 private readonly ILogReportingFacade loggingRepository;

 public LoggingController()
 {
 loggingRepository = new LogReportingFacade();
 }

 public LoggingController(ILogReportingFacade repository)
 {
 loggingRepository = repository;
 }

 /// &lt;summary&gt;
 /// Returns the Index view
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;Period&quot;&gt;Text representation of the date time period. eg: Today, Yesterday, Last Week etc.&lt;/param&gt;
 /// &lt;param name=&quot;LoggerProviderName&quot;&gt;Elmah, Log4Net, NLog, Health Monitoring etc&lt;/param&gt;
 /// &lt;param name=&quot;LogLevel&quot;&gt;Debug, Info, Warning, Error, Fatal&lt;/param&gt;
 /// &lt;param name=&quot;page&quot;&gt;The current page index (0 based)&lt;/param&gt;
 /// &lt;param name=&quot;PageSize&quot;&gt;The number of records per page&lt;/param&gt;
 /// &lt;returns&gt;&lt;/returns&gt;
 public ActionResult Index(string Period, string LoggerProviderName, string LogLevel, int? page, int? PageSize)
 {
 // Set up our default values
 string defaultPeriod = Session[&quot;Period&quot;] == null ? &quot;Today&quot; : Session[&quot;Period&quot;].ToString();
 string defaultLogType = Session[&quot;LoggerProviderName&quot;] == null ? &quot;All&quot; : Session[&quot;LoggerProviderName&quot;].ToString();
 string defaultLogLevel = Session[&quot;LogLevel&quot;] == null ? &quot;Error&quot; : Session[&quot;LogLevel&quot;].ToString();

 // Set up our view model
 LoggingIndexModel model = new LoggingIndexModel();

 model.Period = (Period == null) ? defaultPeriod : Period;
 model.LoggerProviderName = (LoggerProviderName == null) ? defaultLogType : LoggerProviderName;
 model.LogLevel = (LogLevel == null) ? defaultLogLevel : LogLevel;
 model.CurrentPageIndex = page.HasValue ? page.Value - 1 : 0;
 model.PageSize = PageSize.HasValue ? PageSize.Value : 20;

 TimePeriod timePeriod = TimePeriodHelper.GetUtcTimePeriod(model.Period);

 // Grab the data from the database
 model.LogEvents = loggingRepository.GetByDateRangeAndType(model.CurrentPageIndex, model.PageSize, timePeriod.Start, timePeriod.End, model.LoggerProviderName, model.LogLevel);

 // Put this into the ViewModel so our Pager can get at these values
 ViewData[&quot;Period&quot;] = model.Period;
 ViewData[&quot;LoggerProviderName&quot;] = model.LoggerProviderName;
 ViewData[&quot;LogLevel&quot;] = model.LogLevel;
 ViewData[&quot;PageSize&quot;] = model.PageSize;

 // Put the info into the Session so that when we browse away from the page and come back that the last settings are rememberd and used.
 Session[&quot;Period&quot;] = model.Period;
 Session[&quot;LoggerProviderName&quot;] = model.LoggerProviderName;
 Session[&quot;LogLevel&quot;] = model.LogLevel;

 return View(model);
 }
}
}

</pre></p>
<h4>Index View</h4>
<p>Now right-click the Index method in the Controller and Add the Index View.</p>
<p>Choose the &#8220;Strongly typed view&#8221; option and select &#8220;MvcLoggingDemo.ViewModels.LoggingIndexModel&#8221; as the Type.</p>
<p>Select Empty for &#8220;View Type&#8221; and click &#8220;OK&#8221; to create a blank slate for our Index view.</p>
<p>Our Dashboard will allow users to see a list of errors, a chart of the errors and also a RSS feed of the errors so let&#8217;s add those options to the top of our Index page:</p>
<p><pre class="brush: xml;">
&lt;div&gt;
 View :
 &lt;strong&gt;List&lt;/strong&gt;
 | &lt;%: Html.ActionLink(&quot;Chart&quot;, &quot;Chart&quot;)%&gt;
 | &lt;%: Html.ActionLink(&quot;RSS&quot;, &quot;RssFeed&quot;, new { LoggerProviderName = Model.LoggerProviderName, Period = Model.Period, LogLevel = Model.LogLevel }, new { target = &quot;_blank&quot; })%&gt;
 &lt;/div&gt;
</pre></p>
<p>As our index page will be the list or grid based view we will need a way to filter the error messages to be displayed. So let&#8217;s start a HTML form and add some filtering fields to our view:</p>
<p><pre class="brush: xml;">
&lt;div&gt;
 &lt;div&gt;

 Logger : &lt;%: Html.DropDownList(&quot;LoggerProviderName&quot;, new SelectList(MvcLoggingDemo.Helpers.FormsHelper.LogProviderNames, &quot;Value&quot;, &quot;Text&quot;))%&gt;

 Level : &lt;%: Html.DropDownList(&quot;LogLevel&quot;, new SelectList(MvcLoggingDemo.Helpers.FormsHelper.LogLevels, &quot;Value&quot;, &quot;Text&quot;))%&gt;

 For : &lt;%: Html.DropDownList(&quot;Period&quot;, new SelectList(MvcLoggingDemo.Helpers.FormsHelper.CommonTimePeriods, &quot;Value&quot;, &quot;Text&quot;))%&gt;

 &lt;input id=&quot;btnGo&quot; name=&quot;btnGo&quot; type=&quot;submit&quot; value=&quot;Apply Filter&quot; /&gt;

 &lt;/div&gt;
 &lt;/div&gt;
</pre></p>
<p>We also need a header for our grid that will display the number of  messages found  and a way for the user to change the number of records displayed per page. Let&#8217;s add our grid header now:</p>
<p><pre class="brush: xml;">
&lt;div&gt;

 &lt;div&gt;
 &lt;div&gt;

 &lt;span style=&quot;float: left&quot;&gt;
 &lt;%: string.Format(&quot;{0} records found. Page {1} of {2}&quot;, Model.LogEvents.TotalItemCount, Model.LogEvents.PageNumber, Model.LogEvents.PageCount)%&gt;
 &lt;/span&gt;

 &lt;span style=&quot;float: right&quot;&gt;
 Show &lt;%: Html.DropDownList(&quot;PageSize&quot;, new SelectList(MvcLoggingDemo.Helpers.FormsHelper.PagingPageSizes, &quot;Value&quot;, &quot;Text&quot;), new { onchange = &quot;document.getElementById('myform').submit()&quot; })%&gt; results per page
 &lt;/span&gt;

 &lt;div style=&quot;clear: both&quot;&gt;&lt;/div&gt;

 &lt;/div&gt;

 &lt;/div&gt;

 &lt;div&gt;
 &lt;div&gt;
 &lt;%= Html.Pager(ViewData.Model.LogEvents.PageSize, ViewData.Model.LogEvents.PageNumber, ViewData.Model.LogEvents.TotalItemCount, new { LogType = ViewData[&quot;LogType&quot;], Period = ViewData[&quot;Period&quot;], PageSize = ViewData[&quot;PageSize&quot;] })%&gt;
 &lt;/div&gt;
 &lt;/div&gt;

 &lt;/div&gt;

 &lt;% } %&gt;
</pre></p>
<p>Notice how the paging helper using the information from the ViewData and also our need to add in the routing data to include information from our filters.</p>
<p>With the grid filter and the grid header all done we now need to turn our attention to the actual grid data. Add the following code to the view:</p>
<p><pre class="brush: xml;">
&lt;% if (Model.LogEvents.Count() == 0) { %&gt;

 &lt;p&gt;No results found&lt;/p&gt;

 &lt;% } else { %&gt;

 &lt;div&gt;
 &lt;table&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;
 #
 &lt;/th&gt;
 &lt;th&gt;
 Log
 &lt;/th&gt;
 &lt;th&gt;
 Date
 &lt;/th&gt;
 &lt;th style='white-space: nowrap;'&gt;
 Time ago
 &lt;/th&gt;
 &lt;th&gt;
 Host
 &lt;/th&gt;
 &lt;th&gt;
 Source
 &lt;/th&gt;
 &lt;th&gt;
 Message
 &lt;/th&gt;
 &lt;th&gt;
 Type
 &lt;/th&gt;
 &lt;th&gt;
 Level
 &lt;/th&gt;
 &lt;/tr&gt;

 &lt;% int i = 0;  foreach (var item in Model.LogEvents)
 { %&gt;

 &lt;tralt&quot; : &quot;&quot; %&gt;&quot;&gt;
 &lt;td&gt;
 &lt;%: Html.ActionLink(&quot;Details&quot;, &quot;Details&quot;, new { id = item.Id.ToString(), loggerProviderName = item.LoggerProviderName })%&gt;
 &lt;/td&gt;
 &lt;td&gt;
 &lt;%: i.ToString() %&gt;
 &lt;/td&gt;
 &lt;td&gt;
 &lt;%: item.LoggerProviderName%&gt;
 &lt;/td&gt;
 &lt;td style='white-space: nowrap;'&gt;
 &lt;%: String.Format(&quot;{0:g}&quot;, item.LogDate.ToLocalTime())%&gt;
 &lt;/td&gt;
 &lt;td style='white-space: nowrap;'&gt;
 &lt;%: item.LogDate.ToLocalTime().TimeAgoString()%&gt;
 &lt;/td&gt;
 &lt;td&gt;
 &lt;%: item.MachineName%&gt;
 &lt;/td&gt;
 &lt;td&gt;
 &lt;%: item.Source%&gt;
 &lt;/td&gt;
 &lt;td&gt;
 &lt;pre&gt;&lt;%: item.Message.WordWrap(80) %&gt;&lt;/pre&gt;
 &lt;/td&gt;
 &lt;td&gt;
 &lt;%: item.Type%&gt;
 &lt;/td&gt;
 &lt;td&gt;
 &lt;%: item.Level.ToLower().ToPascalCase() %&gt;
 &lt;/td&gt;
 &lt;/tr&gt;

 &lt;% } %&gt;

 &lt;/table&gt;
 &lt;/div&gt;

 &lt;% } %&gt;

 &lt;div&gt;

 &lt;div&gt;
 &lt;div&gt;
 &lt;%= Html.Pager(ViewData.Model.LogEvents.PageSize, ViewData.Model.LogEvents.PageNumber, ViewData.Model.LogEvents.TotalItemCount, new { LogType = ViewData[&quot;LogType&quot;], Period = ViewData[&quot;Period&quot;], PageSize = ViewData[&quot;PageSize&quot;] })%&gt;
 &lt;/div&gt;
 &lt;/div&gt;

 &lt;/div&gt;
</pre></p>
<p>In the snippet above, first we check to see if there are any log messages to be displayed. If not we let the user know that no records were found.</p>
<p>Instead of just displaying the datetime that the log message was recorded we also add a column to display the amount of time that has elapsed since then in a user friendly way. We use a DateTime extension method called &#8220;TimeAgoString()&#8221; to accomplish this.</p>
<p>Another nice feature is that we would also like to word wrap the error messages. Whilst building this project I received several error messages that had very long sequences of unbroken characters. The WordWrap helper function will split very long strings into lines that have a maximum length that you specify. It will try to break the lines apart on words but if it encounters long unbroken text it will just split them where necessary. The end result is a nicely formatted display.</p>
<p>Also, when dealing with multiple log providers it is nice to display the Log Level consistently across all of them. In order to accomplish this we convert the Log Level to lower case and then to PascalCase using a string extension helper method.</p>
<p>And lastly, we repeat the paging at the bottom of the grid so that if the user is looking at 50 or 100 records at a time they do not need to scroll back up to the top of the grid to navigate to the next page of results.</p>
<p>Now let&#8217;s run the website, log in, and view our Log Reporting Dashboard page:</p>
<p><a href="http://dotnetdarren.files.wordpress.com/2010/07/log-dashboard.png"><img class="alignnone size-medium wp-image-9" title="Logging - Dashboard" src="http://dotnetdarren.files.wordpress.com/2010/07/log-dashboard.png?w=300&#038;h=241" alt="" width="300" height="241" /></a></p>
<p>To wrap up this article let&#8217;s quickly create the details page for a log message.</p>
<h4>Details View</h4>
<p>1. The first step is to go to the &#8220;Logging&#8221; controller and add the following code underneath the &#8220;Index&#8221; action:</p>
<p><pre class="brush: csharp;">
//
 // GET: /Logging/Details/5

 public ActionResult Details(string loggerProviderName, string id)
 {
 LogEvent logEvent = loggingRepository.GetById(loggerProviderName, id);

 return View(logEvent);
 }

</pre></p>
<p>2. Right-click the &#8220;Details&#8221; method in the &#8220;Controller&#8221; and choose &#8220;Add View&#8221;<br />
Select &#8220;Strongly Typed View&#8221; and choose the &#8220;MvcLoggingDemo.Models.LogEvent&#8221; Type.<br />
Select &#8220;Empty&#8221; for the &#8220;Content Type&#8221;<br />
Click on &#8220;Add&#8221;</p>
<p>3. Add the following code to the &#8220;Details&#8221; page to the &#8220;MainContent&#8221; content placeholder:</p>
<p><pre class="brush: xml;">
&lt;h2&gt;Details&lt;/h2&gt;

 &lt;p&gt;
 &lt;%: Html.ActionLink(&quot;Back to List&quot;, &quot;Index&quot;) %&gt;
 &lt;/p&gt;

 &lt;fieldset&gt;
 &lt;legend&gt;Fields&lt;/legend&gt;

 &lt;div&gt;Id&lt;/div&gt;
 &lt;div&gt;&lt;%: Model.Id %&gt;&lt;/div&gt;

 &lt;div&gt;LogDate&lt;/div&gt;
 &lt;div&gt;&lt;%: String.Format(&quot;{0:g}&quot;, Model.LogDate) %&gt;&lt;/div&gt;

 &lt;div&gt;Name&lt;/div&gt;
 &lt;div&gt;&lt;%: Model.LoggerProviderName %&gt;&lt;/div&gt;

 &lt;div&gt;Source&lt;/div&gt;
 &lt;div&gt;&lt;%: Model.Source %&gt;&lt;/div&gt;

 &lt;div&gt;MachineName&lt;/div&gt;
 &lt;div&gt;&lt;%: Model.MachineName %&gt;&lt;/div&gt;

 &lt;div&gt;Type&lt;/div&gt;
 &lt;div&gt;&lt;%: Model.Type %&gt;&lt;/div&gt;

 &lt;div&gt;Level&lt;/div&gt;
 &lt;div&gt;&lt;%: Model.Level %&gt;&lt;/div&gt;

 &lt;div&gt;Message&lt;/div&gt;
 &lt;div&gt;
 &lt;pre&gt;&lt;%: Model.Message.WordWrap(80) %&gt;&lt;/pre&gt;
 &lt;/div&gt;

 &lt;div&gt;StackTrace&lt;/div&gt;
 &lt;div&gt;&lt;%: Model.StackTrace %&gt;&lt;/div&gt;

 &lt;/fieldset&gt;

 &lt;% =FormsHelper.OutputXmlTableForLogging(Model.AllXml) %&gt;

 &lt;p&gt;
 &lt;%: Html.ActionLink(&quot;Back to List&quot;, &quot;Index&quot;) %&gt;
 &lt;/p&gt;
</pre></p>
<p>Notice that we once again we use the WordWrap helper function to tidy up the error message</p>
<p>At the bottom of the page we use another helper method to display the server variables and cookies in a table.</p>
<p>Here is what the details page looks like :</p>
<p><a href="http://dotnetdarren.files.wordpress.com/2010/08/log-message-details.png"><img class="alignnone size-medium wp-image-108" title="log-message-details" src="http://dotnetdarren.files.wordpress.com/2010/08/log-message-details.png?w=283&#038;h=300" alt="" width="283" height="300" /></a></p>
<h4>Conclusion</h4>
<p>That ends part 6 of this series. In this article we created the Logging controller and views for the &#8220;Index&#8221; and &#8220;Details&#8221; actions on the controller. In the next article we will add an RSS feed to our dashboard.</p>
<h4>Download</h4>
<p>The sourcecode for part 6 is on the Downloads tab of the <a href="http://mvclogging.codeplex.com/" target="_blank">associated Codeplex website</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/dotnetdarren.wordpress.com/102/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/dotnetdarren.wordpress.com/102/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/dotnetdarren.wordpress.com/102/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/dotnetdarren.wordpress.com/102/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/dotnetdarren.wordpress.com/102/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/dotnetdarren.wordpress.com/102/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/dotnetdarren.wordpress.com/102/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/dotnetdarren.wordpress.com/102/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/dotnetdarren.wordpress.com/102/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/dotnetdarren.wordpress.com/102/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/dotnetdarren.wordpress.com/102/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/dotnetdarren.wordpress.com/102/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/dotnetdarren.wordpress.com/102/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/dotnetdarren.wordpress.com/102/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=dotnetdarren.wordpress.com&amp;blog=14830143&amp;post=102&amp;subd=dotnetdarren&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://dotnetdarren.wordpress.com/2010/08/16/mvc-logging-part-6-controller-views/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/c6764e85b5f6b2e2e71cb35e39c28fc0?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">darrenw74</media:title>
		</media:content>

		<media:content url="http://dotnetdarren.files.wordpress.com/2010/07/log-dashboard.png?w=300" medium="image">
			<media:title type="html">Logging - Dashboard</media:title>
		</media:content>

		<media:content url="http://dotnetdarren.files.wordpress.com/2010/08/log-message-details.png?w=283" medium="image">
			<media:title type="html">log-message-details</media:title>
		</media:content>
	</item>
		<item>
		<title>Logging in MVC Part 5 &#8211; The Model and Data Layer</title>
		<link>http://dotnetdarren.wordpress.com/2010/07/29/logging-in-mvc-part-5-the-model-and-data-layer/</link>
		<comments>http://dotnetdarren.wordpress.com/2010/07/29/logging-in-mvc-part-5-the-model-and-data-layer/#comments</comments>
		<pubDate>Thu, 29 Jul 2010 03:55:06 +0000</pubDate>
		<dc:creator>darrenw74</dc:creator>
				<category><![CDATA[ASP.NET MVC]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[elmah]]></category>
		<category><![CDATA[health monitoring]]></category>
		<category><![CDATA[log4net]]></category>
		<category><![CDATA[logging]]></category>

		<guid isPermaLink="false">http://dotnetdarren.wordpress.com/?p=74</guid>
		<description><![CDATA[This is part 5 of the MVC Logging series. Other articles in the series are: MVC Logging Part 1 &#8211; Elmah MVC Logging Part 2 &#8211; Health Monitoring MVC Logging Part 3 &#8211; NLog MVC Logging Part 4 &#8211; Log4Net MVC Logging Part 5 &#8211; Model and Data Layer Introduction This is the 5th article [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=dotnetdarren.wordpress.com&amp;blog=14830143&amp;post=74&amp;subd=dotnetdarren&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<blockquote><p>This is part 5 of the MVC Logging series. Other articles in the series are:</p>
<ul>
<li><a href="http://dotnetdarren.wordpress.com/logging-on-mvc-part-1" target="_self">MVC Logging Part 1 &#8211; Elmah</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-2-health-monitoring" target="_self">MVC Logging Part 2 &#8211; Health Monitoring</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-3-–-nlog" target="_self">MVC Logging Part 3 &#8211; NLog</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-4-log4net" target="_self">MVC Logging Part 4 &#8211; Log4Net</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-5-the-model-and-data-layer" target="_self">MVC Logging Part 5 &#8211; Model and Data Layer</a></li>
</ul>
</blockquote>
<h4>Introduction</h4>
<p>This is the 5th article in a series on MVC logging.</p>
<p>Our sample website now has Elmah, NLog, Log4Net and Health monitoring  set up and working. Now it is time to start work on our log reporting  website by tying them all together.</p>
<h4>Preparing the database</h4>
<p>ASP.NET Health Monitoring logs a lot of different types of messages but there is no way to differentiate whether a message is just for information purposes or whether it is an error message that may need attention. So to address this issue, let&#8217;s create a new table called &#8220;aspnet_WebEvent_ErrorCodes&#8221; and introduce a column called &#8220;Level&#8221; which will map each message eventcode to either &#8220;Info&#8221;, &#8220;Error&#8221;.</p>
<p>The reason for doing this is so that we have a common &#8220;Level&#8221; attribute that we can use for all of our logging providers and this will allow us to later on filter all messages by their Log Level. Elmah for example will only be used to log unhandled exceptions so the LogLevel for our Elmah messages will always be &#8220;Error&#8221;.</p>
<p>Here is the database script necessary to add the new table to our database:</p>
<p><pre class="brush: sql;">
/****** Object:  Table [dbo].[aspnet_WebEvent_ErrorCodes]    Script Date: 07/29/2010 09:56:45 ******/
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[aspnet_WebEvent_ErrorCodes]') AND type in (N'U'))
DROP TABLE [dbo].[aspnet_WebEvent_ErrorCodes]
GO
/****** Object:  Default [DF_aspnet_WebEvent_ErrorCodes_Level]    Script Date: 07/29/2010 09:56:45 ******/
IF  EXISTS (SELECT * FROM sys.default_constraints WHERE object_id = OBJECT_ID(N'[dbo].[DF_aspnet_WebEvent_ErrorCodes_Level]') AND parent_object_id = OBJECT_ID(N'[dbo].[aspnet_WebEvent_ErrorCodes]'))
Begin
IF  EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[DF_aspnet_WebEvent_ErrorCodes_Level]') AND type = 'D')
BEGIN
ALTER TABLE [dbo].[aspnet_WebEvent_ErrorCodes] DROP CONSTRAINT [DF_aspnet_WebEvent_ErrorCodes_Level]
END

End
GO
/****** Object:  Table [dbo].[aspnet_WebEvent_ErrorCodes]    Script Date: 07/29/2010 09:56:45 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[aspnet_WebEvent_ErrorCodes]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[aspnet_WebEvent_ErrorCodes](
 [Id] [int] IDENTITY(1,1) NOT NULL,
 [Name] [nvarchar](255) COLLATE Latin1_General_CI_AS NOT NULL,
 [EventCode] [int] NOT NULL,
 [Level] [nvarchar](10) COLLATE Latin1_General_CI_AS NOT NULL,
 CONSTRAINT [PK_aspnet_WebEvent_ErrorCodes] PRIMARY KEY CLUSTERED
(
 [Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON)
)
END
GO
SET IDENTITY_INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ON
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (1, N'InvalidEventCode', -1, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (2, N'UndefinedEventCode/UndefinedEventDetailCode', 0, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (3, N'Not used', -9999, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (4, N'ApplicationCodeBase', 1000, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (5, N'ApplicationStart', 1001, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (6, N'ApplicationShutdown', 1002, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (7, N'ApplicationCompilationStart', 1003, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (8, N'ApplicationCompilationEnd', 1004, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (9, N'ApplicationHeartbeat', 1005, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (10, N'RequestCodeBase', 2000, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (11, N'RequestTransactionComplete', 2001, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (12, N'RequestTransactionAbort', 2002, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (13, N'ErrorCodeBase', 3000, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (14, N'RuntimeErrorRequestAbort', 3001, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (15, N'RuntimeErrorViewStateFailure', 3002, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (16, N'RuntimeErrorValidationFailure', 3003, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (17, N'RuntimeErrorPostTooLarge', 3004, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (18, N'RuntimeErrorUnhandledException', 3005, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (19, N'WebErrorParserError', 3006, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (20, N'WebErrorCompilationError', 3007, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (21, N'WebErrorConfigurationError', 3008, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (22, N'WebErrorOtherError', 3009, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (23, N'WebErrorPropertyDeserializationError', 3010, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (24, N'WebErrorObjectStateFormatterDeserializationError', 3011, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (25, N'AuditCodeBase', 4000, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (26, N'AuditFormsAuthenticationSuccess', 4001, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (27, N'AuditMembershipAuthenticationSuccess', 4002, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (28, N'AuditUrlAuthorizationSuccess', 4003, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (29, N'AuditFileAuthorizationSuccess', 4004, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (30, N'AuditFormsAuthenticationFailure', 4005, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (31, N'AuditMembershipAuthenticationFailure', 4006, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (32, N'AuditUrlAuthorizationFailure', 4007, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (33, N'AuditFileAuthorizationFailure', 4008, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (34, N'AuditInvalidViewStateFailure', 4009, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (35, N'AuditUnhandledSecurityException', 4010, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (36, N'AuditUnhandledAccessException', 4011, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (37, N'MiscCodeBase', 6000, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (38, N'WebEventProviderInformation', 6001, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (39, N'ApplicationDetailCodeBase', 50000, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (40, N'ApplicationShutdownUnknown', 50001, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (41, N'ApplicationShutdownHostingEnvironment', 50002, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (42, N'ApplicationShutdownChangeInGlobalAsax', 50003, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (43, N'ApplicationShutdownConfigurationChange', 50004, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (44, N'ApplicationShutdownUnloadAppDomainCalled', 50005, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (45, N'ApplicationShutdownChangeInSecurityPolicyFile', 50006, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (46, N'ApplicationShutdownBinDirChangeOrDirectoryRename', 50007, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (47, N'ApplicationShutdownBrowsersDirChangeOrDirectoryRename', 50008, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (48, N'ApplicationShutdownCodeDirChangeOrDirectoryRename', 50009, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (49, N'ApplicationShutdownResourcesDirChangeOrDirectoryRename', 50010, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (50, N'ApplicationShutdownIdleTimeout', 50011, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (51, N'ApplicationShutdownPhysicalApplicationPathChanged', 50012, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (52, N'ApplicationShutdownHttpRuntimeClose', 50013, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (53, N'ApplicationShutdownInitializationError', 50014, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (54, N'ApplicationShutdownMaxRecompilationsReached', 50015, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (55, N'StateServerConnectionError', 50016, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (56, N'AuditDetailCodeBase', 50200, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (57, N'InvalidTicketFailure', 50201, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (58, N'ExpiredTicketFailure', 50202, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (59, N'InvalidViewStateMac', 50203, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (60, N'InvalidViewState', 50204, N'Error')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (61, N'WebEventDetailCodeBase', 50300, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (62, N'SqlProviderEventsDropped', 50301, N'Info')
INSERT [dbo].[aspnet_WebEvent_ErrorCodes] ([Id], [Name], [EventCode], [Level]) VALUES (63, N'WebExtendedBase', 100000, N'Info')
SET IDENTITY_INSERT [dbo].[aspnet_WebEvent_ErrorCodes] OFF
/****** Object:  Default [DF_aspnet_WebEvent_ErrorCodes_Level]    Script Date: 07/29/2010 09:56:45 ******/
IF Not EXISTS (SELECT * FROM sys.default_constraints WHERE object_id = OBJECT_ID(N'[dbo].[DF_aspnet_WebEvent_ErrorCodes_Level]') AND parent_object_id = OBJECT_ID(N'[dbo].[aspnet_WebEvent_ErrorCodes]'))
Begin
IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[DF_aspnet_WebEvent_ErrorCodes_Level]') AND type = 'D')
BEGIN
ALTER TABLE [dbo].[aspnet_WebEvent_ErrorCodes] ADD  CONSTRAINT [DF_aspnet_WebEvent_ErrorCodes_Level]  DEFAULT ('Info') FOR [Level]
END

End
GO
</pre></p>
<p>The last step in preparing our database is to create a new view that will return all of the ASP.NET Health Monitoring messages with the LogLevel included.</p>
<p>Here is the database script to create the view:</p>
<p><pre class="brush: sql;">

CREATE VIEW vw_aspnet_WebEvents_extended
AS

SELECT
 webEvent.EventId
 , webEvent.EventTimeUtc
 , webEvent.EventTime
 , webEvent.EventType
 , webEvent.EventSequence
 , webEvent.EventOccurrence
 , webEvent.EventCode
 , webEvent.EventDetailCode
 , webEvent.Message
 , webEvent.ApplicationPath
 , webEvent.ApplicationVirtualPath
 , webEvent.MachineName
 , webEvent.RequestUrl
 , webEvent.ExceptionType
 , webEvent.Details
 , webEventCodes.Level
FROM
 dbo.aspnet_WebEvent_Events AS webEvent
INNER JOIN
 dbo.aspnet_WebEvent_ErrorCodes AS webEventCodes ON webEvent.EventCode = webEventCodes.EventCode
</pre></p>
<p>Before we get started on our model and data access layer I want to   introduce some helper classes that we will be using along the way.</p>
<h4>Paging Service</h4>
<p>Every  database application needs to page results from the database   and  display them to the end user.I looked at several paging    implementations on the web but finally settled on <a href="http://blogs.taiga.nl/martijn/2008/08/27/paging-with-aspnet-mvc/" target="_blank">Martin Bolands paging   mechanism.</a></p>
<p>In  our sample website I have created a new folder called &#8220;Paging&#8221;   under  the &#8220;Services&#8221; folder and added the following 4 classes (code is    provided in the downloadable code at the end of this article) :</p>
<p>IPagedList.cs<br />
PagedList.cs<br />
Pager.cs<br />
PagingExtensions.cs</p>
<p>We will be making use of the PagedList class in our repository class below.</p>
<h4>Creating the Model</h4>
<p>For our sample website we will be using Linq to Entities.</p>
<p>1. Create a new folder underneath Models and name it &#8220;Entities&#8221;.<br />
2. Right-click the folder and select &#8220;Add -&gt; New Item&#8221;.<br />
3. Select the &#8220;ADO.NET Entity Data Model&#8221; from the Data category. In the name field, type &#8220;MvcLoggingDemo.edmx&#8221; and click on &#8220;Add&#8221;.<br />
4. Select the &#8220;Create from Database&#8221; option and the click on &#8220;Next&#8221;.<br />
5. The wizard should automatically find the SampleDatabase.mdf in the app_data folder. Ensure that the checkbox at the bottom of the dialog is &#8220;checked&#8221; and provide the connection string name of  &#8220;MvcLoggingDemoContainer&#8221;. Click &#8220;Next&#8221;.<br />
6. Expand the tables node and select the following tables : Elmah_Error, NLog_Error, Log4Net_Error</p>
<p><a href="http://dotnetdarren.files.wordpress.com/2010/07/log_entitymodel_select_logging_tables.png"><img class="alignnone size-medium wp-image-88" title="Logging - Entity Model" src="http://dotnetdarren.files.wordpress.com/2010/07/log_entitymodel_select_logging_tables.png?w=300&#038;h=267" alt="" width="300" height="267" /></a><br />
7. Expand the views node and select the following view : vw_aspnet_WebEvents_extended<br />
8. Click OK and the tables and the view should now be added to the Entities diagram.</p>
<p>At this point Linq to Entities will have created 4 entities that we  can use to retrieve information from our database but we need a new  Entity that we can use to store the common information from all of our  tables.</p>
<p><a href="http://dotnetdarren.files.wordpress.com/2010/07/log_entitymodel_diagram_logging_tables.png"><img class="alignnone size-medium wp-image-89" title="Logging - Entity Diagram - Tables" src="http://dotnetdarren.files.wordpress.com/2010/07/log_entitymodel_diagram_logging_tables.png?w=300&#038;h=219" alt="" width="300" height="219" /></a></p>
<p>So underneath our Models folder, create a new class and name it &#8220;LogEvent.cs&#8221;</p>
<p>Replace the file contents with the code below:</p>
<p><pre class="brush: csharp;">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MvcLoggingDemo.Models
{

 /// &lt;summary&gt;
 /// This represents a generic log message that can store log information about
 /// any logger implemented. Eg: Log4Net, NLog, Health Monitoring, Elmah
 /// &lt;/summary&gt;
 public class LogEvent
 {
 private string _Id = string.Empty;

 /// &lt;summary&gt;
 /// String representation of the event log id
 /// &lt;/summary&gt;
 public string Id
 {
 get
 {
 switch (IdType)
 {
 case &quot;number&quot;:
 return IdAsInteger.ToString();

 case &quot;guid&quot;:
 return IdAsGuid.ToString();

 default:
 return _Id;
 }
 }

 set
 {
 _Id = value;
 }
 }

 /// &lt;summary&gt;
 /// Stores the Id of the log event as a GUID
 /// &lt;/summary&gt;
 internal Guid IdAsGuid { get; set; }

 /// &lt;summary&gt;
 /// Stores the Id of the log event as an integer
 /// &lt;/summary&gt;
 internal int IdAsInteger { get; set; }

 /// &lt;summary&gt;
 /// Stores the base type of the id
 /// Valid values are : number, guid, string
 /// &lt;/summary&gt;
 internal string IdType { get; set; }

 /// &lt;summary&gt;
 /// The date of the log event
 /// &lt;/summary&gt;
 public DateTime LogDate { get; set; }

 /// &lt;summary&gt;
 /// The name of the log provider
 /// Example values are NLog, Log4Net, Elmah, Health Monitoring
 /// &lt;/summary&gt;
 public string LoggerProviderName { get; set; }

 /// &lt;summary&gt;
 /// Information about where the error occurred
 /// &lt;/summary&gt;
 public string Source { get; set; }

 /// &lt;summary&gt;
 /// The machine where the error occured
 /// &lt;/summary&gt;
 public string MachineName { get; set; }

 /// &lt;summary&gt;
 /// The Type name of the class that logged the error
 /// &lt;/summary&gt;
 public string Type { get; set; }

 /// &lt;summary&gt;
 /// The level of the message logged
 /// Valid values are : Debug, Info, Warning, Error, Fatal
 /// &lt;/summary&gt;
 public string Level { get; set; }

 /// &lt;summary&gt;
 /// The message that was logged
 /// &lt;/summary&gt;
 public string Message { get; set; }

 /// &lt;summary&gt;
 /// If the message was from an error this value will contain details of the stack trace.
 /// Otherwise it will be empty
 /// &lt;/summary&gt;
 public string StackTrace { get; set; }

 /// &lt;summary&gt;
 /// If the message was from an error this value will contain details of the HTTP Server variables and Cookies.
 /// Otherwise it will be empty
 /// &lt;/summary&gt;
 public string AllXml { get; set; }
 }
}
</pre></p>
<p>Our various logging providers all use a different kind of primary key. Some use a unique identifier, some use an integer. So our LogEvent class has a few properties that can store the Id in various datatypes internally within our assembly but we have one public Id property available to all the consumers of the class. To accommodate this we also introduce another property called IdType that stores the type of datatype used by the logging provider. At first I tried to use a generic &#8220;Object&#8221; datatype for the Id property but I could not get the Linq to Entities query working when doing a UNION on all of the IQueryable results returned from the providers.</p>
<p>All of the other public properties are self-explanatory.</p>
<h4>Creating the Data Access Layer</h4>
<p>For our Data Access Layer we will be using the Repository pattern and  the first thing we need to do is create an interface that our  repository will implement.</p>
<p>1. Create a new folder called Repository underneath Models<br />
2. Create a new folder underneath Repository called Interfaces<br />
3. In the Interfaces directory, create a new class file and name it, &#8220;ILogReportingRepository&#8221;<br />
4. Replace the contents of the newly created file with the code below :</p>
<p><pre class="brush: csharp;">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using MvcLoggingDemo.Services.Paging;

namespace MvcLoggingDemo.Models.Repository
{
 /// &lt;summary&gt;
 /// This interface provides the methods that we need to so that we can report on log messages stored by the various
 /// logging tools used in our website
 /// &lt;/summary&gt;
 public interface ILogReportingRepository
 {
 /// &lt;summary&gt;
 /// Gets a filtered list of log events
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;pageIndex&quot;&gt;0 based page index&lt;/param&gt;
 /// &lt;param name=&quot;pageSize&quot;&gt;max number of records to return&lt;/param&gt;
 /// &lt;param name=&quot;start&quot;&gt;start date&lt;/param&gt;
 /// &lt;param name=&quot;end&quot;&gt;end date&lt;/param&gt;
 /// &lt;param name=&quot;logLevel&quot;&gt;The level of the log messages&lt;/param&gt;
 /// &lt;returns&gt;A filtered list of log events&lt;/returns&gt;
 IQueryable&lt;LogEvent&gt; GetByDateRangeAndType(int pageIndex, int pageSize, DateTime start, DateTime end, string logLevel);

 /// &lt;summary&gt;
 /// Returns a single Log event
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;id&quot;&gt;Id of the log event as a string&lt;/param&gt;
 /// &lt;returns&gt;A single Log event&lt;/returns&gt;
 LogEvent GetById(string id);

 /// &lt;summary&gt;
 /// Clears log messages between a date range and for specified log levels
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;start&quot;&gt;start date&lt;/param&gt;
 /// &lt;param name=&quot;end&quot;&gt;end date&lt;/param&gt;
 /// &lt;param name=&quot;logLevels&quot;&gt;string array of log levels&lt;/param&gt;
 void ClearLog(DateTime start, DateTime end, string[] logLevels);
 }
}
</pre></p>
<p>5. In the Repository directory, create a new class file and name it, &#8220;ElmahRepository&#8221;<br />
6. Replace the contents of the newly created file with the code below :</p>
<p><pre class="brush: csharp;">
using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.Data.SqlClient;
using System.Linq;
using System.Web;

using MvcLoggingDemo.Models.Entities;
using MvcLoggingDemo.Services.Paging;
using MvcLoggingDemo.Helpers;

namespace MvcLoggingDemo.Models.Repository
{
 /// &lt;summary&gt;
 /// This class extracts information that Elmah stores so that we can report on it
 /// &lt;/summary&gt;
 public class ElmahRepository : ILogReportingRepository
 {
 MvcLoggingDemoContainer _context = null;

 /// &lt;summary&gt;
 /// Default Constructor uses the default Entity Container
 /// &lt;/summary&gt;
 public ElmahRepository()
 {
 _context = new MvcLoggingDemoContainer();
 }

 /// &lt;summary&gt;
 /// Overloaded constructor that can take an EntityContainer as a parameter so that it can be mocked out by our tests
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;context&quot;&gt;The Entity context&lt;/param&gt;
 public ElmahRepository(MvcLoggingDemoContainer context)
 {
 _context = context;
 }

 /// &lt;summary&gt;
 /// Gets a filtered list of log events
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;pageIndex&quot;&gt;0 based page index&lt;/param&gt;
 /// &lt;param name=&quot;pageSize&quot;&gt;max number of records to return&lt;/param&gt;
 /// &lt;param name=&quot;start&quot;&gt;start date&lt;/param&gt;
 /// &lt;param name=&quot;end&quot;&gt;end date&lt;/param&gt;
 /// &lt;param name=&quot;logLevel&quot;&gt;The level of the log messages&lt;/param&gt;
 /// &lt;returns&gt;A filtered list of log events&lt;/returns&gt;
 public IQueryable&lt;LogEvent&gt; GetByDateRangeAndType(int pageIndex, int pageSize, DateTime start, DateTime end, string logLevel)
 {
 IQueryable&lt;LogEvent&gt; list = (from a in _context.ELMAH_Error
 where a.TimeUtc &gt;= start &amp;&amp; a.TimeUtc &lt;= end
 &amp;&amp; (logLevel == &quot;All&quot; || logLevel == &quot;Error&quot;)
 select new LogEvent { IdType = &quot;guid&quot;
 , Id = &quot;&quot;
 , IdAsInteger = 0
 , IdAsGuid = a.ErrorId
 , LoggerProviderName = &quot;Elmah&quot;
 , LogDate = a.TimeUtc
 , MachineName = a.Host
 , Message = a.Message
 , Type = a.Type
 , Level = &quot;Error&quot;
 , Source = a.Source, StackTrace = &quot;&quot; });

 return list;
 }

 /// &lt;summary&gt;
 /// Returns a single Log event
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;id&quot;&gt;Id of the log event as a string&lt;/param&gt;
 /// &lt;returns&gt;A single Log event&lt;/returns&gt;
 public LogEvent GetById(string id)
 {
 Guid guid = new Guid(id);
 LogEvent logEvent = (from b in _context.ELMAH_Error
 where b.ErrorId == guid
 select new LogEvent { IdType = &quot;guid&quot;
 , IdAsGuid = b.ErrorId
 , LoggerProviderName = &quot;Elmah&quot;
 , LogDate = b.TimeUtc
 , MachineName = b.Host
 , Message = b.Message
 , Type = b.Type
 , Level = &quot;Error&quot;
 , Source = b.Source
 , StackTrace = &quot;&quot;
 , AllXml = b.AllXml })
 .SingleOrDefault();

 return logEvent;
 }

 /// &lt;summary&gt;
 /// Clears log messages between a date range and for specified log levels
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;start&quot;&gt;start date&lt;/param&gt;
 /// &lt;param name=&quot;end&quot;&gt;end date&lt;/param&gt;
 /// &lt;param name=&quot;logLevels&quot;&gt;string array of log levels&lt;/param&gt;
 public void ClearLog(DateTime start, DateTime end, string[] logLevels)
 {
 string commandText = &quot;delete from Elmah_Error WHERE TimeUtc &gt;= @p0 and TimeUtc &lt;= @p1&quot;;

 SqlParameter paramStartDate = new SqlParameter { ParameterName = &quot;p0&quot;, Value = start.ToUniversalTime(), DbType = System.Data.DbType.DateTime };
 SqlParameter paramEndDate = new SqlParameter { ParameterName = &quot;p1&quot;, Value = end.ToUniversalTime(), DbType = System.Data.DbType.DateTime };

 _context.ExecuteStoreCommand(commandText, paramStartDate, paramEndDate);
 }

 }
}
 </pre></p>
<p>7. In the Repository directory, create a new class file and name it,  &#8220;NLogRepository&#8221;<br />
8. Replace the contents of the newly created file with the code below :</p>
<p><pre class="brush: csharp;">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using MvcLoggingDemo.Models.Entities;
using MvcLoggingDemo.Services.Paging;
using MvcLoggingDemo.Helpers;
using System.Data.SqlClient;

namespace MvcLoggingDemo.Models.Repository
{
 /// &lt;summary&gt;
 /// This class extracts information that NLog stores so that we can report on it
 /// &lt;/summary&gt;
 public class NLogRepository : ILogReportingRepository
 {
 MvcLoggingDemoContainer _context = null;

 /// &lt;summary&gt;
 /// Default Constructor uses the default Entity Container
 /// &lt;/summary&gt;
 public NLogRepository()
 {
 _context = new MvcLoggingDemoContainer();
 }

 /// &lt;summary&gt;
 /// Overloaded constructor that can take an EntityContainer as a parameter so that it can be mocked out by our tests
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;context&quot;&gt;The Entity context&lt;/param&gt;
 public NLogRepository(MvcLoggingDemoContainer context)
 {
 _context = context;
 }

 /// &lt;summary&gt;
 /// Gets a filtered list of log events
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;pageIndex&quot;&gt;0 based page index&lt;/param&gt;
 /// &lt;param name=&quot;pageSize&quot;&gt;max number of records to return&lt;/param&gt;
 /// &lt;param name=&quot;start&quot;&gt;start date&lt;/param&gt;
 /// &lt;param name=&quot;end&quot;&gt;end date&lt;/param&gt;
 /// &lt;param name=&quot;logLevel&quot;&gt;The level of the log messages&lt;/param&gt;
 /// &lt;returns&gt;A filtered list of log events&lt;/returns&gt;
 public IQueryable&lt;LogEvent&gt; GetByDateRangeAndType(int pageIndex, int pageSize, DateTime start, DateTime end, string logLevel)
 {
 IQueryable&lt;LogEvent&gt; list = (from b in _context.NLog_Error
 where b.time_stamp &gt;= start &amp;&amp; b.time_stamp &lt;= end
 &amp;&amp; (b.level == logLevel || logLevel == &quot;All&quot;)
 select new LogEvent { IdType = &quot;number&quot;
 , Id = &quot;&quot;
 , IdAsInteger = b.Id
 , IdAsGuid = Guid.NewGuid()
 , LoggerProviderName = &quot;NLog&quot;
 , LogDate = b.time_stamp
 , MachineName = b.host
 , Message = b.message
 , Type = b.type
 , Level = b.level
 , Source = b.source
 , StackTrace = b.stacktrace });

 return list;
 }

 /// &lt;summary&gt;
 /// Returns a single Log event
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;id&quot;&gt;Id of the log event as a string&lt;/param&gt;
 /// &lt;returns&gt;A single Log event&lt;/returns&gt;
 public LogEvent GetById(string id)
 {
 int logEventId = Convert.ToInt32(id);

 LogEvent logEvent = (from b in _context.NLog_Error
 where b.Id == logEventId
 select new LogEvent { IdType = &quot;number&quot;
 , IdAsInteger = b.Id
 , LoggerProviderName = &quot;NLog&quot;
 , LogDate = b.time_stamp
 , MachineName = b.host
 , Message = b.message
 , Type = b.type
 , Level = b.level
 , Source = b.source
 , StackTrace = b.stacktrace
 , AllXml = b.allxml })
 .SingleOrDefault();

 return logEvent;

 }

 /// &lt;summary&gt;
 /// Clears log messages between a date range and for specified log levels
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;start&quot;&gt;start date&lt;/param&gt;
 /// &lt;param name=&quot;end&quot;&gt;end date&lt;/param&gt;
 /// &lt;param name=&quot;logLevels&quot;&gt;string array of log levels&lt;/param&gt;
 public void ClearLog(DateTime start, DateTime end, string[] logLevels)
 {
 string logLevelList = &quot;&quot;;
 foreach (string logLevel in logLevels)
 {
 logLevelList += &quot;,'&quot; + logLevel + &quot;'&quot;;
 }
 if (logLevelList.Length &gt; 0)
 {
 logLevelList = logLevelList.Substring(1);
 }

 string commandText = &quot;delete from NLog_Error WHERE time_stamp &gt;= @p0 and time_stamp &lt;= @p1 and level in (@p2)&quot;;

 SqlParameter paramStartDate = new SqlParameter { ParameterName = &quot;p0&quot;, Value = start.ToUniversalTime(), DbType = System.Data.DbType.DateTime };
 SqlParameter paramEndDate = new SqlParameter { ParameterName = &quot;p1&quot;, Value = end.ToUniversalTime(), DbType = System.Data.DbType.DateTime };
 SqlParameter paramLogLevelList = new SqlParameter { ParameterName = &quot;p2&quot;, Value = logLevelList };

 _context.ExecuteStoreCommand(commandText, paramStartDate, paramEndDate, paramLogLevelList);
 }

 }
}
 </pre></p>
<p>9. In the Repository directory, create a new class file and name it,  &#8220;Log4NetRepository&#8221;<br />
10. Replace the contents of the newly created file with the code below :</p>
<p><pre class="brush: csharp;">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using MvcLoggingDemo.Models.Entities;
using MvcLoggingDemo.Services.Paging;
using MvcLoggingDemo.Helpers;
using System.Data.SqlClient;

namespace MvcLoggingDemo.Models.Repository
{
 /// &lt;summary&gt;
 /// This class extracts information that Log4Net stores so that we can report on it
 /// &lt;/summary&gt;
 public class Log4NetRepository : ILogReportingRepository
 {
 MvcLoggingDemoContainer _context = null;

 /// &lt;summary&gt;
 /// Default Constructor uses the default Entity Container
 /// &lt;/summary&gt;
 public Log4NetRepository()
 {
 _context = new MvcLoggingDemoContainer();
 }

 /// &lt;summary&gt;
 /// Overloaded constructor that can take an EntityContainer as a parameter so that it can be mocked out by our tests
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;context&quot;&gt;The Entity context&lt;/param&gt;
 public Log4NetRepository(MvcLoggingDemoContainer context)
 {
 _context = context;
 }

 /// &lt;summary&gt;
 /// Gets a filtered list of log events
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;pageIndex&quot;&gt;0 based page index&lt;/param&gt;
 /// &lt;param name=&quot;pageSize&quot;&gt;max number of records to return&lt;/param&gt;
 /// &lt;param name=&quot;start&quot;&gt;start date&lt;/param&gt;
 /// &lt;param name=&quot;end&quot;&gt;end date&lt;/param&gt;
 /// &lt;param name=&quot;logLevel&quot;&gt;The level of the log messages&lt;/param&gt;
 /// &lt;returns&gt;A filtered list of log events&lt;/returns&gt;
 public IQueryable&lt;LogEvent&gt; GetByDateRangeAndType(int pageIndex, int pageSize, DateTime start, DateTime end, string logLevel)
 {
 IQueryable&lt;LogEvent&gt; list = (from b in _context.Log4Net_Error
 where b.Date &gt;= start &amp;&amp; b.Date &lt;= end
 &amp;&amp; (b.Level == logLevel || logLevel == &quot;All&quot;)
 select new LogEvent { IdType = &quot;number&quot;
 , Id = &quot;&quot;
 , IdAsInteger = b.Id
 , IdAsGuid = Guid.NewGuid()
 , LoggerProviderName = &quot;Log4Net&quot;
 , LogDate = b.Date
 , MachineName = b.Thread
 , Message = b.Message
 , Type = &quot;&quot;
 , Level = b.Level
 , Source = b.Thread
 , StackTrace = &quot;&quot; });

 return list;
 }

 /// &lt;summary&gt;
 /// Returns a single Log event
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;id&quot;&gt;Id of the log event as a string&lt;/param&gt;
 /// &lt;returns&gt;A single Log event&lt;/returns&gt;
 public LogEvent GetById(string id)
 {
 int logEventId = Convert.ToInt32(id);

 LogEvent logEvent = (from b in _context.Log4Net_Error
 where b.Id == logEventId
 select new LogEvent { IdType = &quot;number&quot;
 , IdAsInteger = b.Id
 , LoggerProviderName = &quot;Log4Net&quot;
 , LogDate = b.Date
 , MachineName = b.Thread
 , Message = b.Message
 , Type = &quot;&quot;
 , Level = b.Level
 , Source = b.Thread
 , StackTrace = &quot;&quot;
 , AllXml = &quot;&quot; })
 .SingleOrDefault();

 return logEvent;
 }

 /// &lt;summary&gt;
 /// Clears log messages between a date range and for specified log levels
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;start&quot;&gt;start date&lt;/param&gt;
 /// &lt;param name=&quot;end&quot;&gt;end date&lt;/param&gt;
 /// &lt;param name=&quot;logLevels&quot;&gt;string array of log levels&lt;/param&gt;
 public void ClearLog(DateTime start, DateTime end, string[] logLevels)
 {
 string logLevelList = &quot;&quot;;
 foreach (string logLevel in logLevels)
 {
 logLevelList += &quot;,'&quot; + logLevel + &quot;'&quot;;
 }
 if (logLevelList.Length &gt; 0)
 {
 logLevelList = logLevelList.Substring(1);
 }

 string commandText = &quot;delete from Log4Net_Error WHERE [Date] &gt;= @p0 and [Date] &lt;= @p1 and Level in (@p2)&quot;;

 SqlParameter paramStartDate = new SqlParameter { ParameterName = &quot;p0&quot;, Value = start.ToUniversalTime(), DbType = System.Data.DbType.DateTime };
 SqlParameter paramEndDate = new SqlParameter { ParameterName = &quot;p1&quot;, Value = end.ToUniversalTime(), DbType = System.Data.DbType.DateTime };
 SqlParameter paramLogLevelList = new SqlParameter { ParameterName = &quot;p2&quot;, Value = logLevelList };

 _context.ExecuteStoreCommand(commandText, paramStartDate, paramEndDate, paramLogLevelList);
 }

 }
}
 </pre></p>
<p>11. In the Repository directory, create a new class file and name it,  &#8220;HealthMonitoringRepository&#8221;<br />
12. Replace the contents of the newly created file with the code below :</p>
<p><pre class="brush: csharp;">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using MvcLoggingDemo.Models.Entities;
using MvcLoggingDemo.Services.Paging;
using MvcLoggingDemo.Helpers;
using System.Data.SqlClient;

namespace MvcLoggingDemo.Models.Repository
{
 /// &lt;summary&gt;
 /// This class extracts information that ASP.NET Health Monitoring stores so that we can report on it
 /// &lt;/summary&gt;
 public class HealthMonitoringRepository : ILogReportingRepository
 {
 MvcLoggingDemoContainer _context = null;

 /// &lt;summary&gt;
 /// Default Constructor uses the default Entity Container
 /// &lt;/summary&gt;
 public HealthMonitoringRepository()
 {
 _context = new MvcLoggingDemoContainer();
 }

 /// &lt;summary&gt;
 /// Overloaded constructor that can take an EntityContainer as a parameter so that it can be mocked out by our tests
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;context&quot;&gt;The Entity context&lt;/param&gt;
 public HealthMonitoringRepository(MvcLoggingDemoContainer context)
 {
 _context = context;
 }

 /// &lt;summary&gt;
 /// Gets a filtered list of log events
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;pageIndex&quot;&gt;0 based page index&lt;/param&gt;
 /// &lt;param name=&quot;pageSize&quot;&gt;max number of records to return&lt;/param&gt;
 /// &lt;param name=&quot;start&quot;&gt;start date&lt;/param&gt;
 /// &lt;param name=&quot;end&quot;&gt;end date&lt;/param&gt;
 /// &lt;param name=&quot;logLevel&quot;&gt;The level of the log messages&lt;/param&gt;
 /// &lt;returns&gt;A filtered list of log events&lt;/returns&gt;
 public IQueryable&lt;LogEvent&gt; GetByDateRangeAndType(int pageIndex, int pageSize, DateTime start, DateTime end, string logLevel)
 {
 IQueryable&lt;LogEvent&gt; list = (from h in _context.vw_aspnet_WebEvents_extended
 where h.EventTimeUtc &gt;= start &amp;&amp; h.EventTimeUtc &lt;= end
 &amp;&amp; (h.Level == logLevel || logLevel == &quot;All&quot;)
 select new LogEvent { IdType = &quot;string&quot;
 , Id = h.EventId
 , IdAsInteger = 0
 , IdAsGuid = Guid.NewGuid()
 , LoggerProviderName = &quot;Health Monitoring&quot;
 , LogDate = h.EventTimeUtc
 , MachineName = h.MachineName
 , Message = h.Message
 , Type = h.EventType
 , Level = h.Level
 , Source = h.RequestUrl
 , StackTrace = &quot;&quot; });

 return list;
 }

 /// &lt;summary&gt;
 /// Returns a single Log event
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;id&quot;&gt;Id of the log event as a string&lt;/param&gt;
 /// &lt;returns&gt;A single Log event&lt;/returns&gt;
 public LogEvent GetById(string id)
 {
 LogEvent logEvent = logEvent = (from b in _context.vw_aspnet_WebEvents_extended
 where b.EventId == id
 select new LogEvent { IdType = &quot;string&quot;
 , Id = b.EventId
 , LoggerProviderName = &quot;Health Monitoring&quot;
 , LogDate = b.EventTimeUtc
 , MachineName = b.MachineName
 , Message = b.Message
 , Type = b.EventType
 , Level = b.Level
 , Source = b.RequestUrl
 , StackTrace = &quot;&quot;
 , AllXml = &quot;&quot; })
 .SingleOrDefault();

 return logEvent;

 }

 /// &lt;summary&gt;
 /// Clears log messages between a date range and for specified log levels
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;start&quot;&gt;start date&lt;/param&gt;
 /// &lt;param name=&quot;end&quot;&gt;end date&lt;/param&gt;
 /// &lt;param name=&quot;logLevels&quot;&gt;string array of log levels&lt;/param&gt;
 public void ClearLog(DateTime start, DateTime end, string[] logLevels)
 {
 string logLevelList = &quot;&quot;;
 foreach (string logLevel in logLevels)
 {
 logLevelList += &quot;,'&quot; + logLevel + &quot;'&quot;;
 }
 if (logLevelList.Length &gt; 0)
 {
 logLevelList = logLevelList.Substring(1);
 }

 string commandText = &quot;&quot;;
 commandText += &quot;DELETE &quot;;
 commandText += &quot;FROM &quot;;
 commandText += &quot;    aspnet_WebEvent_Events &quot;;
 commandText += &quot;WHERE&quot;;
 commandText += &quot;    EventId IN    &quot;;
 commandText += &quot;(SELECT EventId    &quot;;
 commandText += &quot; FROM vw_aspnet_WebEvents_extended &quot;;
 commandText += &quot; WHERE &quot;;
 commandText += &quot;    [EventTimeUtc] &gt;= @p0&quot;;
 commandText += &quot; AND [EventTimeUtc] &lt;= @p1&quot;;
 commandText += &quot; AND [Level] IN (@p2)&quot;; // eg:  AND [Level] IN ('Info','Debug')
 commandText += &quot; )&quot;;

 SqlParameter paramStartDate = new SqlParameter { ParameterName = &quot;p0&quot;, Value = start.ToUniversalTime(), DbType = System.Data.DbType.DateTime };
 SqlParameter paramEndDate = new SqlParameter { ParameterName = &quot;p1&quot;, Value = end.ToUniversalTime(), DbType = System.Data.DbType.DateTime };
 SqlParameter paramLogLevelList = new SqlParameter { ParameterName = &quot;p2&quot;, Value = logLevelList };

 _context.ExecuteStoreCommand(commandText, paramStartDate, paramEndDate, paramLogLevelList);
 }

 }
}
 </pre></p>
<p>At this point we have 4 stand alone repository classes that can each retrieve information from their own data store (table).</p>
<p>To build our log reporting tool we will need to create a new class  that will be able to pull data out of one or all of the repositories  that we have just created.</p>
<p>13. In our &#8220;Interfaces&#8221; folder, create a new file and name it, &#8220;ILogReportingFacade.cs&#8221;</p>
<p>14. Replace the contents of the newly created file with the code  below :</p>
<p><pre class="brush: csharp;">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using MvcLoggingDemo.Services.Paging;

namespace MvcLoggingDemo.Models.Repository
{
 /// &lt;summary&gt;
 /// This interface provides a facade over all of our LogReport repositories
 /// &lt;/summary&gt;
 public interface ILogReportingFacade
 {
 /// &lt;summary&gt;
 /// Gets a filtered list of log events
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;pageIndex&quot;&gt;0 based page index&lt;/param&gt;
 /// &lt;param name=&quot;pageSize&quot;&gt;max number of records to return&lt;/param&gt;
 /// &lt;param name=&quot;start&quot;&gt;start date&lt;/param&gt;
 /// &lt;param name=&quot;end&quot;&gt;end date&lt;/param&gt;
 /// &lt;param name=&quot;logProviderName&quot;&gt;If empty all log providers used, otherwise it will be filtered by the specified log provider&lt;/param&gt;
 /// &lt;param name=&quot;logLevel&quot;&gt;The level of the log messages&lt;/param&gt;
 /// &lt;returns&gt;A filtered list of log events&lt;/returns&gt;
 IPagedList&lt;LogEvent&gt; GetByDateRangeAndType(int pageIndex, int pageSize, DateTime start, DateTime end, string logProviderName, string logLevel);

 /// &lt;summary&gt;
 /// Returns a single Log event
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;logProviderName&quot;&gt;Name of the log provider&lt;/param&gt;
 /// &lt;param name=&quot;id&quot;&gt;Id of the log event as a string&lt;/param&gt;
 /// &lt;returns&gt;A single Log event&lt;/returns&gt;
 LogEvent GetById(string logProviderName, string id);

 /// &lt;summary&gt;
 /// Clears log messages for a given date range and log level
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;logProviderName&quot;&gt;Name of the log provider&lt;/param&gt;
 /// &lt;param name=&quot;start&quot;&gt;start date&lt;/param&gt;
 /// &lt;param name=&quot;end&quot;&gt;end date&lt;/param&gt;
 /// &lt;param name=&quot;logLevels&quot;&gt;The level of the log messages&lt;/param&gt;
 void ClearLog(string logProviderName, DateTime start, DateTime end, string[] logLevels);

 /// &lt;summary&gt;
 /// Get's a list of all log providers registered in the web.config file
 /// &lt;/summary&gt;
 /// &lt;returns&gt;A list of all log providers registered&lt;/returns&gt;
 Dictionary&lt;string, string&gt; GetLogProviders();
 }
}
 </pre></p>
<p>15. In the &#8220;Repository&#8221; folder, create a new file and name it, &#8220;LogReportingFacade.cs&#8221;</p>
<p>16. Replace the contents of the newly created file with the code  below :</p>
<p><pre class="brush: csharp;">
using System;
using System.Configuration;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Web;

using MvcLoggingDemo.Models.Entities;
using MvcLoggingDemo.Services.Logging;
using MvcLoggingDemo.Services.Paging;
using MvcLoggingDemo.Helpers;

namespace MvcLoggingDemo.Models.Repository
{
 /// &lt;summary&gt;
 /// This class provides a facade over all of our LogReport repositories
 /// &lt;/summary&gt;
 public class LogReportingFacade : ILogReportingFacade
 {
 MvcLoggingDemoContainer _context = new MvcLoggingDemoContainer();

 private Dictionary&lt;string, string&gt; logProviders = null;

 /// &lt;summary&gt;
 /// Default constructor
 /// &lt;/summary&gt;
 public LogReportingFacade()
 {
 Init();
 }

 /// &lt;summary&gt;
 /// Overloaded constructor that can take an EntityContainer as a parameter so that it can be mocked out by our tests
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;context&quot;&gt;The Entity context&lt;/param&gt;
 public LogReportingFacade(MvcLoggingDemoContainer context)
 {
 _context = context;

 Init();
 }

 /// &lt;summary&gt;
 ///
 /// &lt;/summary&gt;
 private void Init()
 {
 logProviders = new Dictionary&lt;string, string&gt;();

 // Call ConfigurationManager to read the custom logConfiguration
 // of the web.config file and put its contents into an
 // instance of the custom class created for it.
 LogConfigurationSection configSection = ConfigurationManager.GetSection(&quot;logConfiguration&quot;) as LogConfigurationSection;

 if (configSection == null)
 throw new ApplicationException(&quot;Failed to load the Log Configuration section.&quot;);
 else
 {
 for (int i = 0; i &lt; configSection.LogProviders.Count; i++)
 {
 logProviders.Add(configSection.LogProviders[i].Name, configSection.LogProviders[i].Type);
 }
 }
 }

 /// &lt;summary&gt;
 /// Creates and returns an instance of a log provider
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;logProviderName&quot;&gt;The type name of the log provider&lt;/param&gt;
 /// &lt;returns&gt;An instance of a log provider&lt;/returns&gt;
 private ILogReportingRepository GetProvider(string logProviderName)
 {
 string logSourceType = logProviders[logProviderName];

 Type providerType = Type.GetType(logSourceType);

 ILogReportingRepository provider = Activator.CreateInstance(providerType, _context) as ILogReportingRepository;

 return provider;
 }

 /// &lt;summary&gt;
 /// Gets a filtered list of log events
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;pageIndex&quot;&gt;0 based page index&lt;/param&gt;
 /// &lt;param name=&quot;pageSize&quot;&gt;max number of records to return&lt;/param&gt;
 /// &lt;param name=&quot;start&quot;&gt;start date&lt;/param&gt;
 /// &lt;param name=&quot;end&quot;&gt;end date&lt;/param&gt;
 /// &lt;param name=&quot;logProviderName&quot;&gt;name of the log provider&lt;/param&gt;
 /// &lt;param name=&quot;logLevel&quot;&gt;The level of the log messages&lt;/param&gt;
 /// &lt;returns&gt;A filtered list of log events&lt;/returns&gt;
 public IPagedList&lt;LogEvent&gt; GetByDateRangeAndType(int pageIndex, int pageSize, DateTime start, DateTime end, string logProviderName, string logLevel)
 {
 IQueryable&lt;LogEvent&gt; list = null;

 switch (logProviderName)
 {
 case &quot;All&quot;:
 foreach (string providerName in logProviders.Values)
 {
 IQueryable&lt;LogEvent&gt; logList = GetProvider(providerName).GetByDateRangeAndType(pageIndex, pageSize, start, end, logLevel);
 list = (list == null) ? logList : list.Union(logList);
 }
 break;

 default:
 list = GetProvider(logProviderName).GetByDateRangeAndType(pageIndex, pageSize, start, end, logLevel);
 break;
 }

 list = list.OrderByDescending(d =&gt; d.LogDate);

 return new PagedList&lt;LogEvent&gt;(list, pageIndex, pageSize);
 }

 /// &lt;summary&gt;
 /// Returns a single Log event
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;logProviderName&quot;&gt;name of the log provider&lt;/param&gt;
 /// &lt;param name=&quot;id&quot;&gt;Id of the log event as a string&lt;/param&gt;
 /// &lt;returns&gt;A single Log event&lt;/returns&gt;
 public LogEvent GetById(string logProviderName, string id)
 {
 LogEvent logEvent = GetProvider(logProviderName).GetById(id);
 return logEvent;
 }

 /// &lt;summary&gt;
 /// Clears log messages between a date range and for specified log levels
 /// &lt;/summary&gt;
 /// &lt;param name=&quot;logProviderName&quot;&gt;name of the log provider&lt;/param&gt;
 /// &lt;param name=&quot;start&quot;&gt;start date&lt;/param&gt;
 /// &lt;param name=&quot;end&quot;&gt;end date&lt;/param&gt;
 /// &lt;param name=&quot;logLevels&quot;&gt;string array of log levels&lt;/param&gt;
 public void ClearLog(string logProviderName, DateTime start, DateTime end, string[] logLevels)
 {
 GetProvider(logProviderName).ClearLog(start, end, logLevels);
 }

 /// &lt;summary&gt;
 /// Returns a list of all registered log providers
 /// &lt;/summary&gt;
 /// &lt;returns&gt;A list of all registered log providers&lt;/returns&gt;
 public Dictionary&lt;string, string&gt; GetLogProviders()
 {
 return logProviders;
 }

 }
}
 </pre></p>
<p>The class file above makes use of a custom configuration class. The source code for it can be found in the accompanying download at the end of this article.</p>
<p>The interesting part is in the GetByDateRangeAndType method where we determine if we need to return the results from all of our registered log providers or whether we return the results from a single log provider. Each implemented Log Reporting provider returns an IQueryable interface so we can use the power of LINQ to just loop through each one and do a UNION on them.</p>
<p>After we have consolidated all of the results we order them and page the results. All of this happens server side and then the results for each page are returned back to the calling client. Although at this stage we haven&#8217;t built our controller and views yet. That will be done in the next article.</p>
<h4>Conclusion</h4>
<p>In this article we have created the model and the database access  layer for our log reporting tool. We have seen how we can query multiple  data sources using Linq to Entities UNION keyword.</p>
<p>In the next article we will build our controller and view to display the logs from our various data sources.</p>
<h4>Download</h4>
<p>The sourcecode for part 1 is on the Downloads tab of the <a href="http://mvclogging.codeplex.com/" target="_blank">associated Codeplex website</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/dotnetdarren.wordpress.com/74/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/dotnetdarren.wordpress.com/74/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/dotnetdarren.wordpress.com/74/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/dotnetdarren.wordpress.com/74/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/dotnetdarren.wordpress.com/74/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/dotnetdarren.wordpress.com/74/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/dotnetdarren.wordpress.com/74/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/dotnetdarren.wordpress.com/74/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/dotnetdarren.wordpress.com/74/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/dotnetdarren.wordpress.com/74/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/dotnetdarren.wordpress.com/74/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/dotnetdarren.wordpress.com/74/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/dotnetdarren.wordpress.com/74/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/dotnetdarren.wordpress.com/74/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=dotnetdarren.wordpress.com&amp;blog=14830143&amp;post=74&amp;subd=dotnetdarren&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://dotnetdarren.wordpress.com/2010/07/29/logging-in-mvc-part-5-the-model-and-data-layer/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/c6764e85b5f6b2e2e71cb35e39c28fc0?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">darrenw74</media:title>
		</media:content>

		<media:content url="http://dotnetdarren.files.wordpress.com/2010/07/log_entitymodel_select_logging_tables.png?w=300" medium="image">
			<media:title type="html">Logging - Entity Model</media:title>
		</media:content>

		<media:content url="http://dotnetdarren.files.wordpress.com/2010/07/log_entitymodel_diagram_logging_tables.png?w=300" medium="image">
			<media:title type="html">Logging - Entity Diagram - Tables</media:title>
		</media:content>
	</item>
		<item>
		<title>Logging in MVC Part 4 &#8211; Log4Net</title>
		<link>http://dotnetdarren.wordpress.com/2010/07/29/logging-in-mvc-part-4-log4net/</link>
		<comments>http://dotnetdarren.wordpress.com/2010/07/29/logging-in-mvc-part-4-log4net/#comments</comments>
		<pubDate>Wed, 28 Jul 2010 23:10:19 +0000</pubDate>
		<dc:creator>darrenw74</dc:creator>
				<category><![CDATA[ASP.NET MVC]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[elmah]]></category>
		<category><![CDATA[health monitoring]]></category>
		<category><![CDATA[log4net]]></category>
		<category><![CDATA[logging]]></category>

		<guid isPermaLink="false">http://dotnetdarren.wordpress.com/?p=63</guid>
		<description><![CDATA[This is part 4 of the MVC Logging series. Other articles in the series are: MVC Logging Part 1 &#8211; Elmah MVC Logging Part 2 &#8211; Health Monitoring MVC Logging Part 3 &#8211; NLog MVC Logging Part 4 &#8211; Log4Net MVC Logging Part 5 &#8211; Model and Data Layer Introduction In this article we will [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=dotnetdarren.wordpress.com&amp;blog=14830143&amp;post=63&amp;subd=dotnetdarren&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<blockquote><p>This is part 4 of the MVC Logging series. Other articles in the series are:</p>
<ul>
<li><a href="http://dotnetdarren.wordpress.com/logging-on-mvc-part-1" target="_self">MVC Logging Part 1 &#8211; Elmah</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-2-health-monitoring" target="_self">MVC Logging Part 2 &#8211; Health Monitoring</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-3-–-nlog" target="_self">MVC Logging Part 3 &#8211; NLog</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-4-log4net" target="_self">MVC Logging Part 4 &#8211; Log4Net</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-5-the-model-and-data-layer" target="_self">MVC Logging Part 5 &#8211; Model and Data Layer</a></li>
</ul>
</blockquote>
<h4>Introduction</h4>
<p>In this article we will quickly add Log4Net into the website so that  later on we can decide whether to use Log4Net or NLog to log our custom  messages.</p>
<p>Log4Net is a very widely used logger and it is quite likely that you  have 3rd party dependencies in your website that already use Log4Net so  it can be a good idea to track any messages that it logs.</p>
<h4>Log4Net</h4>
<p>The steps we need to follow are:</p>
<p>1. Download Log4Net<br />
2. Add a reference to Log4Net<br />
3. Add a table in our database to store the Log4Net logs<br />
4. Modify the web.config file for Log4Net<br />
5. Implement a Log4NetLogger that implements our ILogger interface.</p>
<h4>Setting up the database</h4>
<p>Run the following script to create the table that Log4Net will log message to:</p>
<p><pre class="brush: sql;">
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[Log4Net_Error](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[Date] [datetime] NOT NULL,
	[Thread] [varchar](255) NOT NULL,
	[Level] [varchar](50) NOT NULL,
	[Logger] [varchar](255) NOT NULL,
	[Message] [varchar](4000) NOT NULL,
	[Exception] [varchar](2000) NULL
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO
</pre></p>
<h4>Web.config configuration</h4>
<p>Add the following to the top of your web.config file:</p>
<p><pre class="brush: xml;">
&lt;configuration&gt;
  &lt;configSections&gt;
   ...
  &lt;section name=&quot;log4net&quot; type=&quot;log4net.Config.Log4NetConfigurationSectionHandler, log4net&quot; /&gt;
  ...
  &lt;/configSections&gt;
&lt;/configuration&gt;
</pre></p>
<p>Add the following underneath the configuration element in your web.config file:</p>
<p><pre class="brush: xml;">
&lt;log4net&gt;
    &lt;appender name=&quot;AdoNetAppender&quot; type=&quot;log4net.Appender.AdoNetAppender&quot;&gt;
      &lt;bufferSize value=&quot;100&quot; /&gt;
      &lt;connectionType value=&quot;System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089&quot; /&gt;
      &lt;connectionString value=&quot;data source=[Machine]/[Instance];Initial Catalog=[DatabaseName];Integrated Security=True&quot; /&gt;
      &lt;commandText value=&quot;INSERT INTO Log4Net_Error ([Date],[Thread],[Level],[Logger],[Message],[Exception]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception)&quot; /&gt;
      &lt;parameter&gt;
        &lt;parameterName value=&quot;@log_date&quot; /&gt;
        &lt;dbType value=&quot;DateTime&quot; /&gt;
        &lt;layout type=&quot;log4net.Layout.RawTimeStampLayout&quot; /&gt;
      &lt;/parameter&gt;
      &lt;parameter&gt;
        &lt;parameterName value=&quot;@thread&quot; /&gt;
        &lt;dbType value=&quot;String&quot; /&gt;
        &lt;size value=&quot;255&quot; /&gt;
        &lt;layout type=&quot;log4net.Layout.PatternLayout&quot;&gt;
          &lt;conversionPattern value=&quot;%thread&quot; /&gt;
        &lt;/layout&gt;
      &lt;/parameter&gt;
      &lt;parameter&gt;
        &lt;parameterName value=&quot;@log_level&quot; /&gt;
        &lt;dbType value=&quot;String&quot; /&gt;
        &lt;size value=&quot;50&quot; /&gt;
        &lt;layout type=&quot;log4net.Layout.PatternLayout&quot;&gt;
          &lt;conversionPattern value=&quot;%level&quot; /&gt;
        &lt;/layout&gt;
      &lt;/parameter&gt;
      &lt;parameter&gt;
        &lt;parameterName value=&quot;@logger&quot; /&gt;
        &lt;dbType value=&quot;String&quot; /&gt;
        &lt;size value=&quot;255&quot; /&gt;
        &lt;layout type=&quot;log4net.Layout.PatternLayout&quot;&gt;
          &lt;conversionPattern value=&quot;%logger&quot; /&gt;
        &lt;/layout&gt;
      &lt;/parameter&gt;
      &lt;parameter&gt;
        &lt;parameterName value=&quot;@message&quot; /&gt;
        &lt;dbType value=&quot;String&quot; /&gt;
        &lt;size value=&quot;4000&quot; /&gt;
        &lt;layout type=&quot;log4net.Layout.PatternLayout&quot;&gt;
          &lt;conversionPattern value=&quot;%message&quot; /&gt;
        &lt;/layout&gt;
      &lt;/parameter&gt;
      &lt;parameter&gt;
        &lt;parameterName value=&quot;@exception&quot; /&gt;
        &lt;dbType value=&quot;String&quot; /&gt;
        &lt;size value=&quot;2000&quot; /&gt;
        &lt;layout type=&quot;log4net.Layout.ExceptionLayout&quot; /&gt;
      &lt;/parameter&gt;
    &lt;/appender&gt;

    &lt;!-- Set root logger level to DEBUG and its only appender to A1 --&gt;
    &lt;root&gt;
      &lt;level value=&quot;DEBUG&quot; /&gt;
      &lt;appender-ref ref=&quot;AdoNetAppender&quot; /&gt;
    &lt;/root&gt;
  &lt;/log4net&gt;
</pre></p>
<h4>Implement a Log4NetLogger</h4>
<p>Now let&#8217;s create a logger class for Log4Net that implements our ILogger interface (discussed in part 3).1. Create a new folder underneath the Services folder. Our folder structure will be like this:<br />
Services -&gt; Logging -&gt; Log4Net</p>
<p>2. Create a new class file in the new folder and name it &#8216;Log4NetLogger.cs&#8217;.</p>
<p>3. Add the following code to the class:</p>
<p><pre class="brush: csharp;">

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using log4net;

namespace MvcLoggingDemo.Services.Logging.Log4Net
{
 public class Log4NetLogger : ILogger
 {

 private ILog _logger;

 public Log4NetLogger()
 {
 _logger = LogManager.GetLogger(this.GetType());
 }

 public void Info(string message)
 {
 _logger.Info(message);
 }

 public void Warn(string message)
 {
 _logger.Warn(message);
 }

 public void Debug(string message)
 {
 _logger.Debug(message);
 }

 public void Error(string message)
 {
 _logger.Error(message);
 }

 public void Error(Exception x)
 {
 Error(LogUtility.BuildExceptionMessage(x));
 }

 public void Error(string message, Exception x)
 {
 _logger.Error(message, x);
 }

 public void Fatal(string message)
 {
 _logger.Fatal(message);
 }

 public void Fatal(Exception x)
 {
 Fatal(LogUtility.BuildExceptionMessage(x));
 }
 }
}

</pre></p>
<p>You will notice that the code above is almost identical to the one we  created for NLog. The only difference is the line in the constructor  that instantiates the Log4Net logger.</p>
<h4>Testing the Log4Net logger</h4>
<p>The last step is to write some code that uses our new debugger. Update the Index method of our Activity controller like this:<br />
(You will also need to add a refernce to the namespace, &#8220;MvcLoggingDemo.Services.Logging.Log4Net&#8221; at the top of your file)</p>
<p><pre class="brush: csharp;">
public ActionResult Index()
 {
 IEnumerable list = activityRepository.GetAll();

 NLogLogger logger = new NLogLogger();
 logger.Info(&quot;Test message for NLog&quot;);

 Log4NetLogger logger2 = new Log4NetLogger();
 logger2.Info(&quot;Test message for Log4Net&quot;);

 try
 {
 throw new Exception(&quot;A test exception&quot;);
 }
 catch(Exception ex)
 {
 Console.WriteLine(&quot;ERROR - An error has occurred&quot;);
 }

 return View(list);
 }
</pre></p>
<p>Now go to the Index page for Activities and inspect the Log4Net_Error table to ensure that the message has been logged.</p>
<h4>Conclusion</h4>
<p>We now have Elmah, NLog, Log4Net and Health monitoring setup and  working on our website. The next few articles will focus on building a  log reporting viewer so that we can tie all of these things together so  that we can see a consolidated and consistent view of everything that is  getting logged on our website.</p>
<h4>Download</h4>
<p>The source code for part 4 is on the Downloads tab of the <a href="http://mvclogging.codeplex.com/releases/view/49757" target="_blank">associate CodePlex website</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/dotnetdarren.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/dotnetdarren.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/dotnetdarren.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/dotnetdarren.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/dotnetdarren.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/dotnetdarren.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/dotnetdarren.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/dotnetdarren.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/dotnetdarren.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/dotnetdarren.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/dotnetdarren.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/dotnetdarren.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/dotnetdarren.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/dotnetdarren.wordpress.com/63/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=dotnetdarren.wordpress.com&amp;blog=14830143&amp;post=63&amp;subd=dotnetdarren&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://dotnetdarren.wordpress.com/2010/07/29/logging-in-mvc-part-4-log4net/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/c6764e85b5f6b2e2e71cb35e39c28fc0?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">darrenw74</media:title>
		</media:content>
	</item>
		<item>
		<title>Logging in MVC Part 3 – NLog</title>
		<link>http://dotnetdarren.wordpress.com/2010/07/28/logging-in-mvc-part-3-%e2%80%93-nlog/</link>
		<comments>http://dotnetdarren.wordpress.com/2010/07/28/logging-in-mvc-part-3-%e2%80%93-nlog/#comments</comments>
		<pubDate>Wed, 28 Jul 2010 03:50:56 +0000</pubDate>
		<dc:creator>darrenw74</dc:creator>
				<category><![CDATA[ASP.NET MVC]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[elmah]]></category>
		<category><![CDATA[health monitoring]]></category>
		<category><![CDATA[log4net]]></category>
		<category><![CDATA[logging]]></category>

		<guid isPermaLink="false">http://dotnetdarren.wordpress.com/?p=57</guid>
		<description><![CDATA[This is part 3 of the MVC Logging series. Other articles in the series are: MVC Logging Part 1 &#8211; Elmah MVC Logging Part 2 &#8211; Health Monitoring MVC Logging Part 3 &#8211; NLog MVC Logging Part 4 &#8211; Log4Net MVC Logging Part 5 &#8211; Model and Data Layer Introduction In the previous article we [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=dotnetdarren.wordpress.com&amp;blog=14830143&amp;post=57&amp;subd=dotnetdarren&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<blockquote><p>This is part 3 of the MVC Logging series. Other articles in the series are:</p>
<ul>
<li><a href="http://dotnetdarren.wordpress.com/logging-on-mvc-part-1" target="_self">MVC Logging Part 1 &#8211; Elmah</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-2-health-monitoring" target="_self">MVC Logging Part 2 &#8211; Health Monitoring</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-3-–-nlog" target="_self">MVC Logging Part 3 &#8211; NLog</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-4-log4net" target="_self">MVC Logging Part 4 &#8211; Log4Net</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-5-the-model-and-data-layer" target="_self">MVC Logging Part 5 &#8211; Model and Data Layer</a></li>
</ul>
</blockquote>
<h4>Introduction</h4>
<p>In the previous article we implemented Health Monitoring into our MVC  website.</p>
<p>Now we need to provide a way to log our own custom messages so that  we  can keep track of important events that occur throughout our  website.</p>
<p>For example, you may need to log information before and after a financial transaction has occurred on your eCommerce website.</p>
<p>It is easy to write your own logger but as there are already many  established logging frameworks already available for .NET I&#8217;ve decided  to concentrate on 2 of the most popular ones: Log4Net and NLog.</p>
<p>In this article we will concentrate on setting up NLog and in the next article we will take a look at Log4Net.</p>
<h4>NLog</h4>
<p>Prior to working on this series I had never ever used <a href="http://nlog-project.org/" target="_blank">NLog </a>- I&#8217;ve always used Log4Net. However, after spending some time on <a href="http://blog.wekeroad.com/" target="_blank">Rob Connerys blog</a> and looking at his <a href="http://mvcsamples.codeplex.com/" target="_blank">MVC Storefront</a> series and his <a href="http://mvcstarter.codeplex.com/" target="_blank">MVC starter kit</a> I decided to look at NLog so that I have another logging framework in  my utility belt. It&#8217;s always good to broaden your knowledge and learn a  new skill or master a new toolkit.</p>
<p>So what follows is pretty much exactly what Rob Conery did for his MVC starter kit website. However I have extended the default NLog functionality by including 2 new layout renderers.</p>
<p>Here are the steps we need to take to integrate NLog into our website:</p>
<p>1. Download NLog.</p>
<p>2. Create a table in our database to store the NLog messages.</p>
<p>3. Configure our NLog configuration file.</p>
<p>4. Set up a logging interface for our website.</p>
<p>5. Implement an NLog logger that uses our interface to log messages to the database table in step 2.</p>
<p>6. Add some additional layout renders that we will need for NLog.</p>
<p>Let&#8217;s tackle each one in order.</p>
<h4>Download NLog</h4>
<p>1. Go to <a href="http://nlog-project.org/" target="_blank">http://nlog-project.org/</a> and download NLog.</p>
<p>2. Add a reference to the NLog binary in your website</p>
<h4>Create a table in our database to store the NLog messages</h4>
<p>NLog is very flexible and it is up to you as to what information you  want to store in the database but for our sample website we will be  using the script below.</p>
<p>1. Run the following script against your database:</p>
<p><pre class="brush: sql;">
CREATE TABLE [dbo].[NLog_Error](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[time_stamp] [datetime] NOT NULL,
	[host] [nvarchar](max) NOT NULL,
	[type] [nvarchar](50) NOT NULL,
	[source] [nvarchar](50) NOT NULL,
	[message] [nvarchar](max) NOT NULL,
	[level] [nvarchar](50) NOT NULL,
	[logger] [nvarchar](50) NOT NULL,
	[stacktrace] [nvarchar](max) NOT NULL,
	[allxml] [ntext] NOT NULL,
 CONSTRAINT [PK_NLogError] PRIMARY KEY CLUSTERED
(
	[Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

ALTER TABLE [dbo].[NLog_Error] ADD  CONSTRAINT [DF_NLogError_time_stamp]  DEFAULT (getdate()) FOR [time_stamp]
GO
</pre></p>
<h4>Configure our NLog configuration file</h4>
<p>Create a file called NLog.config in the root directory of your website and paste the following into it:</p>
<p><pre class="brush: xml;">
&lt;?xml version=&quot;1.0&quot; ?&gt;
&lt;nlog autoReload=&quot;true&quot; throwExceptions=&quot;true&quot; internalLogFile=&quot;${basedir}/App_Data/nlog.txt&quot; internalLogLevel=&quot;Debug&quot;
 internalLogToConsole=&quot;true&quot;&gt;

 &lt;targets&gt;
 &lt;!--Useful for debugging--&gt;
 &lt;target name=&quot;consolelog&quot; type=&quot;ColoredConsole&quot;
 layout=&quot;${date:format=HH\:mm\:ss}|${level}|${stacktrace}|${message}&quot; /&gt;

 &lt;target name=&quot;filelog&quot; type=&quot;File&quot; fileName=&quot;${basedir}/App_Data/Site.log&quot;
 layout=&quot;${date}: ${message}&quot; /&gt;

 &lt;target name=&quot;eventlog&quot; type=&quot;EventLog&quot; source=&quot;My App&quot; log=&quot;Application&quot;
 layout=&quot;${date}: ${message} ${stacktrace}&quot; /&gt;

 &lt;target name=&quot;databaselog&quot; type=&quot;Database&quot;&gt;

 &lt;dbProvider&gt;sqlserver&lt;/dbProvider&gt;

 &lt;!-- database connection parameters --&gt;
 &lt;!-- alternatively you could provide a single 'connectionstring' parameter --&gt;
 &lt;connectionString&gt;Data Source=[Machine]\[Instance];Initial Catalog=[DatabaseName];Integrated Security=SSPI&lt;/connectionString&gt;

 &lt;commandText&gt;
 insert into NLog_Error ([time_stamp],[level],[host],[type],[source],[logger],[message],[stacktrace],[allxml]) values(@time_stamp,@level,@host,@type,@source,@logger,@message,@stacktrace,@allxml);
 &lt;/commandText&gt;

 &lt;parameter name=&quot;@time_stamp&quot; layout=&quot;${utc_date}&quot; /&gt;
 &lt;parameter name=&quot;@level&quot; layout=&quot;${level}&quot; /&gt;
 &lt;parameter name=&quot;@host&quot; layout=&quot;${machinename}&quot; /&gt;
 &lt;parameter name=&quot;@type&quot; layout=&quot;${exception:format=type}&quot; /&gt;
 &lt;parameter name=&quot;@source&quot; layout=&quot;${callsite:className=true:fileName=false:includeSourcePath=false:methodName=false}&quot; /&gt;
 &lt;parameter name=&quot;@logger&quot; layout=&quot;${logger}&quot; /&gt;
 &lt;parameter name=&quot;@message&quot; layout=&quot;${message}&quot; /&gt;
 &lt;parameter name=&quot;@stacktrace&quot; layout=&quot;${exception:stacktrace}&quot; /&gt;
 &lt;parameter name=&quot;@allxml&quot; layout=&quot;${web_variables}&quot; /&gt;

 &lt;/target&gt;

 &lt;/targets&gt;

 &lt;rules&gt;
 &lt;!--
 &lt;logger name=&quot;*&quot; minlevel=&quot;Fatal&quot; writeTo=&quot;eventlog&quot; /&gt;
 --&gt;
 &lt;logger name=&quot;*&quot; minlevel=&quot;Info&quot; writeTo=&quot;filelog&quot; /&gt;
 &lt;logger name=&quot;*&quot; minlevel=&quot;Info&quot; writeTo=&quot;databaselog&quot; /&gt;
 &lt;/rules&gt;

&lt;/nlog&gt;
</pre></p>
<p>The following link provides a handy reference to all of the layouts used by NLog:<br />
<a href="http://nlog-project.org/wiki/Layout_renderers" target="_blank">http://nlog-project.org/wiki/Layout_renderers</a></p>
<p>You may notice that there are a couple of non-standard layouts used  in the example above : {utc_date} and {web_variables}. But don&#8217;t worry  we will come back to them later on in this article.</p>
<h4>Set up a logging interface for our website</h4>
<p>I&#8217;m just going to use the logging interface created by Rob Conery  (although I noticed that the Orchard project has a fuller logging  interface, but I think the one below is fine for most needs):</p>
<p>Before we add it in, let&#8217;s create a folder called &#8216;Services&#8217; in the  root directory of our website and a child folder in there called  &#8216;Logging&#8217;.</p>
<p>Now, in the &#8216;Services -&gt; Logging&#8217; folder add the following code to a new file called, &#8216;ILogger.cs&#8217; :</p>
<p><pre class="brush: csharp;">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MySampleApp.Services.Logging
{
 public interface ILogger
 {
 void Info(string message);

 void Warn(string message);

 void Debug(string message);

 void Error(string message);
 void Error(string message, Exception x);
 void Error(Exception x);

 void Fatal(string message);
 void Fatal(Exception x);

 }
}
</pre></p>
<p>As per what Rob did, we will also add a helper function that will be  used to format an exception. Place this in a file called,  &#8216;LogUtility.cs&#8217;:</p>
<p><pre class="brush: csharp;">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MySampleApp.Services.Logging
{
 public class LogUtility
 {

 public static string BuildExceptionMessage(Exception x)
 {

 Exception logException = x;
 if (x.InnerException != null)
 logException = x.InnerException;

 string strErrorMsg = Environment.NewLine + &quot;Error in Path :&quot; + System.Web.HttpContext.Current.Request.Path;

 // Get the QueryString along with the Virtual Path
 strErrorMsg += Environment.NewLine + &quot;Raw Url :&quot; + System.Web.HttpContext.Current.Request.RawUrl;

 // Get the error message
 strErrorMsg += Environment.NewLine + &quot;Message :&quot; + logException.Message;

 // Source of the message
 strErrorMsg += Environment.NewLine + &quot;Source :&quot; + logException.Source;

 // Stack Trace of the error

 strErrorMsg += Environment.NewLine + &quot;Stack Trace :&quot; + logException.StackTrace;

 // Method where the error occurred
 strErrorMsg += Environment.NewLine + &quot;TargetSite :&quot; + logException.TargetSite;
 return strErrorMsg;
 }
 }
}
</pre></p>
<p>Next create a new folder under &#8220;Services -&gt; Logging&#8221; called &#8220;NLog&#8221;.</p>
<p>Add the following code to a new file called, &#8220;NLogLogger.cs&#8221;:</p>
<p><pre class="brush: csharp;">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using NLog;

namespace MySampleApp.Services.Logging.NLog
{
 public class NLogLogger : ILogger
 {

 private Logger _logger;

 public NLogLogger()
 {
 _logger = LogManager.GetCurrentClassLogger();
 }

 public void Info(string message)
 {
 _logger.Info(message);
 }

 public void Warn(string message)
 {
 _logger.Warn(message);
 }

 public void Debug(string message)
 {
 _logger.Debug(message);
 }

 public void Error(string message)
 {
 _logger.Error(message);
 }

 public void Error(Exception x)
 {
 Error(LogUtility.BuildExceptionMessage(x));
 }

 public void Error(string message, Exception x)
 {
 _logger.ErrorException(message, x);
 }

 public void Fatal(string message)
 {
 _logger.Fatal(message);
 }

 public void Fatal(Exception x)
 {
 Fatal(LogUtility.BuildExceptionMessage(x));
 }
 }
}
</pre></p>
<p>By default, NLog can only log the datetime using local time. However,  in this series we need to integrate the NLog logs with Elmah and Health  Monitoring which are all capable of storing their dates in UTC format.  So to do that in NLog we need to create a new layout renderer.</p>
<p>In our NLog folder, add the following code to a new file called, &#8220;UtcDateRenderer.cs&#8221;:</p>
<p><pre class="brush: csharp;">
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Web;

using NLog;
using NLog.Config;

namespace MySampleApp.Services.Logging.NLog
{
 [LayoutRenderer(&quot;utc_date&quot;)]
 public class UtcDateRenderer : LayoutRenderer
 {

 ///
 /// Initializes a new instance of the  class.
 ///
 public UtcDateRenderer()
 {
 this.Format = &quot;G&quot;;
 this.Culture = CultureInfo.InvariantCulture;
 }

 protected override int GetEstimatedBufferSize(LogEventInfo ev)
 {
 // Dates can be 6, 8, 10 bytes so let's go with 10
 return 10;
 }

 ///
 /// Gets or sets the culture used for rendering.
 ///
 ///
 public CultureInfo Culture { get; set; }

 ///
 /// Gets or sets the date format. Can be any argument accepted by DateTime.ToString(format).
 ///
 ///
 [DefaultParameter]
 public string Format { get; set; }

 ///
 /// Renders the current date and appends it to the specified .
 ///
 /// &lt;param name=&quot;builder&quot;&gt;The  to append the rendered data to.
 /// &lt;param name=&quot;logEvent&quot;&gt;Logging event.
 protected override void Append(StringBuilder builder, LogEventInfo logEvent)
 {
 builder.Append(logEvent.TimeStamp.ToUniversalTime().ToString(this.Format, this.Culture));
 }

 }
}
</pre></p>
<p>Also, I like how ELMAH stores all of the web request and cookies  information so let&#8217;s create another custom NLog renderer that will allow  us to easily record all of this information.</p>
<p>Add the following code to a new file in the &#8220;NLog&#8221; folder, called &#8220;WebVariablesRenderer.cs&#8221;:</p>
<p><pre class="brush: csharp;">
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Web;

using System.Xml;

using NLog;
using NLog.Config;

namespace MySampleApp.Services.Logging.NLog
{
 [LayoutRenderer(&quot;web_variables&quot;)]
 public class WebVariablesRenderer : LayoutRenderer
 {

 ///
 /// Initializes a new instance of the  class.
 ///
 public WebVariablesRenderer()
 {
 this.Format = &quot;&quot;;
 this.Culture = CultureInfo.InvariantCulture;
 }

 protected override int GetEstimatedBufferSize(LogEventInfo ev)
 {
 // This will be XML of an unknown size
 return 10000;
 }

 ///
 /// Gets or sets the culture used for rendering.
 ///
 ///
 public CultureInfo Culture { get; set; }

 ///
 /// Gets or sets the date format. Can be any argument accepted by DateTime.ToString(format).
 ///
 ///
 [DefaultParameter]
 public string Format { get; set; }

 ///
 /// Renders the current date and appends it to the specified .
 ///
 /// &lt;param name=&quot;builder&quot;&gt;The  to append the rendered data to.
 /// &lt;param name=&quot;logEvent&quot;&gt;Logging event.
 protected override void Append(StringBuilder builder, LogEventInfo logEvent)
 {
 StringBuilder sb = new StringBuilder();
 XmlWriter writer = XmlWriter.Create(sb);

 writer.WriteStartElement(&quot;error&quot;);

 // -----------------------------------------
 // Server Variables
 // -----------------------------------------
 writer.WriteStartElement(&quot;serverVariables&quot;);

 foreach (string key in HttpContext.Current.Request.ServerVariables.AllKeys)
 {
 writer.WriteStartElement(&quot;item&quot;);
 writer.WriteAttributeString(&quot;name&quot;, key);

 writer.WriteStartElement(&quot;value&quot;);
 writer.WriteAttributeString(&quot;string&quot;, HttpContext.Current.Request.ServerVariables[key].ToString());
 writer.WriteEndElement();

 writer.WriteEndElement();
 }

 writer.WriteEndElement();

 // -----------------------------------------
 // Cookies
 // -----------------------------------------
 writer.WriteStartElement(&quot;cookies&quot;);

 foreach (string key in HttpContext.Current.Request.Cookies.AllKeys)
 {
 writer.WriteStartElement(&quot;item&quot;);
 writer.WriteAttributeString(&quot;name&quot;, key);

 writer.WriteStartElement(&quot;value&quot;);
 writer.WriteAttributeString(&quot;string&quot;, HttpContext.Current.Request.Cookies[key].Value.ToString());
 writer.WriteEndElement();

 writer.WriteEndElement();
 }

 writer.WriteEndElement();
 // -----------------------------------------

 writer.WriteEndElement();
 // -----------------------------------------

 writer.Flush();
 writer.Close();

 string xml = sb.ToString();

 builder.Append(xml);
 }

 }
}
</pre></p>
<p>The last step is to modify the global.asax.cs file so that NLog is aware of the new layout renderers:</p>
<p><pre class="brush: csharp;">
protected void Application_Start()
 {
 AreaRegistration.RegisterAllAreas();

 RegisterRoutes(RouteTable.Routes);

 ControllerBuilder.Current.SetControllerFactory(new ErrorHandlingControllerFactory());

 // Register custom NLog Layout renderers
 LayoutRendererFactory.AddLayoutRenderer(&quot;utc_date&quot;, typeof(MySampleApp.Services.Logging.NLog.UtcDateRenderer));
 LayoutRendererFactory.AddLayoutRenderer(&quot;web_variables&quot;, typeof(MySampleApp.Services.Logging.NLog.WebVariablesRenderer));

 }
[sourcecode]&lt;/pre&gt;
At this point we are all set up to log our own messages using NLog.

To test NLog out, add some code to one of your controllers like this:

[sourcecode language=&quot;csharp&quot;]
public ActionResult Index()
 {
 IEnumerable list = activityRepository.GetAll();

 NLogLogger logger = new NLogLogger();
 logger.Info(&quot;We're on the Index page for Activities&quot;);

 try
 {
 throw new Exception(&quot;A test exception&quot;);
 }
 catch(Exception ex)
 {
 logger.Error(&quot;An error has occurred&quot;, ex);
 }

 return View(list);
 }
</pre></p>
<h4>Conclusion</h4>
<p>To recap, we now have the following in our MVC website:</p>
<p>* Elmah logging unhandled exceptions into the &#8216;ELMAH_Error&#8217; table in our database.</p>
<p>* ASP.NET Health monitoring logging events to the &#8216;aspnet_WebEvent_Events&#8217; table in our database.</p>
<p>* NLog logging messages and/or exceptions to the &#8216;NLog_Error&#8217; table in our database.</p>
<p>I&#8217;ve purposefully let each one of these tools log messages to their  own tables so that we can swap any of them out without affecting the  other ones.</p>
<p>In the upcoming articles we will tie them all together and create a  nice looking reporting tool that will consolidate all of the information  that we are now logging on our website.</p>
<p>But before we do that in the next article we will quickly add Log4Net support to our website.</p>
<h4>Download</h4>
<p>The source code for part 3 is on the Downloads tab of the <a href="http://mvclogging.codeplex.com/releases/view/49755" target="_blank">associate CodePlex website</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/dotnetdarren.wordpress.com/57/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/dotnetdarren.wordpress.com/57/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/dotnetdarren.wordpress.com/57/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/dotnetdarren.wordpress.com/57/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/dotnetdarren.wordpress.com/57/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/dotnetdarren.wordpress.com/57/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/dotnetdarren.wordpress.com/57/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/dotnetdarren.wordpress.com/57/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/dotnetdarren.wordpress.com/57/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/dotnetdarren.wordpress.com/57/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/dotnetdarren.wordpress.com/57/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/dotnetdarren.wordpress.com/57/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/dotnetdarren.wordpress.com/57/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/dotnetdarren.wordpress.com/57/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=dotnetdarren.wordpress.com&amp;blog=14830143&amp;post=57&amp;subd=dotnetdarren&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://dotnetdarren.wordpress.com/2010/07/28/logging-in-mvc-part-3-%e2%80%93-nlog/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/c6764e85b5f6b2e2e71cb35e39c28fc0?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">darrenw74</media:title>
		</media:content>
	</item>
		<item>
		<title>Logging in MVC Part 2 &#8211; Health Monitoring</title>
		<link>http://dotnetdarren.wordpress.com/2010/07/28/logging-in-mvc-part-2-health-monitoring/</link>
		<comments>http://dotnetdarren.wordpress.com/2010/07/28/logging-in-mvc-part-2-health-monitoring/#comments</comments>
		<pubDate>Tue, 27 Jul 2010 23:48:56 +0000</pubDate>
		<dc:creator>darrenw74</dc:creator>
				<category><![CDATA[ASP.NET MVC]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[elmah]]></category>
		<category><![CDATA[health monitoring]]></category>
		<category><![CDATA[log4net]]></category>
		<category><![CDATA[logging]]></category>

		<guid isPermaLink="false">http://dotnetdarren.wordpress.com/?p=37</guid>
		<description><![CDATA[This is part 2 of the MVC Logging series. Other articles in the series are: MVC Logging Part 1 &#8211; Elmah MVC Logging Part 2 &#8211; Health Monitoring MVC Logging Part 3 &#8211; NLog MVC Logging Part 4 &#8211; Log4Net MVC Logging Part 5 &#8211; Model and Data Layer Introduction This is the second article [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=dotnetdarren.wordpress.com&amp;blog=14830143&amp;post=37&amp;subd=dotnetdarren&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<blockquote><p>This is part 2 of the MVC Logging series. Other articles in the series are:</p>
<ul>
<li><a href="http://dotnetdarren.wordpress.com/logging-on-mvc-part-1" target="_self">MVC Logging Part 1 &#8211; Elmah</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-2-health-monitoring" target="_self">MVC Logging Part 2 &#8211; Health Monitoring</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-3-–-nlog" target="_self">MVC Logging Part 3 &#8211; NLog</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-4-log4net" target="_self">MVC Logging Part 4 &#8211; Log4Net</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-5-the-model-and-data-layer" target="_self">MVC Logging Part 5 &#8211; Model and Data Layer</a></li>
</ul>
</blockquote>
<h4>Introduction</h4>
<p>This is the second article in a series. The first article showed how to set up ELMAH to run on an MVC website.</p>
<p>The main focus of ELMAH is to log unhandled exceptions but there are  many events that it can&#8217;t log for us. That is where ASP.NET Health  monitoring comes in!</p>
<h4>Health Monitoring</h4>
<p>To log such things as when a website starts up, shuts down,  recompiles etc the best tool to use is ASP.NET Health Monitoring. Health  monitoring is closely tied to the ASP.NET runtime and can log many  events that happen on your website:</p>
<p>For those not familiar with what Health Monitoring here is a list of events that can be tracked using it:</p>
<p>* Application starts and stops<br />
* Failed logins and unhandled exceptions<br />
* &#8220;Heartbeats&#8221;<br />
* Successful and failed login attempts through Membership<br />
* Successful and failed URL and ACL authorizations by authenticated users<br />
* Valid and expired forms authentication tickets<br />
* View state validation failures<br />
* Compilation errors<br />
* Configuration errors<br />
* Unhandled exceptions<br />
* Request validation failures<br />
* Anything that causes request to abort<br />
* Requests queued, processing, or rejected<br />
* Specific or periodic monitoring event<br />
* Process start time and more</p>
<p>One of the best articles to get started with Health Monitoring is the official page here:</p>
<p><a href="http://www.asp.net/hosting/tutorials/logging-error-details-with-asp-net-health-monitoring-cs" target="_blank">http://www.asp.net/hosting/tutorials/logging-error-details-with-asp-net-health-monitoring-cs</a></p>
<h4>Quick setup guide</h4>
<p>1. Setup the database we are using so that we can store health monitoring events.</p>
<p>2. Modify the web.config to include a health monitoring section.</p>
<h4>Setting up the database</h4>
<p>You have 2 choices here. You can choose to store the health  monitoring events in a separate database to your normal website or you  can choose to store everything in the one database.</p>
<p>When you create a new MVC project in VS2010, a database is already   created for you in the app_data folder which should be setup and ready   to go. You may have to select &#8220;Show all files&#8221; to see the database as it   is hidden by default.</p>
<p>However, for the sample application we are building in this series I  chose to store everything in one database as it is easier to manage and  move around one database instead of two.</p>
<p>* Browse to C:\WINDOWS\Microsoft.NET\Framework\&lt;versionNumber&gt;<br />
* Run the following command :<br />
aspnet_regsql.exe -E -S [machinename]\[instancename] -d [databasename] -A all</p>
<p>-E Use Windows authentication<br />
-S server<br />
-d database<br />
-A Select options to install</p>
<p>You should now see all of the aspnet tables in your database:</p>
<p><a href="http://dotnetdarren.files.wordpress.com/2010/07/log-healthmonitoring-tables.png"><img class="alignnone size-medium wp-image-38" title="Logging - Health monitoring tables" src="http://dotnetdarren.files.wordpress.com/2010/07/log-healthmonitoring-tables.png?w=228&#038;h=300" alt="" width="228" height="300" /></a></p>
<p>If you get stuck setting up your database or want to see the full options available see the following article:</p>
<p><a href="http://msdn.microsoft.com/en-us/library/x28wfk74.aspx" target="_blank">http://msdn.microsoft.com/en-us/library/x28wfk74.aspx</a></p>
<h4>Modifying the web.config file</h4>
<p>Once the database is configured add the following to your web.config file. The parent tag is &lt;system.web&gt; :</p>
<p><pre class="brush: xml;">

&lt;healthMonitoring enabled=&quot;true&quot;&gt;
  &lt;eventMappings&gt;
    &lt;clear /&gt;
    &lt;!-- Log ALL error events --&gt;
    &lt;add name=&quot;All Errors&quot; type=&quot;System.Web.Management.WebBaseErrorEvent&quot; startEventCode=&quot;0&quot; endEventCode=&quot;2147483647&quot; /&gt;
    &lt;!-- Log application startup/shutdown events --&gt;
    &lt;add name=&quot;Application Events&quot; type=&quot;System.Web.Management.WebApplicationLifetimeEvent&quot; startEventCode=&quot;0&quot; endEventCode=&quot;2147483647&quot; /&gt;
  &lt;/eventMappings&gt;
  &lt;providers&gt;
    &lt;clear /&gt;
    &lt;!-- Provide any customized SqlWebEventProvider information here (such as a different connection string name value --&gt;
    &lt;add connectionStringName=&quot;SampleDatabaseConnectionString&quot; maxEventDetailsLength=&quot;1073741823&quot; buffer=&quot;false&quot; name=&quot;SqlWebEventProvider&quot; type=&quot;System.Web.Management.SqlWebEventProvider&quot; /&gt;
  &lt;/providers&gt;
  &lt;rules&gt;
    &lt;clear /&gt;
    &lt;add name=&quot;All Errors Default&quot; eventName=&quot;All Errors&quot; provider=&quot;SqlWebEventProvider&quot; profile=&quot;Default&quot; minInstances=&quot;1&quot; maxLimit=&quot;Infinite&quot; minInterval=&quot;00:00:00&quot; /&gt;
    &lt;add name=&quot;Application Events Default&quot; eventName=&quot;Application Events&quot; provider=&quot;SqlWebEventProvider&quot; profile=&quot;Default&quot; minInstances=&quot;1&quot; maxLimit=&quot;Infinite&quot; minInterval=&quot;00:00:00&quot; /&gt;
  &lt;/rules&gt;
&lt;/healthMonitoring&gt;
</pre></p>
<p>Don&#8217;t forget to change the name of the connection string to the one used by your website.</p>
<h4>Viewing the results</h4>
<p>ASP.NET Health Monitoring does not come with any way to view the  events logged in the database so fire up your website and query the  table in your database to ensure that the events are being logged.</p>
<p><a href="http://dotnetdarren.files.wordpress.com/2010/07/log-healthmonitoring-query.png"><img class="alignnone size-medium wp-image-45" title="Logging  - Querying the health monitoring tables" src="http://dotnetdarren.files.wordpress.com/2010/07/log-healthmonitoring-query.png?w=300&#038;h=227" alt="" width="300" height="227" /></a></p>
<h4>Conclusion</h4>
<p>Well that&#8217;s the end of this article. It was quite short in length but   in the upcoming articles I will show how we can display all of these  logged  events in a much nicer format and throw in some bells and  whistles.</p>
<h4>Download</h4>
<p>The source code for part 2 is on the Downloads tab of the <a href="http://mvclogging.codeplex.com/releases/view/49708" target="_blank">associated Codeplex website</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/dotnetdarren.wordpress.com/37/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/dotnetdarren.wordpress.com/37/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/dotnetdarren.wordpress.com/37/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/dotnetdarren.wordpress.com/37/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/dotnetdarren.wordpress.com/37/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/dotnetdarren.wordpress.com/37/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/dotnetdarren.wordpress.com/37/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/dotnetdarren.wordpress.com/37/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/dotnetdarren.wordpress.com/37/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/dotnetdarren.wordpress.com/37/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/dotnetdarren.wordpress.com/37/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/dotnetdarren.wordpress.com/37/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/dotnetdarren.wordpress.com/37/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/dotnetdarren.wordpress.com/37/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=dotnetdarren.wordpress.com&amp;blog=14830143&amp;post=37&amp;subd=dotnetdarren&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://dotnetdarren.wordpress.com/2010/07/28/logging-in-mvc-part-2-health-monitoring/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/c6764e85b5f6b2e2e71cb35e39c28fc0?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">darrenw74</media:title>
		</media:content>

		<media:content url="http://dotnetdarren.files.wordpress.com/2010/07/log-healthmonitoring-tables.png?w=228" medium="image">
			<media:title type="html">Logging - Health monitoring tables</media:title>
		</media:content>

		<media:content url="http://dotnetdarren.files.wordpress.com/2010/07/log-healthmonitoring-query.png?w=300" medium="image">
			<media:title type="html">Logging  - Querying the health monitoring tables</media:title>
		</media:content>
	</item>
		<item>
		<title>Logging in MVC Part 1- Elmah</title>
		<link>http://dotnetdarren.wordpress.com/2010/07/27/logging-on-mvc-part-1/</link>
		<comments>http://dotnetdarren.wordpress.com/2010/07/27/logging-on-mvc-part-1/#comments</comments>
		<pubDate>Tue, 27 Jul 2010 06:56:13 +0000</pubDate>
		<dc:creator>darrenw74</dc:creator>
				<category><![CDATA[ASP.NET MVC]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[elmah]]></category>
		<category><![CDATA[health monitoring]]></category>
		<category><![CDATA[log4net]]></category>
		<category><![CDATA[logging]]></category>

		<guid isPermaLink="false">http://dotnetdarren.wordpress.com/?p=5</guid>
		<description><![CDATA[This is part 1 of the MVC Logging series. Other articles in the series are: MVC Logging Part 1 &#8211; Elmah MVC Logging Part 2 &#8211; Health Monitoring MVC Logging Part 3 &#8211; NLog MVC Logging Part 4 &#8211; Log4Net MVC Logging Part 5 &#8211; Model and Data Layer Introduction Logging is one of the [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=dotnetdarren.wordpress.com&amp;blog=14830143&amp;post=5&amp;subd=dotnetdarren&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<blockquote><p>This is part 1 of the MVC Logging series. Other articles in the series are:</p>
<ul>
<li><a href="http://dotnetdarren.wordpress.com/logging-on-mvc-part-1" target="_self">MVC Logging Part 1 &#8211; Elmah</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-2-health-monitoring" target="_self">MVC Logging Part 2 &#8211; Health Monitoring</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-3-–-nlog" target="_self">MVC Logging Part 3 &#8211; NLog</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-4-log4net" target="_self">MVC Logging Part 4 &#8211; Log4Net</a></li>
<li><a href="http://dotnetdarren.wordpress.com/logging-in-mvc-part-5-the-model-and-data-layer" target="_self">MVC Logging Part 5 &#8211; Model and Data Layer</a></li>
</ul>
</blockquote>
<h2>Introduction</h2>
<p>Logging is one of the most useful services that every production website should have.</p>
<p>When errors occur on your website you should be notified about them. Whilst you may think you have written perfect code and unit tested everything to the best of your ability errors can and will still happen. The database server may go down, a 3rd party website may be offline, the shared hosting environment of your website may suffer an outage, a previously undetected bug may occur and the list goes on.</p>
<p>Having a great logging system in place allows you to stay on top of errors when they do happen.</p>
<p>In this series we will be building a logging reporting system that will allow you to drill into the various events that get logged on your website.</p>
<p>Before we get started let&#8217;s take a look at the finished solution:</p>
<p><a href="http://dotnetdarren.files.wordpress.com/2010/07/log-dashboard.png"><img class="alignnone size-medium wp-image-9" title="Logging - Dashboard" src="http://dotnetdarren.files.wordpress.com/2010/07/log-dashboard.png?w=300&#038;h=241" alt="" width="300" height="241" /></a></p>
<p>and we would also like to see some graphs so we can quickly pinpoint when errors occurred on our website:</p>
<p><a href="http://dotnetdarren.files.wordpress.com/2010/07/log-chart.png"><img class="alignnone size-medium wp-image-11" title="Logging - Chart" src="http://dotnetdarren.files.wordpress.com/2010/07/log-chart.png?w=300&#038;h=289" alt="" width="300" height="289" /></a></p>
<p>and we also would like to have an RSS feed of our errors:</p>
<p><a href="http://dotnetdarren.files.wordpress.com/2010/07/log-rss.png"><img class="alignnone size-medium wp-image-12" title="Logging - RSS" src="http://dotnetdarren.files.wordpress.com/2010/07/log-rss.png?w=300&#038;h=289" alt="" width="300" height="289" /></a></p>
<h4>Goals</h4>
<p>So let&#8217;s write down the goals for what we want to achieve:</p>
<p>1. Log all unhandled exceptions to a database</p>
<p>2. Log entries to our database whenever the website starts up, shuts down, recompiles etc.</p>
<p>3. Log any custom messages that we want to keep track of</p>
<p>4. Handle errors gracefully so that the end-user never sees a yellow screen of death error message.</p>
<p>5. Receive an email when an unhandled exception occurs</p>
<p>6. Allow multiple log providers to be easily swapped in and out of our website.</p>
<p>7. On the website, provide a logging reporting tool that lets us filter and view any message that has been logged.</p>
<p>8. The reporting tool should also allow us to view log entries as an  RSS feed so that we can combine multiple feeds from all of the websites  under our control.</p>
<p>9. The reporting tool should also have a dashboard or chart so that  we can quickly get an overview of any activity on the website.</p>
<p>10. Provide an ability to manage logs, purge old logs, export logs to files or email them to someone.</p>
<p>Well that&#8217;s quite a list, so let&#8217;s get started!</p>
<h4>Getting started</h4>
<p>The first thing that we want to do is take care of unhandled  exceptions so one of the best tools for that job is ELMAH. ELMAH was  created by <a href="http://www.raboof.com/" target="_blank">Atif Aziz</a> and is used on many websites including <a href="http://www.stackoverflow.com/" target="_blank">StackOverflow</a>.</p>
<p>To get started with ELMAH, the following article provides a very good  overview of how to set it all up for a normal ASP.Net Web forms  website:</p>
<p><a href="http://www.asp.net/hosting/tutorials/logging-error-details-with-elmah-cs" target="_blank">http://www.asp.net/hosting/tutorials/logging-error-details-with-elmah-cs</a></p>
<p>I won&#8217;t go into all of the details here about setting up Elmah  because the above article covers pretty much everything you need to  know.</p>
<p>In simple terms you need to :</p>
<p>1. Download Elmah from it&#8217;s <a href="http://code.google.com/p/elmah/" target="_blank">project page on CodePlex.</a></p>
<p>2. Extract the files from the zip.</p>
<p>3. The database script is located in the /src/Elmah directory. For SQL Server it is called &#8220;SQLServer.sql&#8221;. Run the database script to add the Elmah table and stored procs to your database.</p>
<p>4. Add a reference to Elmah for your website.</p>
<p>5. Modify the web.config file sections as explained in the article  above. You can download the code I used at the end of this article to  see the necessary web.config file alterations.</p>
<h4>Configuring Elmah on MVC</h4>
<p>However, as we will be using ELMAH on an MVC website we need to do a  little bit more than the normal configuration to get it all working  together nicely.</p>
<p>In MVC, you normally put [HandleError] attributes on your controllers to handle errors. Like this:</p>
<p><pre class="brush: csharp;">
namespace MySampleApp.Controllers
{
 [HandleError]
 public class AccountController : Controller
 {
 }
}
</pre></p>
<p>But because we would like to use Elmah to handle our exceptions  instead of MVC, we will replace the standard MVC [HandleError] attribute  with our own custom attribute called [HandleErrorWithElmah].</p>
<p>The StackOverflow question below provides the necessary details:</p>
<p><a href="http://stackoverflow.com/questions/766610/" target="_blank">http://stackoverflow.com/questions/766610/</a></p>
<p>The code necessary is shown below:</p>
<p><pre class="brush: csharp;">
public class HandleErrorWithELMAHAttribute : HandleErrorAttribute
    {
        public override void OnException(ExceptionContext context)
        {
            base.OnException(context);

            var e = context.Exception;
            if (!context.ExceptionHandled   // if unhandled, will be logged anyhow
                    || RaiseErrorSignal(e)      // prefer signaling, if possible
                    || IsFiltered(context))     // filtered?
                return;

            LogException(e);
        }

        private static bool RaiseErrorSignal(Exception e)
        {
            var context = HttpContext.Current;
            if (context == null)
                return false;
            var signal = ErrorSignal.FromContext(context);
            if (signal == null)
                return false;
            signal.Raise(e, context);
            return true;
        }

        private static bool IsFiltered(ExceptionContext context)
        {
            var config = context.HttpContext.GetSection(&quot;elmah/errorFilter&quot;)
                                     as ErrorFilterConfiguration;

            if (config == null)
                return false;

            var testContext = new ErrorFilterModule.AssertionHelperContext(
                                                                context.Exception, HttpContext.Current);

            return config.Assertion.Test(testContext);
        }

        private static void LogException(Exception e)
        {
            var context = HttpContext.Current;
            ErrorLog.GetDefault(context).Log(new Error(e, context));
        }
    }
</pre></p>
<p>At this point we could subsitute the [HandleError] attribute with our  [HandleErrorWithELMAH] attribute but we would need to remember to do  this on all of our MVC controllers&#8230; There has to be a better, easier  way! And there is!</p>
<p>The solution is to create a custom Controller factory! And we will  also need to write our own ActionInvoker. Let&#8217;s write that one first:</p>
<p><pre class="brush: csharp;">
public class ErrorHandlingActionInvoker : ControllerActionInvoker
 {
 private readonly IExceptionFilter filter;

 public ErrorHandlingActionInvoker(IExceptionFilter filter)
 {
 if (filter == null)
 {
 throw new ArgumentNullException(&quot;filter&quot;);
 }

 this.filter = filter;
 }

 protected override FilterInfo GetFilters(
 ControllerContext controllerContext,
 ActionDescriptor actionDescriptor)
 {
 var filterInfo =
 base.GetFilters(controllerContext,
 actionDescriptor);

 filterInfo.ExceptionFilters.Add(this.filter);

 return filterInfo;
 }
 }
</pre></p>
<p>And here is the code for the custom controller factory:</p>
<p><pre class="brush: csharp;">
public class ErrorHandlingControllerFactory : DefaultControllerFactory
 {
 public override IController CreateController(
 RequestContext requestContext,
 string controllerName)
 {
 var controller =
 base.CreateController(requestContext,
 controllerName);

 var c = controller as Controller;

 if (c != null)
 {
 c.ActionInvoker =
 new ErrorHandlingActionInvoker(
 new HandleErrorWithELMAHAttribute());
 }

 return controller;
 }
 }
</pre></p>
<p>The last step is to hook it all up so that happens automatically for  each controller we have in our MVC project. To do that we wire up our  new controller factory in the Global.asax.cs file:</p>
<p><pre class="brush: csharp;">
protected void Application_Start()
 {
 AreaRegistration.RegisterAllAreas();

 RegisterRoutes(RouteTable.Routes);

 ControllerBuilder.Current.SetControllerFactory(new ErrorHandlingControllerFactory());
 }
</pre></p>
<p>So now that it is all wired up, we can actually go ahead and remove  the [HandleError] attribute from all of our MVC controllers as our  custom controller factory will now automatically inject the  [HandleErrorWithElmah] attribute automatically.</p>
<p>The last step is to force an unhandled exception occurring in our  application and view the elmah.axd page on our website to see the errors  logged.</p>
<p>In the Home Controller I have this:</p>
<p><pre class="brush: csharp;">
public ActionResult About()
 {
 // Throw a test error so that we can see that it is handled by Elmah
 // To test go to the ~/elmah.axd page to see if the error is being logged correctly
 throw new Exception(&quot;A test exception for ELMAH&quot;);

 return View();
 }
</pre></p>
<p>and then we just browse to the About page which will generate an exception:</p>
<p><a href="http://dotnetdarren.files.wordpress.com/2010/07/log_generate_error.png"><img class="alignnone size-medium wp-image-13" title="Logging - Generate an error" src="http://dotnetdarren.files.wordpress.com/2010/07/log_generate_error.png?w=300&#038;h=180" alt="" width="300" height="180" /></a></p>
<p>and then browse to the Elmah page to verify that the error was logged correctly.</p>
<p><a href="http://dotnetdarren.files.wordpress.com/2010/07/log-elmah.png"><img class="alignnone size-medium wp-image-14" title="Logging - Elmah" src="http://dotnetdarren.files.wordpress.com/2010/07/log-elmah.png?w=300&#038;h=238" alt="" width="300" height="238" /></a></p>
<h4>Conclusion</h4>
<p>That ends part 1 of this series. Next time we will look at logging  things like when the application starts up, shuts down and recompiles  etc.</p>
<h4>Download</h4>
<p>The sourcecode for part 1 is on the Downloads tab of the <a href="http://mvclogging.codeplex.com/releases/view/49668" target="_blank">associated Codeplex website</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/dotnetdarren.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/dotnetdarren.wordpress.com/5/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/dotnetdarren.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/dotnetdarren.wordpress.com/5/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/dotnetdarren.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/dotnetdarren.wordpress.com/5/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/dotnetdarren.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/dotnetdarren.wordpress.com/5/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/dotnetdarren.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/dotnetdarren.wordpress.com/5/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/dotnetdarren.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/dotnetdarren.wordpress.com/5/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/dotnetdarren.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/dotnetdarren.wordpress.com/5/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=dotnetdarren.wordpress.com&amp;blog=14830143&amp;post=5&amp;subd=dotnetdarren&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://dotnetdarren.wordpress.com/2010/07/27/logging-on-mvc-part-1/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/c6764e85b5f6b2e2e71cb35e39c28fc0?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">darrenw74</media:title>
		</media:content>

		<media:content url="http://dotnetdarren.files.wordpress.com/2010/07/log-dashboard.png?w=300" medium="image">
			<media:title type="html">Logging - Dashboard</media:title>
		</media:content>

		<media:content url="http://dotnetdarren.files.wordpress.com/2010/07/log-chart.png?w=300" medium="image">
			<media:title type="html">Logging - Chart</media:title>
		</media:content>

		<media:content url="http://dotnetdarren.files.wordpress.com/2010/07/log-rss.png?w=300" medium="image">
			<media:title type="html">Logging - RSS</media:title>
		</media:content>

		<media:content url="http://dotnetdarren.files.wordpress.com/2010/07/log_generate_error.png?w=300" medium="image">
			<media:title type="html">Logging - Generate an error</media:title>
		</media:content>

		<media:content url="http://dotnetdarren.files.wordpress.com/2010/07/log-elmah.png?w=300" medium="image">
			<media:title type="html">Logging - Elmah</media:title>
		</media:content>
	</item>
		<item>
		<title>Welcome</title>
		<link>http://dotnetdarren.wordpress.com/2010/07/23/welcome/</link>
		<comments>http://dotnetdarren.wordpress.com/2010/07/23/welcome/#comments</comments>
		<pubDate>Thu, 22 Jul 2010 20:00:26 +0000</pubDate>
		<dc:creator>darrenw74</dc:creator>
				<category><![CDATA[ASP.NET MVC]]></category>

		<guid isPermaLink="false">http://dotnetdarren.wordpress.com/?p=1</guid>
		<description><![CDATA[Welcome to my blog. Here you will find articles on ASP.NET, MVC<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=dotnetdarren.wordpress.com&amp;blog=14830143&amp;post=1&amp;subd=dotnetdarren&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Welcome to my blog.</p>
<p>Here you will find articles on ASP.NET, MVC</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/dotnetdarren.wordpress.com/1/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/dotnetdarren.wordpress.com/1/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/dotnetdarren.wordpress.com/1/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/dotnetdarren.wordpress.com/1/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/dotnetdarren.wordpress.com/1/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/dotnetdarren.wordpress.com/1/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/dotnetdarren.wordpress.com/1/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/dotnetdarren.wordpress.com/1/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/dotnetdarren.wordpress.com/1/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/dotnetdarren.wordpress.com/1/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/dotnetdarren.wordpress.com/1/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/dotnetdarren.wordpress.com/1/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/dotnetdarren.wordpress.com/1/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/dotnetdarren.wordpress.com/1/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=dotnetdarren.wordpress.com&amp;blog=14830143&amp;post=1&amp;subd=dotnetdarren&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://dotnetdarren.wordpress.com/2010/07/23/welcome/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/c6764e85b5f6b2e2e71cb35e39c28fc0?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">darrenw74</media:title>
		</media:content>
	</item>
	</channel>
</rss>
