LibrarySites.Banner

All About Exception Management with the Sitecore ASP.NET CMS

This blog post provides basic guidance for managing exceptions with the Sitecore ASP.NET web Content Management System (CMS). This is a high-level overview that provides links to resources that go into more depth and provide code examples to address various issues. This blog post is based loosely on content from my presentation at Sitecore Symposium 2012 about "Five Efficient Ways to Kill Your Website".

Custom Errors

The /configuration/system.web/customErrors element of the /web.config file can affect how ASP.NET manages exceptions. If the mode attribute is Off, or if the mode attribute is RemoteOnly and the current HTTP request originated from the local system, then ASP.NET renders information about exceptions. If the mode attribute is On or the current HTTP request originated from a remote, then ASP.NET redirects the browser as configured in this section of the /web.config file.

In some cases, Sitecore APIs account for this configuration when determining how to handle exceptions. For example, the default implementation for managing exceptions in XSL renderings may render different levels of detail depending on the mode attribute and whether the current HTTP request originated locally.

Try...Catch...Finally Blocks

Always trap exceptions from which you can recover as close as possible to their occurrence, but do not trap exceptions that you do not expect or that you cannot handle. You know the most about how to react to any kind of exception in your code. Exception management logic at higher levels does not have access to the processing context at the time of the occurrence, and can only respond generically by logging, raising alerts, rendering no content or alternate for individual controls, redirecting to friendly error pages, rendering information about the error, or otherwise.

XSL Exception Management

At least two types of exceptions can occur while processing an XSL rendering:

  • The XSL code does not compile
  • A call from XSL code to an .NET XSL extension method (including extension elements) raises an exception

If the code does not compile, the rendering does not generate any output. If an exception occurs at runtime, any output generated before that exception could appear in the response, including invalid markup (elements without closing elements). In such cases, Sitecore renders information about the error so that the remainder of the page renders, although you can cause the component to generate no output, alternate output, redirect, or otherwise handle the error. 

Web Forms Exception Management

You can put exception management at the layout (Web Form/.aspx) level for example when you need to serve a page successfully regardless of any errors that may occur. Your can implement exception management in individual layouts and/or you can add exception management to a base class from which the relevant layouts derive. You can implement the same or different exception management logic for web controls, sublayouts, XSL renderings, method renderings, and url renderings.

Sitecore lets can apply different exception management logic for in specific types of controls. For example, XSL renderings render information about the error, letting exceptions in sublayouts bubble up the ASP.NET call stack, and redirecting for other types of exceptions, but I do not expect that developers frequently implement exception management logic at the control level.

MVC Exception Management

MVC solutions have two additional techniques for managing exceptions:

  • To handle exceptions for all types of renderings, you can override the ExecuteRendering processor in the mvc.renderRendering pipeline.
  • To handle exceptions for all instances of specific types of renderings, you can override the Render() method of the existing class that derives from Sitecore.Mvc.Presentation.Renderer to implement the renderer for that type, and the
    GetXsltRenderer processor in the mvc.getRenderer pipeline to return instances of that type.
  • To handle exceptions that you could otherwise handle by implementing the System.Web.Mvc.IExceptionFilter interface, you can use an mvc.exception pipeline processor.

Application (Global) Exception Management

You can define exception management logic at the application level to trap exceptions not handled at any lower level in the call stack.

I am not sure if responsibility lies with Sitecore or ASP.NET itself, but from my experience Sitecore solutions do not apply the /configuration/system.web/customErrors element of the Web.config file when exceptions occur in MVC solutions. Therefore, that section of the /Web.config file may apply only to Web Forms or other types of pages.

You can use another technique to handle exceptions globally for both Web Forms and MVC solutions. Specifically, you can implement exception management in the application. It almost seems like it should be possible to trap exceptions at the global level and apply the /configuration/system.web/customErrors element to transfer, redirect, or otherwise handle the error.

For me, three forms of global application-level exception management come to mind:

  • Sitecore logs all unhandled exceptions to the system log and in some cases determines whether to redirect to a friendly error page.
  • You can log exceptions to a custom repository and generate notification for some types or levels of exceptions.
  • You can redirect the browser with logic and options beyond those provided by IIS and ASP.NET (customErrors and IIS error pages), such as different error pages (or items) for different managed sites.

Error Pages

If you do not trap an exception at a lower level, depending on its configuration, the solution should redirect the browser to a friendly error page. If the solution fails to redirect the browser, it may show its own friendly error page or an error message.

Managing Exceptions

In general, I recommend that you resolve all exceptions, understand all warnings and errors, and investigate and resolve all errors to the extent possible. More information about an error may appear in the Sitecore log than appears in the user interface, including details about related exceptions. Always monitor the Sitecore logs for all exceptions, errors, and warnings (and of course fatal errors).

The most useful information for diagnosing an exception generally involves the stack trace and any values to assertions, so use assertions liberally and pass as much information as possible. For example:

Sitecore.Data.Database db = Sitecore.Context.Database;
Sitecore.Diagnostics.Assert.IsNotNull(db, "Sitecore.Context.Database");
string path = "/sitecore/content/home";
Sitecore.Data.Items.Item home = db.GetItem(path);
Sitecore.Diagnostics.Assert.IsNotNull(home, path + " in " + db.Name);

I believe that in most cases Sitecore does not cache the output of renderings that experience exceptions, but a customization described in the blog posts about exceptions in sublayouts linked at the end of this page allows caching output after exceptions.

Conclusion

Please comment on this blog post with any additional information about exception management with Sitecore and ASP.NET and links to relevant resources.

Resources

  • John,  In our Sitecore 6.6 setup, it appears that Sitecore is logging any .net exception. We wanted our exceptions to have additional information, like request URL, server name, etc. so we added code to our global.asax.cs to report the exception in there (like any standard Asp.Net site). However, we are now getting the same exception reported twice. Is there any way to either prevent Sitecore from capturing general .Net exceptions internally or allow us to modify the Sitecore exception capture to include the additional information we require?  Thanks, Charles Boyung

  • @Charles:   It appears that something in the Sitecore HTTP module is responsible for that exception logging. Unfortunately, that assembly is obfuscated and therefore difficult to diagnose. It appears that Application_Start()adds an event handler for exceptions that reach the application level. It may be possible to write some code that runs after that and removes that event handler. Alternatively, it might be possible for you to trap, log, and clear the exception before the Sitecore HTTP module, but I don't think you can do that with global.asax (maybe with an HTTP module that you register before the Sitecore module). In any case, if you don't let the exception bubble up (which would result in Sitecore logging it), then you have to be sure that you're handling that exception correctly (not leaking exception details, etc.). And we don't know for sure that this approach would not disable some other Sitecore functionality, such as DMS reporting of exceptions. So I think you would really want to disable Sitecore exception logging, not Sitecore exception management, and I don't know that you can do only one of these.  Continued...

  • ...Continued  To minimize interference, maybe a custom HTTP module before the Sitecore HTTP module (or some other layer in the application) could wrap the exception (create an exception of some custom type, with InnerException set to that original exception) in another exception of a custom type that adds the raw requested URL, servername, and anything else to the original exception message, and then lets Sitecore log your exception and do whatever else it does with exceptions? Note that we're only talking about exceptions that reach the application level; if you trap exceptions at lower levels and want these details logged there, you would need to do something there as well (trap, log, and clear the exception?).  Or could you instead log your exceptions to a separate file that only contains exceptions? I realize this would not provide the surrounding context in the log, but maybe the log message itself could contain enough detail?

  • @Charles: Also note that at least ASP.NET MVC handles some HTTP "errors" (such as the HTTP 404 condition) as exceptions; you might not want to log or interfere with some specific exceptions.

  • Also remember that you may want to handle errors differently depending on the mode attribute of the customErrors element and/or whether the request originated from the local system, although I'm not sure what impact that might have in this case.  I've been trying this with HTTPModule and I can intercept the exception, but if I throw, Sitecore's exception management doesn't log my exception, and I can't find a way to change the last exception at the application layer. So I can't seem to wrap the exception from within an HTTP module, and have Sitecore's HTTP module log my exception.  One alternative is to trap the exception at the page level, wrap it, and throw that, so that Sitecore traps it at the application level and logs the additional detail. But then you would need that error management logic in all of your layouts (or a base class shared to all of them). But you don't need an HTTP module.  Another option would be for your HTTP module to just write some additional information and then let Sitecore log it as it does normally. This should have minimal impact on ASP.NET and Sitecore, but under very heavy load with lots of concurrent exceptions, I assume this could result in log entries appearing out of order (information about one exception interspersed with information about others), but it might be a viable solution.