How To Write To Your Episerver Logs When Using A Shared Code Library

If you work Episerver in a digital agency, or, for a client who has different brands, a good goal to shoot for is to follow the DRY principle and be ruthless to the amount of duplicate code you write.  If you're working on multiple Episerver project and need to share code between them then a very common thought might be to create a custom shared code library (usually through a local NuGet feed on your build server).

This idea is good, however, when you come to try and log errors you hit a snag.  Website A uses EPiserver 7, which uses Log4NET version x and Website B uses Episerver 11 using Log4NET version.  If you've ever stumbled onto this issue, then it can be a pain.  In today's post, I'm going to give you my best tips on how you can tackle this problem, so let's get started.

How Can I Deal With Shared Code With Different Dependencies

If you want to create a shared code library, my advice is to figure this out before you start coding. From my experience there are a few main options to deal with logging:

  • Use a third-party logging library, like NLog, that isn't being used by any project
  • Write something custom
  • A logging service
  • Common.Logging with a custom Episerver log appender 

Everyone will have their own preferences as to which approach they think better.  Using something like NLog is probably the quickest and easiest.  If you're using a service like Episerver DXC you may want to create your own logging server so you can get instant access to your logs.

On a recent project, we decided to try the common.logging approach and for the remainder of this article, I'm going to cover my approach and my thoughts on it.

Implementing Common.Logging With Episerver

The Common.Logging provider was written to try and address exactly these type of issue.  When you work on multiple projects, frameworks etc.. it is very common that different frameworks will use different logging providers or different versions.  The Common.Logging library was developed to provide a simple abstraction to consumers of your code decide the type of logging implementation they want to choose.  For reference, the Common.Logging Nuget package can be found here.

What this means is that in your shared code library, you install the Common.Logging provider.  YIn this library ou use the common.logging log interface (very similar to the Log4Net one) to log your errors.  When you use that code in your Episerver website, you define some config in your web.config for the common.logger that defines the type of logging adaptor to use specific to that site.  

When you use the common.logger, you don't define the logging framework in your shared library, you define one in each website that will consume it.  What this means is that in each website that consumes your code, you will have a small amount of duplicate code that will define common logger for Episerver to use with common.logging.  

This the decoupled the log provider from your shared code base, so now don't need to worry about DLL assembly mismatches.  So the trace off of duplicate code in each solution means you can create a shared code library that will be easy to maintain and won't screw up your references in your web.config.

Implementing Common.Logging With Episerver

Admittedly all the info might be a little overwhelming to grasp at first (it was for me), so hopefully going over some code will make it a little easier.

This first bit of code defines a custom appender, the job of this class is to map the common.logging interface to the Episever logger.

    [Serializable]
    public class EpiserverLogAppender : AbstractLogger
    {
        private readonly EPiServer.Logging.Compatibility.ILog _logger;

        public CustomEpiserverLog4NetAppender()
        {
            _logger = EPiServer.Logging.Compatibility.LogManager.GetLogger(typeof(CustomEpiserverLog4NetAppender));
        }

        public CustomEpiserverLog4NetAppender(Type type)
        {
            _logger = EPiServer.Logging.Compatibility.LogManager.GetLogger(type);
        }

        public CustomEpiserverLog4NetAppender(string type)
        {
            _logger = EPiServer.Logging.Compatibility.LogManager.GetLogger(type);
        }

        public override bool IsTraceEnabled => false;

        public override bool IsDebugEnabled => _logger.IsDebugEnabled;

        public override bool IsInfoEnabled => _logger.IsInfoEnabled;

        public override bool IsWarnEnabled => _logger.IsWarnEnabled;

        public override bool IsErrorEnabled => _logger.IsErrorEnabled;

        public override bool IsFatalEnabled => _logger.IsFatalEnabled;

        protected override void WriteInternal(LogLevel logLevel, object message, Exception exception)
        {
            switch (logLevel)
            {
                case LogLevel.Fatal:
                    _logger.Fatal(message, exception);
                    break;
                case LogLevel.Debug:
                    _logger.Debug(message, exception);
                    break;
                case LogLevel.Error:
                    _logger.Error(message, exception);
                    break;
                case LogLevel.Info:
                    _logger.Info(message, exception);
                    break;
                case LogLevel.Warn:
                    _logger.Warn(message, exception);
                    break;
                default:
                    _logger.Error(message, exception);
                    break;
            }
        }
    }

You'll also need to create a factor adaptor. The adaptor is the thing that you point your common.logging settings within your web.config to, which looks like this:

    using Common.Logging;
    using System;

    [Serializable]
    public class EpiserverLoggerFactoryAdapter : ILoggerFactoryAdapter
    {
        public ILog GetLogger(Type type)
        {
            return new EpiserverLogAppender(type);
        }

        public ILog GetLogger(string key)
        {
            return new EpiserverLogAppender(key);
        }
    }

The last thing is to add some common.logging configuration within the web.config of each website that wants to consume your shared code, which looks like this:

 <configuration>
    <configSections>
      <sectionGroup name="common">
        <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging"/>
      </sectionGroup>
    </configSections>
    <common>
      <logging>
        <factoryAdapter type="MyCustomNamespace.EpiserverLoggerFactoryAdapter, MyAssembly" />
      </logging>
    </common>

That's it! Granted you need to copy these two files and add the config into each project that you want to consume your shared code library, but it does mean that any logs thrown in the shared code library will be logged within your Episerver logs AND you won't have any Nuget referencing hell with trying to include Log.

Common.Logging Takeaway

Common.Logging does provide a means to decouple a logging framework from your code. This can be handy if you want to have a shared code library that works between different websites. Granted the downside of this approach is that you need to add some custom code and some web.config tweaks into anything that you want to consume your shared code base, however, from experience this is a good trade-off to get around the dependencies issues that can result.

Simply using a third-party logging provide not used by EPiserverm like NLog would also get you around this problem.  As Episerver doesn't use NLog, you'd never get the same assembly version conflicts if all your website were using different versions of Episever. Using a sperate Log provide would mean that all your errors for the shared code would appear in a separate log file. As all the errors in that log would be generated by the shared code that isn't necessarily a bad thing depending on your situation.

I think my preference in most situations would be to lean towards NLog, as it's quicker, but using common.logging is also a good option.

 

submit to reddit

Jon D Jones

Software Architect, Programmer and Technologist Jon Jones is founder and CEO of London-based tech firm Digital Prompt. He has been working in the field for nearly a decade, specializing in new technologies and technical solution research in the web business. A passionate blogger by heart , speaker & consultant from England.. always on the hunt for the next challenge

Back to top