Skip to content
Rolf Kristensen edited this page Oct 5, 2023 · 150 revisions

This describes NLog for .NET Framework (.NET ver. 3.5 - 4.8) and .NET Core (NetStandard 1.3+)

NLog can be setup with the following steps:

  1. Install NLog nuget package
  2. Configure NLog Targets for output
  3. Writing log messages

After having setup NLog, then make sure to explore:

If something is not working as expected then check the Troubleshooting section.

If wanting to use Microsoft Extension Logging (MEL) then check .NET Core and ASP.NET Core tutorials.

Configure NLog Targets for output

NLog will only produce output if having configured one (or more) NLog targets.

NLog can be configured using XML by adding a NLog.config file to your application project (File Properties: Copy If newer). NLog will automatically load the NLog.config by searching multiple file locations. This is a simple example of the content for 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">

    <targets>
        <target name="logfile" xsi:type="File" fileName="file.txt" />
        <target name="logconsole" xsi:type="Console" />
    </targets>

    <rules>
        <logger name="*" minlevel="Info" writeTo="logconsole" />
        <logger name="*" minlevel="Debug" writeTo="logfile" />
    </rules>
</nlog>

Alternative one can configure programmatically by doing this in code:

var config = new NLog.Config.LoggingConfiguration();

// Targets where to log to: File and Console
var logfile = new NLog.Targets.FileTarget("logfile") { FileName = "file.txt" };
var logconsole = new NLog.Targets.ConsoleTarget("logconsole");
            
// Rules for mapping loggers to targets            
config.AddRule(LogLevel.Info, LogLevel.Fatal, logconsole);
config.AddRule(LogLevel.Debug, LogLevel.Fatal, logfile);
            
// Apply config           
NLog.LogManager.Configuration = config;

NLog 5.0 introduces a Fluent-Configuration-API so you can do this instead:

NLog.LogManager.Setup().LoadConfiguration(builder => {
   builder.ForLogger().FilterMinLevel(LogLevel.Info).WriteToConsole();
   builder.ForLogger().FilterMinLevel(LogLevel.Debug).WriteToFile(fileName: "file.txt");
});

The rules redirects logger output to the wanted output targets. The rules also provides filtering options to output only relevant logevents. Usually based on logger-name and loglevel (Ex. name="*" minlevel="Trace" means everything).

See also Configuration File or Configure from code

See also Available NLog Targets for output.

Writing log messages

public static class Program
{
    private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();

    public static void Main()
    {
        try
        {
           Logger.Info("Hello world");
           System.Console.ReadKey();
        }
        catch (Exception ex)
        {
           Logger.Error(ex, "Goodbye cruel world");
        }
    }
}  

The Logger can write messages with different LogLevels, which is used by the logging-rules (See minLevel in configuration example above) so only relevant messages are redirected to the wanted targets. The LogLevel identifies how important/detailed the message is. NLog can route log messages based primarily on their logger name and log level.

The log levels ordered by severity:

LogLevel Severity Typical Use
Trace Most verbose level. Used for development and seldom enabled in production. Ex. Request-payload, Response-payload, Begin-method-X or End-method-X
Debug Debugging the application behavior from internal events of interest. Ex. Executed query, User authenticated, Session expired
Info Information that highlights progress or application lifetime events.
Warn Warnings about validation issues or temporary failures that can be recovered.
Error Errors where functionality has failed or Exception have been caught.
Fatal Most critical level. Application is about to abort.

The logger is not tied to a specific target. The messages written to one logger can reach multiple targets based on the logging-rules configuration. Maintaining this separation lets you keep logging statements in your code and easily change how and where the logs are written, just by updating the configuration in one place. See also Filtering log messages.

NLog is also supported by many logging abstractions like Microsoft Extension Logging and LibLog.

Layouts and LayoutRenderers

NLog allows you to customize how log messages are written to a NLog target. This shows the default SimpleLayout used by most NLog targets:

<target name="logfile" xsi:type="File" fileName="file.txt" layout="`${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}`" />

This can be configured to include extra context information (Ex ${threadid}):

<target name="logfile" xsi:type="File" fileName="file.txt" layout="${longdate}|${level:uppercase=true}|${logger}|${threadid}|${message}|${exception:format=tostring}" />

See full list here: Layout Renderers

One can also use a more complex Layout instead of SimpleLayout:

See full list here: Layouts

Target Wrappers

NLog supports special kinds of targets which do not do any logging by themselves, but which modify the behavior of other loggers. Those targets are called wrappers. The most commonly used ones are:

See full list here: Target Wrappers

There is a shortcut for enabling the AsyncWrapper for all targets, by adding async="true":

<?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">

    <targets async="true">
        <target name="logfile" xsi:type="File" fileName="file.txt" />
    </targets>

    <rules>
        <logger name="*" minlevel="Info" writeTo="logfile" />
    </rules>
</nlog>

This will make all writes to the file be asynchronous, which will improve responsiveness of the calling thread.

Best practices for using NLog

1. Logger should be a static variable in each class

When using LogManager.GetCurrentClassLogger:

Creating a new Logger has a small overhead, as it has to acquire locks and allocate objects.

Therefore it is recommended to create the logger like this:

namespace MyNamespace
{
  public class MyClass
  {
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
  }
}

2. Logger should handle string formatting

Avoid performing string allocation or string concatenation upfront, but instead let the Logger do the formatting. This will allow NLog to defer the formatting and improve performance.

Structured logging is recommended, e.g.

logger.Info("Hello {Name}", "Earth");

Or using string-format syntax:

logger.Info("Hello {0}", "Earth");

3. Logger should be given the Exception

Avoid giving the Exception as formatting parameter, but instead provide it explicitly as first parameter. This will help the NLog targets to provide better logging.

try
{
}
catch (Exception ex)
{
    logger.Error(ex, "Something bad happened");
}

4. Validate XML configuration from NLog.config

NLog swallows by default all exceptions, so problems with logging will not cause the application to break. But for many application the logging is very critical, so if the initial NLog configuration fails, then it is fatal.

Adding throwConfigExceptions="true" will make NLog complain when something is wrong with the configuration:

<?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"
      throwConfigExceptions="true">
</nlog>

There is also a setting called throwExceptions="true", which should never be used in production as it can cause havoc for your application. It is intended for unit testing and local troubleshooting. See also Troubleshooting Configuration

It is also a good idea to check output from NLog InternalLogger, as it can give alerts when detecting problems with the NLog configuration.

5. Remember to Flush

NLog will by default attempt to flush automatically at application shutdown. Microsoft Windows give .NET applications a limited amount of time to perform shutdown (usually 2 sec) before being terminated. If having a NLog configuration with NLog Targets that requires network-traffic (Http, Mail, Tcp), then it is a really good idea to perform a manual Flush/Shutdown independent on running on Linux/Windows.

NLog.LogManager.Shutdown(); // Flush and close down internal threads and timers

NET Application running on Mono/Linux are required to stop threads/timers before entering application shutdown phase. Failing to do this will cause unhandled exceptions and segmentation faults, and other unpredictable behavior.

6. Environment specific target options

Most NLog-Target options supports NLog Layout, which means the NLog-Target options can be assigned from environment-variables, configuration-files or global diagnotic values.

Thus one only have to maintain a single NLog-configuration, and all environment-specific options can be extracted to a dedicated location. See also Environment specific NLog Logging Configuration

If wanting different output levels/targets depending on environment, then one can also use NLog Layout in the Logger-rules, thus having different minLevel="${configsetting:DefaultLevel:whenEmpty=Warn}" depending on environment (Where LogLevel Off can disable output).

Notice it can also be helpful to use NLog config variables when having multiple targets, that all define the same Layout for output. This also include more advanced Layouts like the JsonLayout, XmlLayout, CsvLayout or other.

Clone this wiki locally