How to Log to CloudWatch Logs using ASP.NET and Serilog

Serilog is well known in the .NET community as a simple, versatile provider for structured logging. Combine Serilog with CloudWatch Logs which enables the centralization of all your logs within a highly scalable service and you have a very powerful solution. In this article, we’ll integrate ASP.NET, Serilog and CloudWatch Logs to form a powerful debugging, troubleshooting and monitoring solution.

Photo by Mildly Useful on Unsplash

The Solution

In this tutorial, we will provide a simple example where we log to Amazon CloudWatch Logs using ASP.NET and Serilog.

Remember, for any example solution from AWS with .NET, we focus on the code that exemplifies the problem we are trying to solve. We don’t include logging, input validation, exception handling, etc., and we embed the configuration data within classes instead of using environment variables, configuration files, key/value stores and the like. These items should not be skipped for proper solutions.

Prerequisites

To complete this solution, you will need the .NET CLI which is included in the .NET SDK. In addition, you will need to configure your development environment to interact with AWS and create an AWS IAM user with the appropriate permissions to interact with Amazon CloudWatch Logs.

Warning: some AWS services may have fees associated with them.

Our Dev Environment

This tutorial was developed/updated using Ubuntu 24.10, .NET 8 SDK and Visual Studio Code 1.95.3. Some commands/constructs may vary across systems.

Developing the ASP.NET Amazon App

The first thing we will do is create the ASP.NET App using the .NET CLI.

$ dotnet new webapi -n CloudWatchSerilog --use-controllers

Add App Dependencies

Let’s now add the application dependencies.

$ dotnet add package AWS.Logger.SeriLog
$ dotnet add package Serilog.AspNetCore

CloudWatch Logs Configuration

With the basics of the application setup, let’s enter our configuration data which will allow us to send our logging data to CloudWatch Logs. For this example app, we’ll add the configuration data to the appsettings.Development.json file. The config construct will look something like the following:

{  
  "Serilog": {  
    "Using": [  
      "AWS.Logger.SeriLog"  
    ],  
    "LogGroup": "testloggroup",  
    "Region": "us-east-1",  
    "MinimumLevel": "Information",  
    "WriteTo": [  
      {  
        "Name": "AWSSeriLog"  
      }  
    ]  
  }  
}

Here’s a breakdown of the configuration data that we entered:

Using: This section contains a list of assemblies where Serilog methods like WriteTo.File() are located.
LogGroup: The Amazon CloudWatch Logs log group.
Region: The AWS region to log to.
MinimumLevel: This section represents the log level threshold.
WriteTo: Instructs ASP.NET logging where to write the log – in this case it is, CloudWatch Logs.

Update Program.cs

Let’s now update the Program.cs file to pull in the Serilog configuration.

First, add the Serilog using statement.

using Serilog;

Once that using statement is added, update the Program.cs file to wire up Serilog configuration right after the builder initialization.

var builder = WebApplication.CreateBuilder(args);  
  
builder.Services.AddSerilog((loggingConfig) => loggingConfig  
    .ReadFrom.Configuration(builder.Configuration));

When completed, the Program.cs file should look something like the following:

using Serilog;  
  
var builder = WebApplication.CreateBuilder(args);  
  
builder.Services.AddSerilog((loggingConfig) => loggingConfig  
    .ReadFrom.Configuration(builder.Configuration));  
  
// Add services to the container.  
  
builder.Services.AddControllers();  
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle  
builder.Services.AddEndpointsApiExplorer();  
builder.Services.AddSwaggerGen();  
  
var app = builder.Build();  
  
// Configure the HTTP request pipeline.  
if (app.Environment.IsDevelopment())  
{  
    app.UseSwagger();  
    app.UseSwaggerUI();  
}  
  
app.UseHttpsRedirection();  
  
app.UseAuthorization();  
  
app.MapControllers();  
  
app.Run();

Creating a Test API Endpoint

Let’s now create a controller that will allow us to test logging to CloudWatch Logs. In fact, let’s call this controller, LoggingTestController. Let’s start out with a basic controller with a just a single GET method.

Notice, that we are using constructor injection to get an instance of an ILogger object that we set up in Program.cs.

using Microsoft.AspNetCore.Mvc;  
  
namespace CloudWatchSerilog.Controllers;  
  
[ApiController]  
[Route("[controller]")]  
public class LoggingTestController : ControllerBase  
{  
    private readonly ILogger<LoggingTestController> _logger;  
  
    public LoggingTestController(ILogger<LoggingTestController> logger)  
    {  
        _logger = logger;  
    }  
  
    public string Get()  
    {  
  
    }  
}

To test the Cloud Watch Logs logging functionality through Serilog, let’s fill out the Get method a bit.

First, let’s create a string variable named thatThingToLog and we’ll provide a contrived value. Let’s then log an error using the logger instance and the variable that we just created.

We’ll return the variable to complete execution of the Get method.

public string Get()  
{  
    string thatThingToLog = "This is a test string to log via Serilog";  
  
    _logger.LogError(thatThingToLog);  
  
    return thatThingToLog;  
}

Let’s take a look at the LoggingTestController in it’s entirety.

using Microsoft.AspNetCore.Mvc;  
  
namespace CloudWatchSerilog.Controllers;  
  
[ApiController]  
[Route("[controller]")]  
public class LoggingTestController : ControllerBase  
{  
  
    private readonly ILogger<LoggingTestController> _logger;  
  
    public LoggingTestController(ILogger<LoggingTestController> logger)  
    {  
        _logger = logger;  
    }  
  
    public string Get()  
    {  
        string thatThingToLog = "This is a test string to log via Serilog";  
  
        _logger.LogError(thatThingToLog);  
  
        return thatThingToLog;  
    }  
}

Running the Serilog/CloudWatch ASP.NET App

With the application now complete, let’s give everything a test.

First, let’s run the ASP.NET Web API using the following command.

$ dotnet run

Using the host that is provided in the console, navigate to the URL of the LoggingTestController endpoint that we created. In our test case, it is: http://localhost:5052/loggingtest.

You should see the string that is returned from the LoggingTestController endpoint.

This is a test string to log via Serilog

Likewise, take a look at CloudWatch Logs in the AWS Console and look for your log group. Once you find it, drill into the log entries and you should see an entry like the following:

2024-06-10T21:20:39.714Z This is a test string to log via Serilog

Summary

We have concluded this tutorial where you have learned how to log your ASP.NET log events to Amazon CloudWatch Logs.

Want to know more about the tech in this article?  Checkout these resources:

.NET CLI.NET SDKAWS .NET SDKAmazon CloudWatch Logs, Serilog