Add AWS CloudWatch logging to your .NET application

24th Nov 2021 ASP.NET, AWS

If, like me, you host Windows or Linux AWS EC2 instances which run one or more .NET applications, then chances are you’d like to be able to see details of any errors which may arise in your applications, without having to log in remotely to view log files, or hope that the error can just as easily be replicated in your local development environment.

This is where CloudWatch comes in, and it’s very simple to configure.

Assumptions

  • you’re moderately familiar with .NET Core or .NET 5+
  • you have an EC2 instance running, with your application already set up
  • you have an AWS account with access to the console, and
  • your account has the necessary permissions to edit IAM roles and create CloudWatch log groups.

Steps

  1. Add the applicable package to your application via NuGet
  2. Configure the application to include logging
  3. Configure the log group in AWS
  4. Add a CloudWatch policy to the EC2 instance’s role
  5. Configure the application to reference your new log group
  6. Add logging to a class using dependency injection
  7. Publish your application

Add the applicable package to your application via NuGet

Add the AWS.Logger.AspNetCore package to the project which you want to include logging on, via right click -> Manage NuGet packages…

Configure the application to include logging

Go to your Program.cs and configure the application to add the AWS logging provider

using Microsoft.Extensions.Logging;

// ...

public static void Main(string[] args)
    => CreateHostBuilder(args).Build().Run();

public static IHostBuilder CreateHostBuilder(string[] args)
    => Host.CreateDefaultBuilder(args)
        .ConfigureLogging(logging =>
        {
            logging.AddAWSProvider();
        })
        .ConfigureWebHostDefaults(
            webBuilder => webBuilder.UseStartup<Startup>());

Configure the log group in AWS

Log into AWS Console, and navigate to the “Log groups” section within CloudWatch

Hit “Create a log group”, then create a new log group with a meaningful name and sensible retention period.

Although each log entry takes up a negligible amount of space, you will need to consider how much data you will be logging, and how long you intend to retain that data to ensure that you don’t end up needlessly filling up storage space.

If you make a habit of checking errors regularly, or intend to set up alarms to notify you of serious errors, a low retention should be sufficient. The log levels chosen later on will also impact the how much data is logged.

Add a CloudWatch policy to the EC2 instance’s role

Remaining on the AWS console, navigate to the Identity and Access Management (IAM) service, and select “Roles” from the side navigation, then click on the role which was assigned to your EC2 instance when the it was first created

Under the role’s “Permissions” tab, hit the “Attach policies” button.

There are a vast array of AWS managed policies listed here, and you can be as selective as necessary, however to keep things simple, just filter the list to find “CloudWatchFullAccess”. You can view the details of this policy by expanding the section, and once you’re happy, check the box and hit “Attach policy”.

Configure the application to reference your new log group

Return now to your application, navigate to your appsettings.json and add the “Region” and “LogGroup” properties, with values appropriate to your instance’s region and new log group name.

"Logging": {
  "Region": "eu-west-2",
  "LogGroup": "tomjones.dev-prod",
  "LogLevel": {
    "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
  }

Add logging to a class using dependency injection

Navigate to one of your application’s classes, I’ll just use the boilerplate HomeController for this example. Create a private field for the injected logger and assign the field via the class constructor, then call one of the logger’s methods in one of the action methods.

using Microsoft.Extensions.Logging;

// ...

private readonly ILogger<HomeController> _logger;

public HomeController(ILogger<HomeController> logger)
{
    _logger = logger;
}

public IActionResult Index()
{

    _logger.LogInformation($"Returning view: {nameof(Index)}");
    return View();
}

Publish your application

Navigate to the section which will trigger logging (in my case the home page), and verify that the log message reaches CloudWatch by checking the most recent “Log streams” within the newly-created log group