NLog in .Net Applications
The source code can be obtained from: https://github.com/ALightTechnologyAndServicesLimited/NLogSampleDemo001
NLog is a structured logging framework for .Net. NLog is highly extensible, configurable and supports lot of plugins. This post is about some basic usage of NLog, we won’t be discussing any of the advanced features. NLog can be added via nuget package. There are several ways to configure NLog, the documentation can be found here.
NLog supports several built-in Targets such as Console, File, Database etc…, the list of layouts are found here. Some of my favorite targets are: Console, File, Database, BufferingWrapper, AsyncWrapper, AspNetBufferingWrapper, FallbackGroup, RetryingWrapper, ElasticSearch, EventLog, FluentD, MicrosoftTeams, Amazon CloudWatch, Amazon SNS, Amazon SQS, Microsoft Azure ApplicationInsights, Microsoft Azure Blob Storage, Microsoft Azure DataTables Storage, Microsoft Azure FileSystem, Microsoft Azure Queue Storage, Memory, Network, WebService, MessageBox and many more. All the targets are very well documented.
Layouts allow custom layout of log messages and can be viewed here.
Layout Renders are very useful and can be used to determine exactly what information needs to be captured and can be found here. Determining the proper information to capture helps in troubleshooting issues.
In this post, we are going to discuss how to integrate NLog into a console application and how to inject logger into a class for use.
We start off by adding the required nuget packages:
NLog
NLog.Extensions.Logging
Microsoft.Extensions.Configuration.Json
Microsoft.Extensions.DependencyInjection
//packages can be added using the syntax below from .Net CLI
> dotnet add package <package_name>
// optionally the version can be specified via --version flag
// As of the date of this blog post, the following commands would add the latest packages.
> dotnet add package NLog --version 5.0.1
> dotnet add package NLog.Extensions.Logging --version 5.0.0
> dotnet add package Microsoft.Extensions.Configuration.Json --version 7.0.0-preview.5.22301.12
> dotnet add package Microsoft.Extensions.DependencyInjection --version 7.0.0-preview.5.22301.12
Next we will create a nlog.config file with the following content:
<?xml version="1.0" encoding="utf-8" ?>
<!-- XSD manual extracted from package NLog.Schema: https://www.nuget.org/packages/NLog.Schema-->
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogFile="c:\temp\logs\nlog-internal.log"
internalLogLevel="Info" >
<!-- the targets to write to -->
<targets>
<!-- write logs to file -->
<target xsi:type="File" name="logfile" fileName="c:\temp\logs\logs.log"
layout="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}" />
<target xsi:type="Console" name="logconsole"
layout="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}" />
</targets>
<!-- rules to map from logger name to target -->
<rules>
<logger name="*" minlevel="Trace" writeTo="logfile,logconsole" />
</rules>
</nlog>
I personally prefer creating environment specific nlog.config files, so a file with the name nlog.Development.config can be created for Development specific configuration. We will see some code snippets further in this blog for using environment specific config file. Remember to set the copyToOutput value to Always.
Now we will add a sample Interface and called IMyInterface
public interface IMyInterface
{
void PrintHello();
}
Now will add a class that implements IMyInterface known as MyClass
public class MyClass : IMyInterface
{
private readonly ILogger<MyClass> logger;
public MyClass(ILogger<MyClass> logger)
{
this.logger = logger;
logger.LogDebug("Constructor Invoked.");
}
public void PrintHello()
{
logger.LogDebug("PrintHello Invoked.");
Console.WriteLine("Hello!");
}
}
Now for the main, The following list of usings are used.
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NLog;
using NLog.Extensions.Logging;
Now the actual code:
var logger = LogManager.GetCurrentClassLogger();
try
{
var environment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
var configBuilder = new ConfigurationBuilder()
.SetBasePath(System.IO.Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
var nlogConfigFileName = "nlog.config";
if (!String.IsNullOrEmpty(environment))
{
configBuilder.AddJsonFile($"appsettings.json", optional: true, reloadOnChange: true);
nlogConfigFileName = $"nlog.{environment}.config";
}
var config = configBuilder.Build();
var servicesProvider = new ServiceCollection()
.AddTransient<IMyInterface, MyClass>()
// Add Additional Services i.e bind interfaces to classes
.AddLogging(loggingBuilder =>
{
// configure Logging with NLog
loggingBuilder.ClearProviders();
loggingBuilder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
loggingBuilder.AddNLog(nlogConfigFileName);
})
.BuildServiceProvider();
using(servicesProvider as IDisposable)
{
var myInterface = servicesProvider.GetRequiredService<IMyInterface>();
myInterface.PrintHello();
}
}
catch(Exception e)
{
logger.Error(e, "Stopped program because of exception");
throw;
}
finally
{
LogManager.Shutdown();
}
In the above code, we assume DOTNET_ENVIRONMENT environment variable holds information about the environment. ASPNETCORE_ENVIRONMENT environment variable is used for ASP.Net core applications. Another assumption is that the appsetting.[ENVIRONMENT].json holds environment specific information that needs to be over-written and of course, we specified the appsettings.[ENVIRONMENT].json file as optional.
We are specifying the nlog.[ENVIRONMENT].config to be used for NLog logging. If needed, some additional code can be added for verifying the file exists. This way we can have seperate NLog config files for each environment.
Now using dependency injection ILogger<classname> can be injeced into the constructor of the class and the logger gets injected through dependency injection. For example:
using Microsoft.Extensions.Logging;
public class MyClass : IMyInterface {
private ILogger<MyClass> logger;
public MyClass(ILogger<MyClass> logger){
this.logger = logger;
logger.LogDebug("Message");
}
}
The source code can be obtained from: https://github.com/ALightTechnologyAndServicesLimited/NLogSampleDemo001
NLog in .Net Applications