EPiServer - wrapper for log4net with dependency injection

Introduction

Dependency injection is supported in new logging api. You can read more on the following link: http://thisisnothing.nystrom.co.nz/2014/11/24/a-new-logging-api-in-episerver-framework/

However, I wanted to create my own logging wrapper with dependency injection support that allows me to log complex objects as json serialized values.

Let's say we have a Person class defined like this:

public class Person
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

We could log data like this:

public class StartPageController : PageControllerBase<StartPage>
{
    private readonly ILogWrapper _log;

    public StartPageController(ILogWrapper log)
    {
        _log = log;
    }

    public ActionResult Index(StartPage currentPage)
    {
        var person = new Person
        {
            Id = Guid.NewGuid(),
            FirstName = "John",
            LastName = "Doe"
        };

        _log.InfoFormat("Person: {0}", person);
		
		...
	}
}

And have it logged like this:

2015-05-18 11:48:25,665 [75] INFO Alloy.Controllers.StartPageController: Person: {"Id":"3c2d50f0-6672-424c-b70a-82561bdf1020","FirstName":"John","LastName":"Doe"}

I use this a lot when making integrations with external systems (payment providers, CRMs, etc.)

The idea is to log as much data as possible (user requests, response objects from external services, etc.) in human readable format, and JSON serialized values are perfect for that.

Code

public interface ILogWrapper
{
    void Info(string message);
    void InfoFormat(string format, params object[] args);
    void Error(string message);
    void ErrorFormat(string format, params object[] args);
}

public class LogWrapper : ILogWrapper
{
    private readonly ILog _log;

    public LogWrapper(Type type)
    {
        _log = LogManager.GetLogger(type);
    }

    public void Info(string message)
    {
        if (_log.IsInfoEnabled)
        {
            _log.Info(message);
        }
    }

    public void InfoFormat(string format, params object[] args)
    {
        if (_log.IsInfoEnabled)
        {
            _log.InfoFormat(format, GetLogFriendlyArgs(args));
        }
    }

    public void Error(string message)
    {
        if (_log.IsErrorEnabled)
        {
            _log.Error(message);
        }
    }

    public void ErrorFormat(string format, params object[] args)
    {
        if (_log.IsErrorEnabled)
        {
            _log.ErrorFormat(format, GetLogFriendlyArgs(args));
        }
    }

    /// <summary>
    /// Converts class types to json strings
    /// </summary>
    private static object[] GetLogFriendlyArgs(object[] args)
    {
        var logFriendlyArgs = new List<object>();
        if (args.Length > 0)
        {
            for (int i = 0; i < args.Length; i++)
            {
                if (args[i] == null)
                {
                    logFriendlyArgs.Add("null");
                }
                else if (BuiltinType(args[i]))
                {
                    logFriendlyArgs.Add(args[i]);
                }
                else
                {
                    logFriendlyArgs.Add(JsonConvert.SerializeObject(args[i]));
                }
            }
        }
        return logFriendlyArgs.ToArray();
    }

    private static bool BuiltinType(object item)
    {
        if (item == null)
        {
            return false;
        }

        var ns = item.GetType().Namespace;
        return ns != null && ns.StartsWith("System");
    }
}

If you're using Alloy template, open Business / Initialization / DependencyResolverInitialization.cs, find private static void ConfigureContainer(ConfigurationExpression container) method, and add the following code:

//Implementations for custom interfaces can be registered here.
container.For<ILogWrapper>()
		 .AlwaysUnique()
		 .Use(x => x.ParentType == null
					? new LogWrapper(x.BuildStack.Current.ConcreteType)
					: new LogWrapper(x.ParentType));

That's it. Happy logging :)

comments powered by Disqus