In ASP.NET Core development, logging plays a pivotal role in the lifecycle of an application. Among the various logging frameworks available, NLog stands out with its robustness, flexibility, and ease of use. This article delves into the advanced features of NLog in the context of an ASP.NET Core application. We’ll explore features such as structured logging, log filtering, exception logging, file archiving, and more, providing you with the knowledge to fully leverage the power of NLog.
Table of Contents:
NLog Core Features
NuGet Package Manager Installation
Configuration of NLog.Config
How to write logs using NLog
Different log levels in NLog
Configuration Using NLog.Config File
Logging with NLog in C#
Structured Logging: Explanation and examples
Log Filtering: How to filter logs based on level, logger, and message
Exception Logging: Detailed logging of exceptions
File Archiving: How to archive log files automatically
Programmatically Finding NLog Targets
Setting Up NLog Programmatically
Implementing Log Rotation
Database Setup for NLog
Connection String and Parameter Properties
Parameterized Query for Security
Additional Database Options: Logging to MySQL, Oracle, and SQLite
How to use NLog with the built-in ILogger interface
Configuring NLog provider for ASP.NET Core
Introduction to Asynchronous Logging
Configuration of AsyncWrapper
Let's begin!
Introduction to NLog
NLog is an open-source logging platform for .NET applications. It allows developers to log the activity of an application, making the application’s code easier to handle. NLog provides cross-platform support and has rich log routing and management capabilities. It supports structured logs and multiple logging targets.
Logging in ASP.NET Core is a crucial aspect of any application’s lifecycle. It uses the same logging mechanism as .NET Core logging. Efficient logging can greatly assist in debugging and maintaining the application’s health. Implementing best practices for logging, such as consistent formatting and appropriate log levels, enhances the effectiveness and manageability of logs. Logging is especially important when working with ASP.NET Core as it helps developers understand the flow of execution and locate any errors or issues that might occur. It’s highly recommended to implement logging in ASP.NET Core applications.
NLog Core Features
NLog is a popular logging framework for .NET applications. Here are some of its core features:
Ease of Use: NLog is simple to get started with. It takes just 3-4 lines of code to begin logging with this framework.
Template Rendering: NLog gives you complete control to structure the logging messages via your own defined templates. This allows for highly customizable and informative log messages.
Free to Use: NLog is an open-source project, making it completely free to use. This makes it a great choice for projects of all sizes.
Structured Logging: NLog supports structured logging, allowing for complex data types to be logged and easily queried later. This feature is at its best in NLog.
Well-Maintained Documentation: NLog has comprehensive and well-maintained documentation. This ensures you can always find the information you need when using this framework.
Settings up NLog in ASP.NET Core
Setting up NLog in ASP.NET Core involves two main steps:
NuGet Package Manager Installation
Configuration of NLog.config
1. NuGet Package Manager Installation
You can install the necessary packages using either the NuGet Package Manager visual interface or the NuGet Package Manager Console. The packages you need to install are:
NLog.Web.AspNetCore
NLog.Extensions.Logging
NLog.Config
2. Configuration of NLog.config
Once you finish installing NLog.Config, a file named NLog.config will be automatically referenced into your project. It’s worth noting that NLog.Config is not unique to NLog. This means you can use either config mode or code-based mode for configuration.
Basic Logging with NLog
To write logs using NLog, you first need to create an instance of the Logger class. Then, you can call one of its methods (Trace, Debug, Info, Warn, Error, Fatal) to write a log.
var logger = NLog.LogManager.GetCurrentClassLogger();
logger.Info("This is an informational message");
Different Log Levels
NLog supports six levels of logging. Each level represents the severity of the log messages. Here they are, listed in order of increasing severity:
Trace: This is the most verbose logging level. It’s typically used for detailed debugging information that you would only want to be available when diagnosing problems.
Debug: This level is used for debugging information, less detailed than trace, that you might want to enable in a non-production environment.
Info: This level is often used for useful information about the normal operation of your application.
Warn: Warning messages highlight unexpected events, errors, or problems that might cause issues but allow the application to continue running.
Error: Error messages highlight when the current operation or request has failed, such as an exception that was not handled.
Fatal: The Fatal level is the least verbose and represents very severe error events that will presumably lead the application to abort.
Trace is the most verbose and Fatal is the least verbose. You choose the level based on the importance and severity of the log message. The appropriate level depends on the context and how important the information is for diagnosing issues or understanding the application’s activities.
Configuration Using NLog.Config File
The NLog.config file is a crucial component of the NLog logging framework. It’s an XML file that defines how NLog should log messages. The file primarily consists of two sections:
Targets
Rules
Targets: A target represents a location where your logs will be written. This could be a file, a database, an email, or even the console. Each target has a unique name and type, along with other properties that depend on the type of the target.
<targets>
<target name="logfile" xsi:type="File" fileName="file.txt" />
</targets>
In this example, a target named “logfile” is defined. It’s a file target (as indicated by xsi:type="File"), and it will write logs to a file named file.txt.
Rules: Rules define which logger(s) should write to which target(s) and at what level.
<rules>
<logger name="*" minlevel="Info" writeTo="logfile" />
</rules>
In this example, the rule applies to all loggers (as indicated by name="*"). It states that any log events with level Info or higher should be written to the “logfile” target.
The NLog.config file provides a flexible way to control how your logs are written. You can specify different targets and rules to suit your needs. It’s worth noting that NLog also supports code-based configuration, which can be used in conjunction with or as an alternative to file-based configuration.
Here’s an basic example:
<?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">
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<targets>
<target name="logfile" xsi:type="File" fileName="D:\\logs\\LogMessages-${shortdate}.log" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="logfile" />
</rules>
</nlog>
Logging with NLog in a Controller
Once NLog is configured, you can use it in your controllers to log messages. Here’s an example of how to do this:
public class HomeController : Controller
{
Logger _logger = (Logger)LogManager.GetCurrentClassLogger(typeof(Logger));
public IActionResult Index()
{
_logger.Info("Application started");
return View();
}
// Other action methods
}
Logger Initialization:
A Logger object is created using the LogManager.GetCurrentClassLogger(typeof(Logger)) method provided by NLog. This Logger object is used to log messages. The Logger object is cast to its concrete type (Logger) for additional features that might not be available through the interface.
Logging in Action Methods:
Inside the Index action method, the Info method of the Logger object is being called to log an informational message. The string "Application started" is the message that will be logged. The Info method corresponds to the Info log level, which is typically used for informational messages that highlight the progress of the application at coarse-grained level.
Advanced NLog Features
Below are the advanced NLog features in ASP.NET Core:
1. Structured Logging:
Structured logging is a method of logging that captures logs in a way that makes them easy to process and query. It involves logging the entire data object instead of just the message. This allows you to log complex data types and easily search and filter your logs. When this log is written, the actual orderId will be captured along with the log message.
Here’s an example:
logger.Info("Processing order {orderId}", order.Id);
In this example, orderId is a structured property. When this log is written, the actual orderId will be captured along with the log message.
2. Log Filtering:
NLog allows you to filter logs based on level, logger, and message. This can be done in the NLog configuration file. This feature is useful when you want to focus on specific logs that meet certain criteria. For example, you can ignore any log message over 100 characters in length.
Here’s an example:
<rules>
<logger name="*" minlevel="Info" writeTo="file">
<filters>
<when condition="length('${message}') > 100" action="Ignore" />
</filters>
</logger>
</rules>
In this example, any log message over 100 characters in length will be ignored.
3. Exception Logging:
NLog can log detailed information about exceptions. This is crucial for debugging and understanding the state of the application when the exception occurred. The exception details and the order id are logged if an error occurs.
Here’s an example:
try
{
// Some code...
}
catch (Exception ex)
{
logger.Error(ex, "An error occurred while processing order {orderId}", order.Id);
}
In this example, the exception details and the order id are logged if an error occurs.
4. File Archiving:
NLog can automatically archive log files. This can be configured in the NLog configuration file. This feature is useful for managing log files and preventing them from consuming too much disk space. For example, log files can be archived daily, and a maximum of 7 archived files are kept.
Here’s an example:
<targets>
<target xsi:type="File" name="file" fileName="logs/${shortdate}.log"
archiveFileName="archives/${shortdate}.{#}.txt"
archiveEvery="Day"
archiveNumbering="Rolling"
maxArchiveFiles="7" />
</targets>
In this example, log files are archived daily, and a maximum of 7 archived files are kept.
5. Programmatically Finding NLog Targets:
You can find and manipulate NLog targets programmatically. This gives you more control over your logging configuration and allows you to change it based on the needs of your application. For example, the file target’s filename can be changed programmatically.
Here’s an example:
var fileTarget = (FileTarget)LogManager.Configuration.FindTargetByName("file");
fileTarget.FileName = "${basedir}/newfile.log";
LogManager.ReconfigExistingLoggers();
In this example, the file target’s filename is changed programmatically.
Configuring NLog with Code
NLog can be configured programmatically in addition to using the NLog.config file. This involves
Setting up NLog
Implementing log rotation.
Setting Up NLog Programmatically
You can configure NLog by calling the API interface provided by NLog. Here’s an example of how to do this:
private static void ConfigureNLog()
{
var logConfiguration = new LoggingConfiguration();
var dbTarget = new DatabaseTarget();
dbTarget.ConnectionString = "Data Source=JOYDIP; initial catalog=NLogDemo; User Id=sa; Password=sa1@3#.;";
dbTarget.CommandText = "INSERT INTO DbLog (level, callsite, message, logdatetime)" +" Values(@level, @callsite, @message, @logdatetime)";
dbTarget.Parameters.Add(new DatabaseParameterInfo("@level", "${level}"));
dbTarget.Parameters.Add(new DatabaseParameterInfo("@callSite", "${callSite}"));
dbTarget.Parameters.Add(new DatabaseParameterInfo("@message", "${message}"));
dbTarget.Parameters.Add(new DatabaseParameterInfo("@logdatetime","${date:s}"));
var rule = new LoggingRule("*", LogLevel.Debug, dbTarget);
logConfiguration.LoggingRules.Add(rule);
LogManager.Configuration = logConfiguration;
}
In this example, a DatabaseTarget is being configured programmatically. The ConnectionString, CommandText, and parameters for the database target are being set, and a LoggingRule is being added to the LoggingConfiguration.
Implementing Log Rotation
NLog supports automatic log rotation. This means that NLog can be configured to only save logs for nearly n hours and automatically delete logs older than n hours. This feature is practical as it prevents the log files from consuming too much disk space. Here’s an example of how to configure automatic log rotation:
<?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">
<targets>
<target name="logfile"
xsi:type="File"
fileName="${basedir}/logs/App.log"
layout="${longdate} ${message}"
archiveFileName="${basedir}/logs/archive.{#}.log"
archiveEvery="Day"
archiveNumbering="Rolling"
maxArchiveFiles="7"
concurrentWrites="true"
keepFileOpen="true" />
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="logfile" />
</rules>
</nlog>
In this example, the archiveEvery attribute is set to "Day", which means the logs will be archived every day. The maxArchiveFiles attribute is set to 7, which means a maximum of 7 archived files will be kept.
Logging to database
NLog supports logging to a database. This involves setting up the database, configuring the connection string and parameter properties, using parameterized queries for security, and considering additional database options.
1. Database Setup for NLog
You can use NLog to log messages into a database. The following SQL script is used to create a log table:
CREATE TABLE [dbo].DbLog NOT NULL,
[Level] varchar NULL,
[CallSite] varchar NULL,
[Message] varchar NULL,
[AdditionalInfo] varchar NULL,
[LogDateTime] [datetime] NOT NULL,
CONSTRAINT [PK_DbLogs] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
2. Connection String and Parameter Properties
Next, you need to specify the database connection string in the target of NLog. Here’s how it’s configured:
<target name="database" xsi:type="Database" keepConnection="true" useTransactions="true" dbProvider="System.Data.SqlClient" connectionString="data source=localhost;initial catalog=NLogDemo;integrated security=false;persist security info=True;User ID=sa;Password=sa1@3#." commandText="INSERT INTO DbLog (level, callsite, message, additionalInfo, logdatetime) Values (@level, @callsite, @message, @additionalInfo, @logdatetime)">
3. Parameterized Query for Security
Finally, use a parameterized query to prevent injection attacks. Here’s how it’s done:
<parameter name="@level" layout="${level}" />
<parameter name="@callSite" layout="${callsite}" />
<parameter name="@message" layout="${message}" />
<parameter name="@additionalInfo" layout="${var:AdditionalInfo}" />
<parameter name="@logdatetime" layout="${date:s}" />
Complete NLog Configuration
Here’s a complete NLog configuration file for reference:
<?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">
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<variable name="AdditionalInfo" value=""/>
<targets>
<target name="database" xsi:type="Database" keepConnection="true" useTransactions="true" dbProvider="System.Data.SqlClient" connectionString="data source=localhost;initial catalog=NLogDemo;integrated security=false;persist security info=True;User ID=sa;Password=sa1@3#." commandText="INSERT INTO DbLog (level, callsite, message, additionalInfo, logdatetime) Values (@level, @callsite, @message, @additionalInfo, @logdatetime)">
<parameter name="@level" layout="${level}" />
<parameter name="@callSite" layout="${callsite}" />
<parameter name="@message" layout="${message}" />
<parameter name="@additionalInfo" layout="${var:AdditionalInfo}" />
<parameter name="@logdatetime" layout="${date:s}" />
</target>
</targets>
<rules>
<logger levels="Debug,Info,Error,Warn,Fatal" name="databaseLogger" writeTo="database"/>
</rules>
</nlog>
Additional Database Options
NLog supports logging to various types of databases. This flexibility allows you to choose a database that best suits your application’s needs. Here are some additional database options you can consider:
MySQL: MySQL is a popular open-source relational database management system. To log to MySQL, you would need to install the NLog.Targets.MySql NuGet package and configure the DatabaseTarget.
Oracle: Oracle Database is a multi-model database management system produced and marketed by Oracle Corporation. To log to Oracle, you would need to install the NLog.Targets.Oracle NuGet package and configure the DatabaseTarget.
SQLite: SQLite is a C library that provides a lightweight disk-based database. It doesn’t require a separate server process and allows accessing the database using a nonstandard variant of the SQL query language. To log to SQLite, you would need to install the NLog.Targets.SQLite NuGet package and configure the DatabaseTarget.
Integrating NLog with ASP.NET Core
NLog can be integrated with ASP.NET Core by following these steps:
Add NLog dependencies: Begin by adding the required NLog dependencies to your project. In Visual Studio, you can use the NuGet Package Manager to search for and install the following packages: NLog, NLog.Web.AspNetCore.
Configure NLog: Next, create an nlog.config file at the root of your project. This file will define the configuration for NLog, including log targets and rules.
Configure NLog in the ASP.NET Core app: To configure NLog in your ASP.NET Core application, open the Program.cs file and modify as follows:
using NLog.Web;
var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
try
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
// ...
}
Using NLog with the built-in ILogger interface
You can use NLog with the built-in ILogger interface in ASP.NET Core. This allows you to leverage the abstraction provided by ILogger while using NLog as the logging implementation.
Here’s an example of how to do this:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
_logger.LogInformation("Hello, this is the index!");
return View();
}
}
In this example, ILogger<HomeController> is being injected into the HomeController. This ILogger instance can then be used to log messages.
Configuring NLog provider for ASP.NET Core
To add NLog as one of the logging providers, you need to modify the CreateHostBuilder method in the Program.cs file. The below code snippet will enable NLog logging provider:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddNLog(hostingContext.Configuration.GetSection("Logging"));
});
In this example, logging.AddNLog is used to add NLog as a logging provider. The hostingContext.Configuration.GetSection("Logging") part is used to get the logging configuration from the appsettings.json file.
Improving Performance with AsyncWrapper
NLog supports a variety of targets, such as
AsyncWrapper
BufferingWrapper
FallbackGroup
RetryingWrapper.
Among these, AsyncWrapper is used to improve performance by queuing messages and extracting queue messages from multiple threads.
Introduction to Asynchronous Logging
Asynchronous logging can help improve the performance of your application by offloading the I/O operations to a separate thread. This means your application doesn’t have to wait for the log message to be written before it can continue with the next operation.
Configuration of AsyncWrapper
You can configure AsyncWrapper in the NLog.config file. Here’s an example of how to do this:
<targets>
<target xsi:type="AsyncWrapper"
name="String"
queueLimit="Integer"
timeToSleepBetweenBatches="Integer"
batchSize="Integer"
overflowAction="Enum">
<target ... />
</target>
</targets>
In this example, an AsyncWrapper target is being configured. The queueLimit attribute specifies the maximum number of log messages that can be queued. The timeToSleepBetweenBatches attribute specifies the time to wait (in milliseconds) between processing batches of log messages. The batchSize attribute specifies the maximum number of log messages to be processed in a single batch. The overflowAction attribute specifies what action to take when the queue is full.
Here’s an example of how to configure AsyncWrapper for asynchronous logging:
<targets>
<target name="asyncFile" xsi:type="AsyncWrapper">
<target xsi:type="File" name="fileLog"
fileName="${basedir}/Logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}"/>
</target>
</targets>
<rules>
<logger levels="Debug,Info,Error,Warn,Fatal" writeTo="asyncFile"/>
</rules>
In this example, an AsyncWrapper target named “asyncFile” is being configured. It wraps a File target named “fileLog”, which writes log messages to a file. The log messages are formatted with a layout that includes the date, log level, and message.
In addition to this method, you can also mark all targets as asynchronous by setting the async attribute to true on the targets element, as shown in the following configuration:
<targets async="true">
... Write your targets here ...
</targets>
That's It!
Comments