Elmah Custom SqlErrorLog

Following on from my previous problem I had with Elmah which meant I switched from using the SQLite logger to SQLServer I then had an additional annoyance.

When configuring Elmah for SQLServer, you just give it the name of the connection string as defined in the connectionStrings section of the web.config file. Which is all well and good, but that means it makes it a bit annoying when moving between development and production.

Dealing with Dev/Live settings in web.config

One option is to have different web.config files - perhaps web.config.live / web.config.dev, and the publishing process picks and renames the correct file as appropriate. An alternative is for a post-build process to replace the connection string in the web.config file.

For this particular project, I was already using SQLServer in combination with NHibernate. In the web.config, I have two connection string properties - one for live and one for dev. On app start-up, it looks at the machine and then decides which connection string property to use. So I decided to take a similar approach for Elmah.

Extending SqlErrorLog

It proved incredibly simple. I simply extended the existing SqlErrorLog class calling the base constructor with the appropriate connection string name:

using System;
using System.Collections;

namespace MyWebApp.ElmahConfig
{
  public class SqlErrorLog : Elmah.SqlErrorLog
  {
    private static IDictionary configForEnvironment(IDictionary originalConfig)
    {
      if (!originalConfig.Contains("connectionStringName")) return originalConfig;
      var connectionStringName = originalConfig["connectionStringName"] as string;
      if (connectionStringName == null) return originalConfig;

      var localAndLiveConnectionStrings = connectionStringName.Split(';');
      if (localAndLiveConnectionStrings.Length < 2) return originalConfig;

      return new Hashtable() {
        { "connectionStringName", localAndLiveConnectionStrings[IsLocalEnvironment ? 0 : 1] }
      };
    }

    private static bool IsLocalEnvironment
    {
      get { return Environment.MachineName.EndsWith("_Dev", StringComparison.InvariantCultureIgnoreCase); }
    }

    public SqlErrorLog(IDictionary config) : base(configForEnvironment(config)) { }

    public SqlErrorLog(string connectionString) : base(connectionString) { }
  }
}

As you can see, it has the two constructors simply calling the base class, but in the case we're using a connection string name, we intercept the name, extracting either the live or dev/local one depending on the machine name. Of course, this is by no means full proof. If someone else's machine is named differently, or a machine name changes, this method could point to the live connection string incorrectly.

To finish off, the web.config change is simply as follows:

<connectionStrings>
  <add name="mywebapp.elmah.local" connectionString="..."/>
  <add name="mywebapp.elmah.live" connectionString="..."/>
</connectionStrings>
<elmah>
  <errorLog type="MyWebApp.ElmahConfig.SqlErrorLog, MyWebApp" connectionStringName="mywebapp.elmah.local;mywebapp.elmah.live" />
</elmah>

As mentioned, it's a bit crude, but is simple. No post-build scripts or special copy instructions required - just "xcopy deployment" and you can F5 locally and it just works.


Comment Guidelines
See the FAQ for details on the full rules and guidelines. No Spam. Write clearly and thoughtfully - no bad language.