Slab Logging with an IoC Container

Slab Logging or Semantic Logging is part of the Microsoft Enterprise Library 6. In this post, a wrapper framework will be used to log and use EventSource classes. The EventSource can then be used with an IoC container without requiring static constructors. We don’t want to create a wrapper class for each EventSource. Static methods will be avoided. The logging assembly is not coupling to the other assemblies and can be replaced easily if required. Automatic testing is also less complicated.

Code: https://github.com/damienbod/SlabLoggingWithIoCContainer

The application is a simple MVC 5 application. Unity bootstrapper is added for the dependency injection.

slab1

We also add the Semantic Logging.

slab2

Now we can create a new class library which will be used to define our log EventSources. We want a simple interface. As we need to define a unique EventId for each Event, will will also use this to set the log. Then we pass the message.

namespace Damienbod.Slab
{
    public interface ISlabLogger
    {
        void Log(int log, string message);
    }
}

A wrapper class is required for each event source because the EventSource classes must be a singleton within an application.

using System;
using System.Collections.Generic;
using Damienbod.Slab.Events;

namespace Damienbod.Slab.Loggers
{
    public class GlobalLogger
    {
        public void RegisterLogger(Dictionary<int, Action<string>> exectueLogDict)
        {
            exectueLogDict.Add(GlobalType.GlobalCritical, Critical);
            exectueLogDict.Add(GlobalType.GlobalError, Error);
            exectueLogDict.Add(GlobalType.GlobalInformational, Informational);
            exectueLogDict.Add(GlobalType.GlobalLogAlways, LogAlways);
            exectueLogDict.Add(GlobalType.GlobalVerbose, Verbose);
            exectueLogDict.Add(GlobalType.GlobalWarning, Warning);
        }

        public void Critical(string message)
        {
            GlobalEvents.Log.Critical(message);
        }

        public void Error(string message)
        {
            GlobalEvents.Log.Error(message);
        }

        public void Informational(string message)
        {
            GlobalEvents.Log.Informational(message);
        }

        public void LogAlways(string message)
        {
            GlobalEvents.Log.LogAlways(message);
        }

        public void Verbose(string message)
        {
            GlobalEvents.Log.Verbose(message);
        }

        public void Warning(string message)
        {
            GlobalEvents.Log.Warning(message);
        }
    }
}

This class uses application unique EventIds and has a special method RegisterLogger. This method adds Actions to a Dictionary using the EventIds as the key.

public void RegisterLogger(Dictionary<int, Action<string>> exectueLogDict)
{
    exectueLogDict.Add(GlobalType.GlobalCritical, Critical);
    exectueLogDict.Add(GlobalType.GlobalError, Error);
    exectueLogDict.Add(GlobalType.GlobalInformational, Informational);
    exectueLogDict.Add(GlobalType.GlobalLogAlways, LogAlways);
    exectueLogDict.Add(GlobalType.GlobalVerbose, Verbose);
    exectueLogDict.Add(GlobalType.GlobalWarning, Warning);
}

So lets create a EventSource class:

using System.Diagnostics.Tracing;

namespace Damienbod.Slab.Events
{
    [EventSource(Name = "GlobalEvents")]
    public class GlobalEvents : EventSource
    {
        public static readonly GlobalEvents Log = new GlobalEvents();
 
        private const int logInfoMessageEventId = 9;

        [Event(logInfoMessageEventId, Message = "{0}", Level = EventLevel.Informational)]
        public void LogInfoMessage(string message)
        {
            if (IsEnabled()) WriteEvent(logInfoMessageEventId, message);
        }

        [Event(GlobalType.GlobalCritical, Message = "Global Critical: {0}", Level = EventLevel.Critical)]
        public void Critical(string message)
        {
            GlobalEvents.Log.LogInfoMessage("dddd");
            if (IsEnabled()) WriteEvent(GlobalType.GlobalCritical, message);
        }

        [Event(GlobalType.GlobalError, Message = "Global Error {0}", Level = EventLevel.Error)]
        public void Error(string message)
        {
            if (IsEnabled()) WriteEvent(GlobalType.GlobalError, message);
        }

        [Event(GlobalType.GlobalInformational, Message = "Global Informational {0}", Level = EventLevel.Informational)]
        public void Informational(string message)
        {
            if (IsEnabled()) WriteEvent(GlobalType.GlobalInformational, message);
        }

        [Event(GlobalType.GlobalLogAlways, Message = "Global LogAlways {0}", Level = EventLevel.LogAlways)]
        public void LogAlways(string message)
        {
            if (IsEnabled()) WriteEvent(GlobalType.GlobalLogAlways, message);
        }

        [Event(GlobalType.GlobalVerbose, Message = "Global Verbose {0}", Level = EventLevel.Verbose)]
        public void Verbose(string message)
        {
            if (IsEnabled()) WriteEvent(GlobalType.GlobalVerbose, message);
        }

        [Event(GlobalType.GlobalWarning, Message = "Global Warning {0}", Level = EventLevel.Warning)]
        public void Warning(string message)
        {
            if (IsEnabled()) WriteEvent(GlobalType.GlobalWarning, message);
        }
    }
}

This is then used in the implementation of the Logger interface. We can define n-Logger implementations or if needed n-Logger interfaces with different implementations. These classes can then register any EventSources required in the RegisterLog method.

using System;
using System.Collections.Generic;
using Damienbod.Slab.Loggers;
using Microsoft.Build.Framework;

namespace Damienbod.Slab
{
    public class WebLogger : ISlabLogger
    {
        public WebLogger()
        {
            RegisterLog();
        }

        private readonly Dictionary<int, Action<string>> _exectueLogDict = new Dictionary<int, Action<string>>();

        private void RegisterLog()
        {
            var globalLogger = new GlobalLogger();
            globalLogger.RegisterLogger(_exectueLogDict);

            var controllerLogger = new ControllerLogger();
            controllerLogger.RegisterLogger(_exectueLogDict);
        }


        public void Log(int log, string message)
        {
            if (_exectueLogDict.ContainsKey(log))
            {
                _exectueLogDict[log].Invoke(message);
                return;
            }
            
             _exectueLogDict[GlobalType.GlobalWarning].Invoke(message);
        }
    }
}

The Log method justs gets the action from the Dictionary and invokes it.

public void Log(int log, string message)
{
  if (_exectueLogDict.ContainsKey(log))
  {
       _exectueLogDict[log].Invoke(message);
       return;
  }

       _exectueLogDict[GlobalType.GlobalWarning].Invoke(message);
 }

To use the logger, it is registered with unity.

public static void RegisterTypes(IUnityContainer container)
{
  container.RegisterType<ISlabLogger, WebLogger>();
}

And it is used in the HomeController:

   private readonly ISlabLogger _logger;
 
   public HomeController(ISlabLogger logger)
   {
        _logger = logger;
   }

   public ActionResult About()
   {
        ViewBag.Message = "Your application description page.";
        _logger.Log(GlobalType.GlobalCritical, "Hello World");
        return View();
    }

In the Log method the const ints are used and not raw ints.

namespace Damienbod.Slab
{
    // Every const has a unique id. 
    public class GlobalType
    {
        public const int GlobalInformational = 1;
        public const int GlobalCritical = 2;
        public const int GlobalError = 3;
        public const int GlobalLogAlways = 4;
        public const int GlobalVerbose = 5;
        public const int GlobalWarning = 6;

    }

    public class WebType
    {
        public const int ControllerInformational = 7;
        public const int ControllerCritical = 8;
        public const int ControllerError = 9;
        public const int ControllerLogAlways = 10;
        public const int ControllerVerbose = 11;
        public const int ControllerWarning = 12;
    }
}

This logger is simple to use in any injection container. If a Logger was required in an attribute, property injection could be used.

The application uses SLAB OUT-OF-PROCESS Logging, that’s why only the EventSource classes and the Logs are required.

Links:

Enterprise Library 6, Semantic Logging, Part 1, database listener
Enterprise Library 6, Semantic Logging, Part 2, OUT-OF-PROCESS
Enterprise Library 6, Semantic Logging, Part 3, Getting into the details
Enterprise Library 6, Semantic Logging, Part 4 advantages, customising

http://blogs.msdn.com/b/vancem/archive/2012/08/13/windows-high-speed-logging-etw-in-c-net-using-system-diagnostics-tracing-eventsource.aspx

http://msdn.microsoft.com/en-us/library/dn170426%28v=pandp.60%29.aspx

http://blogs.msdn.com/b/dotnet/archive/2013/08/09/announcing-the-eventsource-nuget-package-write-to-the-windows-event-log.aspx

One comment

  1. … [Trackback]

    […] Read More Infos here: damienbod.wordpress.com/2013/10/25/slab-logging-with-an-ioc-container/ […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: