1
votes

I am creating a logging library with NLog and have requirement for setting the logger based on different terminals. What I have done is to create a ILoggerFacade implementation and have the filenames passed as constructor injected with the filename used to create the log file.

Here is my code:

internal static class FileLogFactory
{
    #region "Private Static Members"

    private static ILog _applicationLog;
    private static Dictionary<string, ILog> _terminalLogs;
    private static object _syncLock = new object();

    #endregion

    #region "Constant Fields"

    private const string _APPLICATION_LOG = "applicationLog";
    private const string _TERMINAL_LOG = "terminalHandlerLog";
    private const string _TERMINAL_PREFIX = "filenameprefix";

    public const string APPLICATION = "Application";

    #endregion

    #region "Static Cosntructors"

    static FileLogFactory()
    {
        _terminalLogs = new Dictionary<string, ILog>();
    } 

    #endregion

    #region "Static Methods"

    internal static ILog CreateLoggerInstance(string filename)
    {
        lock (_syncLock)
        {
            if (string.IsNullOrEmpty(filename) || filename == APPLICATION)
            {
                // Application Log requested.
                if (_applicationLog == null)
                {
                    _applicationLog = LogManager.GetLogger(_APPLICATION_LOG);
                }

                return _applicationLog;
            }

            // terminal log requested, check if already created else create new.
            if (!_terminalLogs.ContainsKey(filename))
            {
                NLog.GlobalDiagnosticsContext.Set(_TERMINAL_PREFIX, filename);
                _terminalLogs.Add(filename, LogManager.GetLogger(_TERMINAL_LOG));
            }

            return _terminalLogs[filename];
        }
    } 

    #endregion
}

And Following are my NLog config

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <!-- 
  See https://github.com/nlog/nlog/wiki/Configuration-file 
  for information on customizing logging rules and outputs.
   -->
  <variable name="logpath" value="c:/Log"/>
  <targets>
    <!-- add your targets here -->
    
     <target xsi:type="File" 
             name="applicationTarget"
             layout="${longdate}|${uppercase:${level}}|${threadid}| ${message}"
             archiveAboveSize="1024000"
             fileName="${logpath}/Application.${shortdate}.log"
             />

    <target xsi:type="File"
            name="terminalHandlerTarget"
            layout="${longdate}|${uppercase:${level}}|${threadid}| ${message}"
            archiveAboveSize="1024000"
            fileName="${logpath}/${gdc:item=filenameprefix}.${shortdate}.log"/>
    
  </targets>

  <rules>
    <!-- add your logging rules here -->
    <logger name="applicationLog" minlevel="Trace" writeTo="applicationTarget" />
    <logger name="terminalHandlerLog" minlevel="Trace" writeTo="terminalHandlerTarget" />
  </rules>
</nlog>

The idea is to pass the gdc:item=filenameprefix from code and create log file for each terminal. Now whats happening is GetLogger returns the same object terminalHandlerLog and hence the last filename given takes the preference and from thereon writes always into that file.

I beleive this is because GetLogger follows singleton patterns and creates single object for each logger.

How can this be overriden, is there any setting which could be utilised for this?

1

1 Answers

1
votes

When using the GetLogger method on NLog you get the instance you have configured in your configuration. If you need multiple configuration like writing to different files which are known at runtime, you need to create the log instances in code:

if (!_terminalLogs.ContainsKey(filename))
{
    var config = LogManager.Configuration;
    var fileTarget = new FileTarget();
    fileTarget.FileName = filename;
    fileTarget.Layout = "${longdate}|${uppercase:${level}}|${threadid}| ${message}";
    fileTarget.ArchiveAboveSize = 1024000;

And then add the logger to your collection:

    _terminalLogs.Add(filename, _fileTarget);
    config.AddTarget(filename, fileTarget);
}