Search code examples
c#asp.net-corenlog

Why does NLog work in controllers and not in Program.cs?


I've added NLog to my MVC Core 2.2 project and everything works fine except application startup logging. Logs are written well in controllers but not in Program.cs. Here is my main method, all NLog stuff was taken from NLog documentation :

public static void Main(string[] args)
{
    // NLog: setup the logger first to catch all errors
    var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
    try
    {
        logger.Info("Starting app");
        CreateWebHostBuilder(args).Build().Run();
    }
    catch (Exception ex)
    {
        // NLog: catch setup errors
        logger.Error(ex, "App failed to start");
        throw;
    }
    finally
    {
        // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
        NLog.LogManager.Shutdown();
    }
}

NLog own logs show the following:

Error Error has been raised. Exception: System.ArgumentException: The path is not of a legal form. at NLog.Targets.FileTarget.Write(LogEventInfo logEvent) at NLog.Targets.Target.Write(AsyncLogEventInfo logEvent)

which is weird because logs are written correctly afterwards in the file path I defined.

Here is my nlog.config :

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true"
      internalLogLevel="Info"
      internalLogFile="c:\temp\nlog.log"
      throwConfigExceptions="true">

  <!-- enable asp.net core layout renderers -->
  <extensions>
    <add assembly="NLog.Web.AspNetCore"/>
  </extensions>

  <variable name="logFilePath" value="${configsetting:name=Logging.LogFilePath}"/>

  <targets>
    <target xsi:type="File"
            name="PortailUsagersCore"
            fileName="${logFilePath}"
            layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|controller: ${aspnet-mvc-controller}|action: ${aspnet-mvc-action}"
            maxArchiveFiles="4"
            archiveNumbering="Rolling"
            archiveAboveSize="2097152" />
  </targets>

  <rules>
    <logger name="*" writeTo="PortailUsagersCore" />
  </rules>
</nlog>

LogFilePath is defined in appsettings.json and appsettings.development.json in logging section :

"Logging": {
    "LogFilePath": "C:\\Logs\\Portail\\PortailUsagersCore.log",
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Trace",
      "Microsoft": "Information"
    }
  }

Solution

  • I managed to make it work, thanks to RolfKristensen suggestions. Appsettings is now read before NLog initialization, so NLog is now able to read the filepath in it. I used NLog.GlobalDiagnosticsContext to set the filePath in NLog.config.

    string env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
    
    // Load config file to read LogFilePath
    var config = new ConfigurationBuilder()
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env}.json", optional: true, reloadOnChange: true)
        .Build();
    
    // LogFilePath is read in nlog.config
    NLog.GlobalDiagnosticsContext.Set("LogFilePath", config["Logging:LogFilePath"]);
    
    // NLog: setup the logger first to catch all errors
    var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
    

    And in my NLog.config, I use it this way :

    <variable name="logFilePath" value="${gdc:item=LogFilePath}"/>
    <targets>
        <target xsi:type="File"
                name="PortailUsagersCore"
                fileName="${logFilePath}"