Search code examples
c#nlogbuffering

NLog - Proper way of buffering


Question

How do we read the content of logging buffer in NLog?

Detail

I have configured a normal FileTarget and a BufferingTargetWrapper in my application. Every few minutes, my application runs a set of steps and logs some messages in the process. I want to keep buffering these messages and flush them at the end of the process. At the flush time, I also want to send all the messages from the current batch in an email. The buffer is then flushed to the main log file and is ready for the next batch.

I have configured my logger programmatically:

/// <summary>
/// Initializes global logging service
/// </summary>
private void InitLogger()
{
  var config = new LoggingConfiguration();

  var fileTarget = new FileTarget("MyService")
  {
    FileName = "${basedir}/myservice.log",
    Layout = "${longdate} ${level} ${message}  ${exception}"
  };
  config.AddTarget(fileTarget);

  var bufferTarget = new BufferingTargetWrapper("BufferLog", fileTarget);

  config.AddRuleForAllLevels(bufferTarget);

  LogManager.Configuration = config;
}

Here is a sample Test method that show exactly what I'm trying to achieve:

public static Logger Log => LogManager.GetLogger("BufferLog");

[TestMethod]
public void BufferedLogTest()
{
  InitLogger();

  Log.Info("Message 1");
  Log.Info("Message 2");
  Log.Info("Message 3");
  LogManager.Configuration.FindTargetByName<BufferingTargetWrapper>("BufferLog").Flush(new NLog.Common.AsyncContinuation((s) =>
  {
    //I want to send these 3 buffered messages through email and
    //flush them to the main log file, but s is null unfortunately
  }));
  Log.Info("Message 4");
  Log.Info("Message 5");
  Log.Info("Message 6");
  LogManager.Configuration.FindTargetByName<BufferingTargetWrapper>("BufferLog").Flush((s) =>
  {
    //I want to send next 3 buffered messages through email and
    //flush them to the main log file, but s is null again
  });
}

My expection was that BufferingTargetWrapper will hand over the content of its buffer to the target function, but it doesn't. There also isn't any other direct/indirect way of accessing the buffer.


Solution

  • Following @RolfKristensen's comment (thank you man), it turned out to be easier than I thought. I'm now creating two Targets in my NLog configuration. One keeps buffering log messages till I flush them manually at the end of each batch, while the other one keeps writing them to the main log file.

    In case this helps anyone, here's my programmatic way of doing this (you could do this through configuration files too):

    /// <summary>
    /// Initializes global logging service
    /// </summary>
    private void InitLogger()
    {
      // Create configuration object 
      var Config = new LoggingConfiguration();
    
      // Create main target that continuously writes to the log file
      var FileTarget = new FileTarget("FileTarget")
      {
        FileName = "${basedir}/myservice.log",
        Layout = "${longdate} ${level} ${message}  ${exception}"
      };
      Config.AddTarget(FileTarget);
    
      //Create memory target that buffers log messages
      var MemTarget = new MemoryTarget("MemTarget");
    
      // Define rules
      Config.AddRuleForAllLevels(MemTarget);
      Config.AddRuleForAllLevels(FileTarget);
    
      // Activate the configuration
      LogManager.Configuration = Config;    
    }
    

    I can now call fetch buffered messages at the end of each batch like this:

    var MemTarget = LogManager.Configuration.FindTargetByName<MemoryTarget>("MemTarget");
    //Do whatever you want with buffered messages in MemTarget.Logs
    MemTarget.Logs.Clear();