Tallan Blog

Tallan’s Experts Share Their Knowledge on Technology, Trends and Solutions to Business Challenges

Streamlining ELMAH With A Logging Platform in ASP.NET

Logging and tracing are both critical components of enterprise software development. And yet they are often overlooked or otherwise treated as an afterthought. However, as any programmer worth his or her salt will tell you, proper logging and tracing saves countless hours and headaches when it comes to tracking down bugs in a production environment. To serve these needs, there are numerous logging frameworks to choose from. In this article I am going to explore two popular frameworks and how to best use them together as a comprehensive logging and tracing solution.

Before we begin, let’s define the difference between logging and tracing. Logging is the broad process of recording events that occur in a running software program. The resulting logs include any information about these events that the developer deems necessary. Tracing, on the other hand, is a more specialized form of logging that records specific exception information encountered during a program’s execution. Trace logs are often instrumental when it comes to debugging and diagnosing issues.

With that out of the way, let’s jump into the frameworks.

First up is logging with NLog. NLog is one of the most widely used logging frameworks for ASP.NET applications. It can easily be configured to support multiple loggers and multiple logging destinations. With NLog you can easily filter log events, choose the layout of the logged messages, and even how the messages are rendered. Destinations at your disposal include files, the Windows event log, databases, the console, and e-mail. NLog can even be configured to consume an API endpoint! You can create as many loggers as you need and place them wherever you want in the codebase (NLog suggests one logger per class). This tutorial is not meant to cover NLog in detail. However, if you would like to know more about the platform more information can be found here.

Next up is ELMAH. ELMAH (Error Logging Modules and Handlers) is a logging platform with a much more specific purpose—to log unhandled exceptions in an ASP.Net web application in support of trace logs. It is a bit of a one-trick-pony in that regard, but its trick is a pretty darn good one. Similar to NLog, ELMAH can be configured to log multiple targets. Unlike NLog, ELMAH does most of its work on its own. It only needs to be configured initially and then dropped into a running web application. From there it will automatically log exceptions wherever they occur.

Keep in mind it is only logging the exceptions. While having stack traces automatically logged provides a nice debugging safety net whenever unexpected exceptions arise, it should not be treated as a replacement for catching and handling exceptions appropriately.

If you’re interested in exploring more about ELMAH, click here.

It is fair to say that neither of these frameworks are necessarily better than the other. Each excels at serving a different need. NLog is great as a general-purpose logger and ELMAH supports plug and play logging of exceptions that occur anywhere in the running code. Together they provide a comprehensive logging solution.

A comprehensive solution. This is great! The only issue is, as a developer, now I have two logging frameworks to maintain. For instance, if I want to change a logging target—let’s say I want to log to a database instead of a log file—I must now change the configuration in TWO places. Similarly, if I want to filter a class of log messages, I must now update two different loggers. This violates best practices and is a classic cause of problems in software development. It is all too common in this scenario for an uninformed developer to make a rushed change and only update one of the loggers, overlooking the other. Even for an informed developer, it adds unnecessary time to development and testing having to worry about two different loggers.

So, there’s the problem, where’s the solution? It’s simple really, all we have to do is connect ELMAH and NLog! Thankfully, while ELMAH has many built-in loggers to choose from, it also allows for the creation of custom loggers, providing us with a path to connect it to NLog. Here is how we do it.

The code snippet below shows a simple custom ELMAH Logger that logs its messages by invoking an NLog logger. This raises a log level of ‘Error’ (seems appropriate to me) and logs the message and exception. We can achieve this easily by extending the abstract class ErrorLog, which is provided by the ELMAH library. ErrorLog has three methods we need to implement, GetError, GetErrors, and Log. The only one we are really concerned about is Log (GetError and GetErrors only come into play with the built-in web page log view that comes with ELMAH. We aren’t worried about this feature in this tutorial), which is the method that gets called by the logger whenever an exception is raised.

namespace Tallan.GovSys.VAD.Server.Core.Logging
{
    public class ElmahErrorLog : ErrorLog
    {

        private readonly ILogger _logger = LogManager.GetCurrentClassLogger();

        public ElmahErrorLog(IDictionary config) { }

        public override ErrorLogEntry GetError(string id)
        {
            throw new NotImplementedException();
        }

        public override int GetErrors(int pageIndex, int pageSize, IList errorEntryList)
        {
            throw new NotImplementedException();
        }

        public override string Log(Error error)
        {
            _logger.Error(error.Exception, error.Message);
            return error.Detail;
        }
    }
}

Essentially, we are passing the burden of logging directly to NLog. This is a simple example and can be customized to suit whatever needs you may have. Below is a very high-level overview of how this whole process works.

Process

Now that we have created the new ELMAH logger, we need to configure ELMAH to use it! This can be done either programmatically or through a configuration file. I initially wrote this tutorial in .NET Framework, so this means updating the Web.config file. The following code creates a new ELMAH XML section, which we will use to specify our new log. This new sectionGroup can be placed within the configuration tags.

<sectionGroup name="elmah">
      <section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
    </sectionGroup>

With the sectionGroup created, we can now configure ELMAH to use our new error logger. Here we simply have to specify the type, which as you can see, is the fully qualified name of the object and the project where the object is located. We have already configured the errorLog section to be Elmah.ErrorLogSectionHandler, which as the name implies, handles unhandled exceptions. We are then configuring errorLog to log the error to the specific logger we declare.

  <elmah>
    <errorLog type="Tallan.Core.Logging.ElmahErrorLog, Tallan.Core" />
  </elmah>

The above actually addresses a shortcoming of ELMAH. ELMAH is not designed to respond to unhandled exceptions gracefully. With this, now we have it covered.

That’s it for ELMAH! The only thing left to do is configure NLog. Here I have created two targets—database and console—and a logger for each of them. They are both set to log any log level. Notice there is no mention of ELMAH here. Like I said earlier, the entire logging burden is now placed on NLog. When an unhandled exception is raised, ELMAH handles the exception and raises and Error level log event with an NLog logger. As long as we have a logger that Error level log messages, NLog takes over from here and now any actual logging configuration can be done solely with NLog!

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
        autoReload="true"
        throwExceptions="true"
        internalLogLevel="Trace"
        internalLogFile="c:\temp\nlog-internal.log"
        throwConfigExceptions="true">
    <targets>
      <target name="database" xsi:type="Database"
        connectionString="Data Source=DATASOURCESTRING"
        commandText="insert into Editor.Log (MachineName, IdentityUser, Logged, Level, Message, Logger, CallSite, Exception)
                  values (@MachineName, @IdentityUser, @Logged, @Level, @Message, @Logger, @Callsite, @Exception);">
        <dbProvider>sqlserver</dbProvider>
        <parameter name="@MachineName" layout="${machinename}" />
        <parameter name="@IdentityUser" layout="${windows-identity}" />
        <parameter name="@Logged" layout="${date}" />
        <parameter name="@Level" layout="${level}" />
        <parameter name="@Message" layout="${message}" />
        <parameter name="@Logger" layout="${logger}" />
        <parameter name="@Callsite" layout="${callsite}" />
        <parameter name="@Exception" layout="${exception:tostring}" />
      </target>
      <target name="console"
              xsi:type="Console"
              layout="${machinename} ${longdate} ${level} ${message} ${logger} ${callsite} ${exception:tostring}"/>
    </targets>
    <rules>
      <logger name="*" writeTo="database">
      </logger>
      <logger name="*" writeTo="console" />
    </rules>
  </nlog>

I feel it is important to note a few things about the above configuration before I close. Notice how I am saving both the logger and callsite layouts. ‘Logger’ is the name of the logger that logged the message, and ‘callsite’ is the method that the log event was raised in. By creating the NLog logger with LogManager.GetCurrentClassLogger(), it is named after the class it is embedded in. ‘This makes it clear which log events originate from unhandled exceptions.

And there you have it. We can now utilize all the capabilities of NLog and ELMAH, yet still have one centralized logging platform to maintain; logging that easily and elegantly handles both general logging and trace logging. While we used NLog in this tutorial, it could easily be swapped out for another ASP.NET logging platform such as log4net or Serilog. All you would have to do is write the hook for ELMAH to use that logger as a target as I did above with NLog.

Hopefully, your production software never encounters any errors, but if it does, your logging implementation will be the key to discovering and fixing the solution in an expedient and efficient manner!


Click Here to learn more about Tallan or see us in person at one of many Events!

Share this post:

No comments

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

\\\