Search code examples
c#loggingnlog.net-4.8

How to retrieve a MemoryTarget Logs property in a thread-safe manner in NLog while it is being written to


I'm using a MemoryTarget Logger in my code which will be fed from another thread.

From time to time, I need to provide the logs to some webapi calls, so I need to retrieve the content of the Logs property.

The problem is, the Logs property is an IList backed by a List. So while writing to the log remain thread safe through NLog's help. I can't access the content of the logs while they are being writen to, and I cannot control when or where people are writting to the logs and there doesn't seem to be a thread safe option to retrieve the data.

I'm using the .Net 4.8 Framework.

The current simplified code

public const string filter = "theFilterHere";

public string TargetName { get; set; }

public MemoryTarget Logger { get; private set; }

public void InitLog() {
  Logger = new MemoryTarget();
  var rule = new LoggingRule(filter, LogLevel.Debug, Logger);
                    LogManager.Configuration.AddTarget(TargetName, Logger);
                    LogManager.Configuration.LoggingRules.Add(rule);
                    LogManager.ReconfigExistingLoggers();
}

public List<string> Logs { 
  // Crash happens here. I need the list in reverse order because reasons, but looking at it I know I would have the same problem if I did a ToList first.
  return Logger.Logs.Reverse().ToList();
}

I know using the SyncRoot of Logs won't help because I do not lock write access to the logs. The only idea I have today is casting the Logs (IList) as a List, then doing a code similar to :

List<string> currentLogs = Logger.Logs as List<string>;
if (currentLogs is null) {
  // NLog changed the internal structure of MemoryTarget.
  return Logger.Logs.Reverse().ToList();
}
int count = currentLogs.Count;
string[] logs = new string[count];

// Assumption : the list doesn't shrink, the log will only grow and never empty.
// this is only available on List<T>, and internally seems to do an Array copy of the _items array.
currentLogs.CopyTo(0, logs, 0, count);

Am I on the right track or am I hopelessly wrong ?


Solution

  • NLog v5.2.6 has been released that allows thread-safe enumeration of the NLog MemoryTarget Logs-property:

    See also: https://github.com/NLog/NLog/pull/5409