How can I log on multiple platform or targets ?
You have to use a logger provider.
There is a lot of logger provider: Serilog, Log4Net, NLog. In this article I’d like to show you how add NLog to your ASP.NET Core project in order to have a easy and configurable logger provider

Table of content

Prerequisites

  • A project targets to netcoreapp3.1 😉

NuGet Pacakges

  1. Add NLog NuGetPackage
  2. Add NLog.Web.AspNetCore NuGetPackage

Configure NLog

  1. Add a file called “nlog.config” to your root project folder: this file contains all nlog logging configuration
  2. Ensure this file has property “copy to output directory” set to “copy if newer
  3. Go to your program.cs file and add:
    1. On top of Main method: var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
    2. In the end of Main method: NLog.LogManager.Shutdown();
    3. It’s better if you protect you method with a try-catch-finally and put NLog.LogManager.Shutdown(); into finally
  4. Your Main method should look like this:
/// <summary>
/// entry mehod
/// </summary>
/// <param name="args"></param>
public static void Main(string[] args)
{
    // NLog: setup the logger first to catch all errors
    var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();

    try
    {
        var isDebugging = Debugger.IsAttached || args.Contains("--debug");
        var contentRoot = Directory.GetCurrentDirectory();

        var config = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json", optional: false);

        if (isDebugging)
            config.AddJsonFile("appsettings.development.json", optional: true);
        else
            config.AddJsonFile("appsettings.production.json", optional: true);

        var host = BuildHost(args, contentRoot);//.Build().Run();

        logger.Debug("init main");
        logger.Debug($"path to appsetting.json file set to: {System.IO.Path.GetDirectoryName(new System.Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath)}");

            Console.Title = "ASPNETCore";

            logger.Debug("Starting app");
            host.Run();
    }
    catch (Exception e)
    {
        //NLog: catch setup errors
        logger.Error(e, "Stopped program because of exception");
        throw;
    }
    finally
    {
        // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
        NLog.LogManager.Shutdown();
    }
}
  1. Change the default ” CreateHostBuilder ” with:
/// <summary>
/// create host CreateHostBuilder
/// </summary>
/// <param name="args"></param>
/// <param name="contentRoot"></param>
/// <returns></returns>
public static IHost BuildHost(string[] args, string contentRoot) =>
    Host.CreateDefaultBuilder(args)
        .UseContentRoot(contentRoot)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
            webBuilder.UseKestrel();
        })
        .ConfigureAppConfiguration((hostContext, c) =>
        {
            c.AddCommandLine(args);
        })
        .ConfigureLogging((hostContext, logging) =>
        {
            logging.AddConfiguration(hostContext.Configuration.GetSection("Logging"));
            logging.ClearProviders();
            logging.SetMinimumLevel(LogLevel.Trace);
        })
        .UseNLog()  // NLog: Setup NLog for Dependency injection
        .Build();
  1. Configure nlog.config file with targets and rules, like this one:
<?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=".\logs\internal-nlog.log">

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

  <!-- the targets to write to -->

  <variable name="fileLayout" value="${lowercase:${level}}|${longdate}:${newline}
caller:    ${callsite}${newline}
message:   ${message}${newline}
exception: ${exception:format=tostring,Data:maxInnerExceptionLevel=10:exceptionDataSeparator=\r\n"/>

  <variable name="consoleLayout" value="${lowercase:${level}}: ${callsite}
${message}${newline}
${exception:format=tostring,Data:maxInnerExceptionLevel=10:exceptionDataSeparator=\r\n"/>

  <targets>

    <target
      xsi:type="ColoredConsole"
      useDefaultRowHighlightingRules="false"
      name="console"
      layout="${consoleLayout}"
    >
      <highlight-row backgroundColor="Black" Condition="LogLevel.Fatal" foregroundColor="White"></highlight-row>
      <highlight-row backgroundColor="Black" Condition="LogLevel.Error" foregroundColor="White"></highlight-row>
      <highlight-row backgroundColor="Black" Condition="LogLevel.Warn" foregroundColor="White"></highlight-row>
      <highlight-row backgroundColor="Black" Condition="LogLevel.Info" foregroundColor="White"></highlight-row>
      <highlight-row backgroundColor="Black" Condition="LogLevel.Debug" foregroundColor="White"></highlight-row>
      <highlight-row backgroundColor="Black" Condition="LogLevel.Trace" foregroundColor="White"></highlight-row>

      <highlight-word foregroundColor="White" ignoreCase="true" regex="^debug"></highlight-word>
      <highlight-word foregroundColor="White" ignoreCase="true" regex="^trace"></highlight-word>
      <highlight-word foregroundColor="DarkGreen" ignoreCase="true" regex="^info"></highlight-word>
      <highlight-word foregroundColor="Yellow" ignoreCase="true" regex="^warn"></highlight-word>
      <highlight-word foregroundColor="Red" ignoreCase="true" regex="^error"></highlight-word>
      <highlight-word foregroundColor="White" backgroundColor="Red" ignoreCase="true" regex="^critical"></highlight-word>
      <highlight-word foregroundColor="Black" backgroundColor="Red" ignoreCase="true" regex="^fatal|^fail"></highlight-word>
    </target>

    <!-- write logs to file  -->
    <target
      xsi:type="File"
      name="ASPNETCoreFile"
      fileName="${basedir}\logs\${shortdate}\ASPNETCore.log"
      createDirs= "true"
      layout="${fileLayout}"
      archiveFileName="${basedir}/logs/archives/ASPNETCore.{#}.log"
      archiveNumbering="Date"
      archiveEvery="Day"
      archiveDateFormat="yyyyMMdd"
      keepFileOpen="true"
      openFileCacheTimeout = "30"
      maxArchiveFiles="7"
      />
  </targets>

  <!-- rules to map from logger name to target -->
  <rules>
    <!--trace inforamtion for debugging-->
    <logger name="*" minlevel="Trace" writeTo="console" />

    <!--All logs, including from Microsoft: decomment line below if you want to log all informations-->
    <!--<logger name="*" minlevel="Trace" writeTo="fullfile" />-->

    <!--Skip non-critical Microsoft logs and so log only own logs-->
    <logger name="Microsoft.*" maxLevel="Info" final="true" />
    <!-- BlackHole without writeTo -->

    <logger name="ASPNETCore.*" minlevel="Trace" writeTo="ASPNETCoreFile" />

  </rules>
</nlog>

Where:

  • variable name=”fileLayout” and variable name=”consoleLayout”: allow you to define a variable that you can re-use in your configuration file
  • targets: contains a list of target that allow you to log to “multiple output” at same time
  • target: define where and how you are logging. Each target has a property called “xsi:type” that determinate the output type (in this example I’ve used “ColoredConsole” and “File”)
    Each target has specific configuration
    • ColoredConsole: you can define colors, rules and so on
    • File: you can define the filename, directory, archive policy, retention policy.. in this example I’ve configured to archive every day and keep 7 days of archive log, after that the logs will automatically deleted
  • rules: this allows you to specify at namespace level where a namespace has to be logged (or skip some indesiderata logs)
  • more details are available at official NLog documentation

For example this configuration produce this console logs:

And this file log:

trace|2019-12-24 16:54:49.5485:
 caller:    ASPNETCore.Program.Main
 message:   trace log
 exception: 
debug|2019-12-24 16:54:49.5485:
 caller:    ASPNETCore.Program.Main
 message:   debug log
 exception: 
info|2019-12-24 16:54:49.5485:
 caller:    ASPNETCore.Program.Main
 message:   Info log
 exception: 
warn|2019-12-24 16:54:49.5485:
 caller:    ASPNETCore.Program.Main
 message:   warn log
 exception: 
error|2019-12-24 16:54:49.5485:
 caller:    ASPNETCore.Program.Main
 message:   error log
 exception: 
fatal|2019-12-24 16:54:49.5485:
 caller:    ASPNETCore.Program.Main
 message:   fatal log
 exception: 
 

trace|2019-12-24 16:54:59.1965:
 caller:    ASPNETCore.Services.LoginService.Authenticate
 message:   level trace
 exception: 
info|2019-12-24 16:54:59.2055:
 caller:    ASPNETCore.Services.LoginService.Authenticate
 message:   level information
 exception: 
warn|2019-12-24 16:54:59.2055:
 caller:    ASPNETCore.Services.LoginService.Authenticate
 message:   level warning
 exception: 
error|2019-12-24 16:54:59.2055:
 caller:    ASPNETCore.Services.LoginService.Authenticate
 message:   level error
 exception: 
fatal|2019-12-24 16:54:59.2055:
 caller:    ASPNETCore.Services.LoginService.Authenticate
 message:   level critical
 exception: 

Use NLog in your code

  1. Inject ILogger<T> into your class
  2. Log

For example:

using Microsoft.Extensions.Logging;

/// <summary>
/// Implements ILoginService
/// </summary>
public class LoginService: ILoginService
{
    private readonly IConfiguration _config;
    private readonly ILogger<LoginService> _logger;

    /// <summary>
    /// ctor
    /// </summary>
    /// <param name="config"></param>
    /// <param name="logger"></param>
    public LoginService(IConfiguration config, ILogger<LoginService> logger)
    {
        _config = config;
        _logger = logger;
    }

    /// <summary>
    /// MyMethod
    /// </summary>
    public bool MyMethod()
    {
        _logger.LogTrace("level trace");
        _logger.LogInformation("level information");
        _logger.LogWarning("level warning");
        _logger.LogError("level error");
        _logger.LogCritical("level critical");

    }
}

Source Code

Leave a Comment