DEV Community

Derek Comartin - CodeOpinion
Derek Comartin - CodeOpinion

Posted on • Originally published at codeopinion.com on

1

Custom Metrics to AWS CloudWatch from ASP.NET Core

CloudWatch Custom Metrics

I was playing around with AWS CloudWatch and was curious to send custom metrics from ASP.NET Core. Specifically the execution time of an HTTP request.

AWS SDK

I created a simple middleware that starts a StopWatch before calling the next middleware in the pipeline. When it returns, stop the StopWatch and send the data to CloudWatch.

First is to add the relevant NuGet packages.

<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="AWSSDK.Core" Version="3.3.103.23" />
<PackageReference Include="AWSSDK.Extensions.NETCore.Setup" Version="3.3.100.1" />
<PackageReference Include="AWSSDK.CloudWatch" Version="3.3.102.14" />
</ItemGroup>
</Project>
view raw csproj.xml hosted with ❤ by GitHub

Configuration

If you’ve never used the AWS SDK/Packages, I recommend checking out my post on Configuring AWS SDK in ASP.NET Core. It goes over creating a named profile to store your AWS credentials and using appSettings or Environment variables to pass them through to ASP.NET Core.

Middleware

Next is to create a simple middleware using the SDK. The middleware is having the IAmazonCloudwatch injected into the constructor. In the Startup’s ConfigureServices is where this is configured.

We’re simply calling the PutMetricDataAsync with a list of one MetricDatum. It contains the metric name, value, unit, timestamp, and dimensions.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Amazon.CloudWatch;
using Amazon.CloudWatch.Model;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace AspNetCore.Aws.Demo
{
public class CloudWatchExecutionTimeMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
private readonly IAmazonCloudWatch _amazonCloudWatch;
public CloudWatchExecutionTimeMiddleware(RequestDelegate next, ILogger<CloudWatchExecutionTimeMiddleware> logger, IAmazonCloudWatch amazonCloudWatch)
{
_next = next;
_logger = logger;
_amazonCloudWatch = amazonCloudWatch;
}
public async Task InvokeAsync(HttpContext context)
{
var stopWatch = new Stopwatch();
stopWatch.Start();
await _next(context);
stopWatch.Stop();
try
{
await _amazonCloudWatch.PutMetricDataAsync(new PutMetricDataRequest
{
Namespace = "Demo",
MetricData = new List<MetricDatum>
{
new MetricDatum
{
MetricName = "AspNetExecutionTime",
Value = stopWatch.ElapsedMilliseconds,
Unit = StandardUnit.Milliseconds,
TimestampUtc = DateTime.UtcNow,
Dimensions = new List<Dimension>
{
new Dimension
{
Name = "Method",
Value = context.Request.Method
},
new Dimension
{
Name = "Path",
Value = context.Request.Path
}
}
}
}
});
}
catch (Exception ex)
{
_logger.LogCritical(ex, "Failed to send CloudWatch Metric");
}
}
}
}
view raw CloudWatch.cs hosted with ❤ by GitHub

Startup

As mentioned, in order to use the AWS SDK and the IAmazonCloudWatch in the middleware, you can use the AddDefaultAWSOptions and AddAWSService to ConfigureServices to register the appropriate types.

using Amazon.CloudWatch;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace AspNetCore.Aws.Demo
{
public class Startup
{
private readonly IConfiguration _configuration;
public Startup(IConfiguration configuration)
{
_configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(builder =>
{
builder.AddConsole();
builder.AddDebug();
}
);
services.AddDefaultAWSOptions(_configuration.GetAWSOptions());
services.AddAWSService<IAmazonCloudWatch>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMiddleware<CloudWatchExecutionTimeMiddleware>();
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}
}
}
view raw startup.cs hosted with ❤ by GitHub

Results

If you look in CloudWatch, you can now see the new custom metrics we’ve published.

Next

Before you start using this in a production environment, there are a couple of glaring issues.

First, sending the metrics to AWS, although incredibly fast should be handled outside of the actual HTTP request. this can be done completely asynchronously from the actual request. There is no point in delaying the HTTP response to the client. To solve this, we’ll add a queue and do it in a background service.

Secondly, some of the cost associated with CloudWatch is based on the number of API calls. This is why there is a List in the PutMetricDataRequest. It allows you to send/batch multiple metrics together to limit the number of API calls.

This is a better solution is to collect PutMetricDataRequest separately and send them in batch in a background service.

More on that coming soon.

Follow @codeopinion

The post Custom Metrics to AWS CloudWatch from ASP.NET Core appeared first on CodeOpinion.

AWS GenAI LIVE image

How is generative AI increasing efficiency?

Join AWS GenAI LIVE! to find out how gen AI is reshaping productivity, streamlining processes, and driving innovation.

Learn more

Top comments (0)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay