Home > front end >  NLog: how to specify log folder at runtime
NLog: how to specify log folder at runtime

Time:04-26

For an example, i have following code (ASP.NET core 6):

Program.cs:

//...
var config = new NLog.Config.LoggingConfiguration();
var logfile = new NLog.Targets.FileTarget("logfile") 
    {
        FileName = Path.Combine(AppFolderPath, "log", "${var:myLogName}", "log_${date:format=dd-MM-yyyy}.txt") 
    };
config.AddRule(NLog.LogLevel.Trace, NLog.LogLevel.Fatal, logfile);
LogManager.Configuration = config;
//...

Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    //...
    services.AddSingleton<ISomeClass, SomeClass>(_ => new SomeClass(LogManager.GetCurrentClassLogger()));
}

SomeClass.cs:

public class SomeClass(ILogger logger)
{
    _logger = logger;
}

private ILogger _logger;

... then somewhere in class:

foreach(var a in b)
{
    var thread = Task.Run(async () =>
    {
        var myLogName = a.Name;
        _logger.Trace("Hello!");
    }
}

My goal is to create all subfolders for each started thread with myLogName as result:

AppFolderPath\log\myLogName1\log_25-04-2022.txt
AppFolderPath\log\myLogName2\log_25-04-2022.txt
AppFolderPath\log\myLogName3\log_25-04-2022.txt

But i can see only one file

AppFolderPath\log\log_25-04-2022.txt

So myLogName variable is just skipped and this file contains one log string "Hello!" per started thread. How can i configure NLog to make folders as i want? Only by using GDC and making extension-methods with updating global resource each time i want write a log?

UPD1: solution with GDC works, but its not so thread-safe, i can see that some log files contains many strings (expected only 1) and some threads were'nt created any log files:

public static class LoggerExtensions
{
    public static NLog.ILogger WithMyLog(this NLog.ILogger logger, string myLogName)
    {
        GlobalDiagnosticsContext.Set("myLogName", myLogName);
        return logger;
    }
}

... and then:

foreach(var a in b)
{
    var thread = Task.Run(async () =>
    {
        var myLogName = a.Name;
        _logger.WithMyLog(myLogName).Trace("Hello!");
    }
}

Very expected tho, no questions to this mechanism. Maybe i should add some more extension methods and fill them with lock statements to make this work properly.

CodePudding user response:

The answer is very simple: just use structured logging! According to this NLog article, NLog 4.5 can use properties directly from arguments:

var logfile = new NLog.Targets.FileTarget("logfile") 
    {
        FileName = Path.Combine(AppFolderPath, "log", "${event-properties:item=myLogName}", "log_${date:format=dd-MM-yyyy}.txt") 
    };
config.AddRule(NLog.LogLevel.Trace, NLog.LogLevel.Fatal, logfile);

and then:

_logger.Trace("{myLogName}", a.Name);

CodePudding user response:

Maybe consider using ${logger} or ${threadName}

Example:

var logfile = new NLog.Targets.FileTarget("logfile") 
    {
        FileName = Path.Combine(AppFolderPath, "log", "${logger}", "log_${date:format=dd-MM-yyyy}.txt") 
    };

By specifying a custom token in the Logger-Name-Filter, then you can ensure only special "thread-loggers" will reach the "logfile". Ex:

config.AddRule(NLog.LogLevel.Trace, NLog.LogLevel.Fatal, logfile, "*CustomToken*");

Then just ensure the Logger-Name contains the token:

public void ConfigureServices(IServiceCollection services)
{
    //...
    services.AddSingleton<ISomeClass, SomeClass>(_ => new SomeClass(LogManager.GetLogger("SomeClassCustomToken")));
}

If you place the custom-token as prefix (Ex. "CustomToken.SomeClass"), then you can strip it away using ${logger:shortName=true} in FileName="..."

  • Related