Logging .NET to AWS CloudWatch: Using NLog

In the first post of this series, we used a low-level, first-principles approach to log messages to AWS CloudWatch. Now that we understand how the logging works under the hood, we are now going to see how we can configure NLog to log to AWS CloudWatch. This is recommended over trying to write your own logger as NLog is thoroughly tested and has error handling for when it writes to AWS CloudWatch.

Logging .NET to AWS CloudWatch Series

Video

Prerequisites

AWS Account

You will need an active AWS account that we can use to push logs from our .NET application to AWS CloudWatch. Our volumes will be minimal so we shouldn't exceed the free tier for AWS CloudWatch.

If you don't have an AWS account yet, you can sign up for one here.

AWS Profile

Since this article is just focusing on AWS CloudWatch, we won't be configuring any AWS credentials in our .NET application. Rather, we will let the AWS SDK automatically use our AWS profile that is set up when running the AWS CLI. This simplifies the demonstrations as we don't have to fumble around with permissions and credentials.

However, please note that you should always make sure to give applications their own IAM roles/users in production with minimal permissions according to AWS best practices.

Let's Go!

With that out the way, let's get started!

1. Create a new Console application

We will run this demo in a .NET 6 Console application. You can create it using either Visual Studio 2022 or the .NET 6 CLI. I will be using Visual Studio 2022:

Create a .NET 6 Console application

2. Add the NLog package and set up a logger

Let's first add the NLog Nuget package and set up a basic logger. Afterwards, we'll configure this logger to also log to AWS CloudWatch.

Install the NLog Nuget package (4.7.13 at the time of writing):

Install the NLog Nuget package

We can now configure NLog and log some messages to our Console to test the configuration:

Program.cs

using NLog;
using NLog.Config;
using NLog.Targets;

// Setup the NLog configuration
var config = new LoggingConfiguration();
config.AddRule(LogLevel.Trace, LogLevel.Fatal, new ConsoleTarget());

LogManager.Configuration = config;

// Create a new logger and test it
var log = LogManager.GetCurrentClassLogger();

log.Trace("Writing introduction message...");

log.Info("Hi there! How are you?");

log.Trace("Wrote introduction message!");

You should see the following output:

Output after configuring our logger initially.

3. Install the AWS.Logger.NLog

AWS provides a nice NLog Target library that allows NLog to log directly to AWS CloudWatch. Let's install it now (we are installing 3.0.0 at the time of writing):

4. Setup the AWS CloudWatch Target on our logger

We can now create an NLog Target for AWS CloudWatch and our logger should start logging in there as well!

Program.cs

using NLog;
using NLog.AWS.Logger;
using NLog.Config;
using NLog.Targets;

// Setup the NLog configuration
var config = new LoggingConfiguration();
config.AddRule(LogLevel.Trace, LogLevel.Fatal, new ConsoleTarget());

// Add the AWS Target with minimal configuration
config.AddRule(LogLevel.Trace, LogLevel.Fatal, new AWSTarget()
{
    LogGroup = "/dotnet/logging-demo/nlog"
});

LogManager.Configuration = config;

// Create a new logger and test it
var log = LogManager.GetCurrentClassLogger();

log.Trace("Writing introduction message...");

log.Info("Hi there! How are you?");

log.Trace("Wrote introduction message!");

If you run the program, you should see the following logs appear in our AWS CloudWatch:

5. (Optional) Customising the AWS CloudWatch Target

We can customise the configuration for the AWS Target for NLog. Let's look at some of the configuration options available to us:

Program.cs

using Amazon.Runtime;
using NLog;
using NLog.AWS.Logger;
using NLog.Config;
using NLog.Layouts;
using NLog.Targets;

// Setup the NLog configuration
var config = new LoggingConfiguration();
config.AddRule(LogLevel.Trace, LogLevel.Fatal, new ConsoleTarget());

// Add the AWS Target with minimal configuration
config.AddRule(LogLevel.Trace, LogLevel.Fatal, new AWSTarget()
{
    LogGroup = "/dotnet/logging-demo/nlog",
    // (Optional) How often batches of log messages should be pushed to AWS CloudWatch
    BatchPushInterval = TimeSpan.FromSeconds(3),
    // (Optional) Whether NLog should try create the log group if it does not exist or not
    DisableLogGroupCreation = false,
    // (Optional) The maximum number of log events that are kept in memory before being pushed to
    // AWS CloudWatch
    MaxQueuedMessages = 10000,
    // (Optional) A string to prefix log stream names with
    LogStreamNamePrefix = "Prefix",
    // (Optional) A string to suffix log stream names with
    LogStreamNameSuffix = "Suffix",
    // (Optional) AWS credentials that should be used to log into AWS and access AWS CloudWatch
    // Credentials = new BasicAWSCredentials(accessKey, secretKey), (Optional) The region that AWS
    // CloudWatch logs should be logged in. In this case it defaults to the AWS Profile's region
    Region = "af-south-1",
    // (Optional) Custom layout that should be used when formatting log messages
    Layout = new SimpleLayout("[${date} ${level:uppercase=true:truncate=3}] ${message} ${exception}")
});

LogManager.Configuration = config;

// Create a new logger and test it
var log = LogManager.GetCurrentClassLogger();

log.Trace("Writing introduction message...");

log.Info("Hi there! How are you?");

log.Trace("Wrote introduction message!");

Closing

As you can see, logging to AWS CloudWatch using existing .NET logging libraries is straightforward. In this article, we've demonstrated how easy it is to integrate AWS CloudWatch with NLog. This is especially useful when you have an application that already uses NLog.

If you would like to find out more about NLog then you can view their documentation here. You can also find out more about the AWS Target for NLog here.S