A shared logger

A simple implementation of the shared Logger based on NLog that can be easily included into any solution with a minimum configuration.

First, create a separate shared project and add the NLog and NLog.Config packages into it.

Next, configure added NLog.config according to the documentation. To have a possibility to change the log file path, use the variables. Here is an example of the NLog config:

<?xml version="1.0" encoding="utf-8"?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <targets>
    <target name="InfoTarget"
            xsi:type="File"
            fileName="${var:logPath}\Info.log"
            archiveFileName="${var:logPath}\Info_{#}.log"
            archiveEvery="Day"
            archiveNumbering="Rolling"
            maxArchiveFiles="30"
            layout="${longdate} ${uppercase:${level}} ${message}" />
    <target name="ErrorTarget"
            xsi:type="File"
            fileName="${var:logPath}\Error.log"
            archiveFileName="${var:logPath}\Error_{#}.log"
            archiveEvery="Day"
            archiveNumbering="Rolling"
            maxArchiveFiles="30"
            layout="${longdate} ${uppercase:${level}} ${message}" />
    <target name="DebugTarget"
            xsi:type="File"
            fileName="${var:logPath}\Debug.log"
            archiveFileName="${var:logPath}\Debug_{#}.log"
            archiveEvery="Day"
            archiveNumbering="Rolling"
            maxArchiveFiles="30"
            layout="${longdate} ${uppercase:${level}} ${message}" />
  </targets>
  <rules>
    <logger name="LoggerInfo" minlevel="Info" writeTo="InfoTarget" />
    <logger name="LoggerError" minlevel="Error" writeTo="ErrorTarget" />
    <logger name="LoggerDebug" minlevel="Debug" writeTo="DebugTarget" />
  </rules>
</nlog>

There are three loggers for info, error and debug messages and three corresponding targets which use the logPath variable to specify the directory of the log files.

Set the properties of NLog.config so it is not copied to the output directory, but included as an Embedded Resource.

The config should be read when the logger is created. Here how we can get it from the resources:

private static XmlLoggingConfiguration GetEmbeddedLoggerConfig()
{
    var asm = Assembly.GetExecutingAssembly();

    // In the example the NLog.config in in the root of the project.
    var resName = $"{asm.GetName().Name}.NLog.config";
    using (var stream = asm.GetManifestResourceStream(resName))
    {
        using (var reader = XmlReader.Create(stream))
        {
            return new XmlLoggingConfiguration(reader, null);
        }
    }
}

The next step is to get the logPath variable value from the app setting of the main config:

var logPath = ConfigurationManager.AppSettings["LogPath"];

That is the only thing that we need to configure in the project that uses our logger implementation.

Now let’s put all things together and finish the implementation of the logger itself:

public class Logger
{
    public Logger()
    {
        LogManager.Configuration = GetEmbeddedLoggerConfig();
        var logPath = ConfigurationManager.AppSettings["LogPath"];
        LogManager.Configuration.Variables["logPath"] = logPath;
    }
 
    private readonly Lazy<NLog.Logger> _infoLoggerLazy =
        new Lazy<NLog.Logger>(() => LogManager.GetLogger("LoggerInfo"));
 
    private readonly Lazy<NLog.Logger> _errorLoggerLazy =
        new Lazy<NLog.Logger>(() => LogManager.GetLogger("LoggerError"));
 
    private readonly Lazy<NLog.Logger> _debugLoggerLazy =
        new Lazy<NLog.Logger>(() => LogManager.GetLogger("LoggerDebug"));
 
    private NLog.Logger InfoLogger => _infoLoggerLazy.Value;
 
    private NLog.Logger ErrorLogger => _errorLoggerLazy.Value;
 
    private NLog.Logger DebugLogger => _debugLoggerLazy.Value;
 
    public void LogInfo(string infoMessage)
    {
        InfoLogger.Info(infoMessage);
    }
 
    public void LogError(string errorMessage)
    {
        ErrorLogger.Error(errorMessage);
    }
 
    public void LogDebug(string debugMessage)
    {
        DebugLogger.Debug(debugMessage);
    }
}

That’s it. Just reference the project with the Logger, set the LogPath setting in the App.config and use it.

previous post: ConfigEx 2.1.0