skip to Main Content

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:

AppFolderPathlogmyLogName1log_25-04-2022.txt
AppFolderPathlogmyLogName2log_25-04-2022.txt
AppFolderPathlogmyLogName3log_25-04-2022.txt

But i can see only one file

AppFolderPathloglog_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.

2

Answers


  1. Chosen as BEST ANSWER

    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);
    

  2. 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="..."

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search