AWS Lambda Powertools for .NET Part 2: Metrics
Throttles, duration, invocations, error rate. These are the bread and butter of your metrics whenever you bring a new Lambda function into the world. Useful though they are, the intrepid serverless developer will no doubt find a wealth of reasons to want to hook into even more information than the out-of-the-box metrics provide.
There are a few different options available to developers, from adding Lambda layers to provide additional observability, to crafting metrics from your logs via CloudWatch Insights. But for .NET developers there's another awesome tool made available with the recent GA announcement of Lambda Powertools for .NET, and that's custom metrics.
So, how can we put custom metrics to use? And how easy does Lambda Powertools make the exercise? (Hint: Super easy).
Let's take a walk through a simple example I've put together to demonstrate how to set up and then report on custom metrics.
For detailed information on Lambda Powertools you can head over to the GitHub repository:
But for the moment I've set up a simple project in Visual Studio to walk you through the steps involved.
I've chosen to start with a template for the Lambda annotations framework (because it's awesome - more on that another time) and have taken the liberty of creating a simple Lambda and API Gateway powered HTTP API endpoint that we can use to get a feel for how custom metrics work.
First things first, the magical metrics package we need to add to our project:
dotnet add package AWS.Lambda.Powertools.Metrics
Next, let's take a look at the function code I've put together in its entirely, then we can pull apart the specifics:
I've set my Lambda handler up to accept a string of environment and an integer of metricValue. The response is a simple ApiResponse object that contains a http status code and a message.
Because I'm using the Lambda annotations framework, it's a breeze to set up a HTTP API. I just decorate my handler with the HttpApi decorator specifying the HTTP verb, path and parameters. In this case I've created a post endpoint that accepts path parameters of {environment} and {metricValue} as an integer.
Additionally, I've decorated the handler with the configuration for my function (namely memory size and timeout). Thanks to the magic of the annotations framework, as I add or modify these configuration values, my serverless application model (SAM) template is automatically synchronised and updated with the configuration as described in code.
If that's not literal sorcery I don't know what is.
The idea with this sample is that I'll be able to call the API, pass a {metricValue} to it, and use those values to generate CloudWatch custom metrics that I'll be able to view in AWS. So where does {environment} come in? Well, we're going to use that to demonstrate dimensionality in metrics. Essentially, we'll be able to report metrics to the same namespace, but report them as either /production or /development so that we can further slice our metrics by the dimension of environment.
So let's take a look at the actual pushing of our custom metric.
领英推荐
As you can see, we've pushed one single metric value to CloudWatch. We're sourcing the value and dimension from our API endpoint, and the rest has been hardcoded. The namespace will show in the CloudWatch console under custom namespaces, with the service and dimension(s) underneath the specific metric.
We've chosen our 'metric unit' as CountPerSecond, but there are a few options available depending on the type of data you're looking to capture (examples being Seconds, BytesPerSecond or Percentage).
So with that one method, we should have everything we need to post the data to CloudWatch. Let's deploy and see how it works!
(.....one SAM-to-CloudFormation journey later....)
With our API deployed to AWS, let's have a go at invoking it with a custom metric value. To be slightly more efficient than just manually calling curl a bunch of times, I've put together the following PowerShell script which will invoke my API a bunch of times, passing in random metric values each time within a range:
if (-not $args) {
? Write-Output "Usage: .\test_script.ps1 <api-id>"
? exit 1
}
$api_id = $args[0]
for ($i = 1; $i -le 1000; $i++) {
? $value = Get-Random -Minimum 1 -Maximum 300
? Invoke-RestMethod -Uri "https://$api_id.execute-api.ap-southeast-2.amazonaws.com/metrics/embedded/production/$value" -Method Post
? Start-Sleep -Seconds 1
}
So let's fire it off and see what happens!
Excellent - my metrics are off (we hope) to CloudWatch. So let's log into the console and see what's available.
Excellent! Under custom namespaces, we can see our MyMetrics namespace available, including our custom dimension of 'Environment' available alongside defaults like Service and FunctionName.
(Side note: You might have noticed the Lambda handler was also decorated with [Metrics(CaptureColdStart)], this handy metrics feature lets you include Lambda cold starts in your metrics namespace along with anything else you want to capture. That's why there's 3 metrics available under MyMetrics > Service. One for cold starts, one for my custom metric, and one for ....well... the same custom metric but with 'service_undefined' as the service name because I forgot to include it the first time I created the sample :)
So back to our custom metrics, as you can see I've created a simple CloudWatch graph featuring my custom metric viewed two ways. One is as a 1 second average, the other as the maximum value during the same time. From that we get our line graph of our custom metric over time.
It's really as easy as that, and while I've used a pretty abstract example here, you can imagine all the use cases just waiting to be explored. I know with my own team, we'll be able to utilise custom metrics in our serverless functions as a way to measure the performance of different dependent services, without needing to do what we do today; log the data in our own custom JSON format then 'hand craft' metrics via CloudWatch Insights.
In case you're looking to reproduce the sample above, I've put the code into a small repository here:
Hopefully I'll get some time in the near future to round out these articles on Powertools for Lambda by taking a look at the last library - tracing.
Until then!
Developer Experience @ AWS | Software Builder | Public Speaker | Content Creator
1 年Nice write up James