Exception logging in MVC with log4net

Ideally you don't want to have any unhandled exceptions within your application but when they do occur you will want to at least get some clues on what happened.

In a Web Forms applications you could always inspect the server event viewer to see any exceptions that have not been handled, this is not an ideal scenario for two reasons: First of all you don't want to have to go digging through server logs to find out what went wrong, secondly you may want the flexibility to perform other actions when an error occurs.

Out of the box MVC catches all exceptions, this means your users should never see a yellow screen of death but it also means you have no way of knowing what exactly happened. The simplest method around this is to use a package such as log4net to give you more options on what to do with the errors. The following steps were tested using MVC 5.2.x. 

First install log4net

The easiest way to do this is with nuget:

PM> Install-Package log4net

Modify Global.asax.cs

In the Global.asax.cs setup configure log4net, again there are more options here see the log4net site for details.

protected void Application_Start()
{
log4net.Config.XmlConfigurator.Configure();
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}

Add log4net config section to the web.config

You have a lot of flexibility with log4net which is beyond the scope of this example, the following section simply logs everything to a txt file, but you could easily send the error via email or log it to a database if you prefer.

<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" requirePermission="false" />
<log4net debug="true">
    <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
        <file value="logs\log.txt" />
        <appendToFile value="true" />
        <rollingStyle value="Size" />
        <maxSizeRollBackups value="10" />
        <maximumFileSize value="10MB" />
        <staticLogFileName value="true" />
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%-5p %d %5rms %-22.22c{1} %-18.18M - %m%n" />
        </layout>
    </appender>
    <root>
        <level value="*" />
        <appender-ref ref="RollingLogFileAppender" />
    </root>
</log4net>

Modify Startup.cs 

[assembly: log4net.Config.XmlConfigurator(ConfigFile = "Web.config", Watch = true)]

Implement a global action filter to log any exceptions

Action filters allow you to add custom behaviors to Action methods, MVC already has a built in handle error action filter which can be extended: 

public class HandleExceptionsAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
var exception = filterContext.Exception;
        ILog log = log4net.LogManager.GetLogger("UnhandledException");
        log.Error("Error", exception);
    }
}

Modify FilterConfig.cs

In the App_start folder add your new exception action filter to the global FilterConfig.cs, this means the custom filter will be applied globally to all action methods.

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleExceptionsAttribute());
}

This is obviously a catch all approach to get something working quickly. You will likely want to do some more robust error handling throughout your application and log the information directly from those locations if it is appropriate, or be more specific about what you log.