'Create sub-folder that contains log-files for each request

I have an application that listens for a hash input, processes that input, and serves it back. I would like to create a separate log directory for each input with the following structure:

- hashcode1
    - input1.txt
    - output1.txt
    - log1.txt
- hashcode2
    - input2.txt
    - output2.txt
    - log2.txt

I've looked into the NLog library but it seems the config requires a hardcoded log location, whereas my implementation requires the hashcode as an input. How can I achieve this with NLog, or any other logging library?



Solution 1:[1]

You can create the NLog configuration from code. This allows you to dynamically create targets in your processing loop for the appropriate log outputs:

using NLog;
using NLog.Targets;

// Init the config if we don't have a nlog.config
LogManager.Configuration = new();

// Do the work
var work = new List<string> { "5dd2ec2f", "24e1e841", "05137e70" };
work.ForEach(ProcessHash);

void ProcessHash(string hash)
{
  // Create dynamic configuration and logger
  var fileTarget = new FileTarget(hash) { FileName = $"{hash}/log.txt" };
  LogManager.Configuration.AddRuleForAllLevels(fileTarget, hash);
  LogManager.ReconfigExistingLoggers();
  var logger = LogManager.GetLogger(hash);

  // Process...
  logger.Info($"Processing hash {hash}");

  /* ... your code here ... */

  // Remove the logging target
  LogManager.Configuration.RemoveTarget(fileTarget.Name);
}

Solution 2:[2]

You can create a scope-property like this:

void ProcessHashCode(string hashInput)
{
    using (NLog.MappedDiagnosticsLogicalContext.SetScoped("HashCode", hashInput)) 
    {
        NLog.LogManager.GetLogger("HashInput").Info(hashInput);
        NLog.LogManager.GetCurrentClassLogger().Info("Received Hashcode");
        var hashOutput = hashInput.GetHashCode();
        NLog.LogManager.GetLogger("HashOuput").Info(hashOutput);
    }
}

Then you can do this in NLog.config

<nlog>
   <variable name="logdir" value="${mdlc:HashCode:whenEmpty=${basedir}}" />
   <targets>
      <target name="hashinput" type="file" filename="${logdir}/input.txt" />
      <target name="hashouput" type="file" filename="${logdir}/output.txt" />
      <target name="hashlog" type="file" filename="${logdir}/log.txt" />
   </target>
   <rules>
      <logger name="HashInput" writeTo="hashinput" final="true" />
      <logger name="HashOuput" writeTo="hashouput" final="true" />
      <logger name="*" writeTo="hashlog" />
   </rules>
</nlog>

See also: https://github.com/NLog/NLog/wiki/MDLC-Layout-Renderer

Alternative you can use Logger.WithProperty("HashCode", hashInput) like this:

void ProcessHashCode(string hashInput)
{
    var inputLogger = NLog.LogManager.GetLogger("HashInput").WithProperty("HashCode", hashInput);
    var outputLogger = 
NLog.LogManager.GetLogger("HashOutput").WithProperty("HashCode", hashInput);
    var hashLogger = 
NLog.LogManager.GetCurrentClassLogger.WithProperty("HashCode", hashInput);

    inputLogger.Info(hashCode);
    hashLogger.Info("Received Hashcode");
    var hashOutput = hashInput.GetHashCode();
    outputLogger.Info(hashOutput);
}

And instead use variable like this:

<variable name="logdir" value="${event-properties:HashCode:whenEmpty=${basedir}}" />

See also: https://github.com/NLog/NLog/wiki/Context#logevent-properties

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Jann Westermann
Solution 2