How to report CloudWatch metrics without AWS SDK
Tags: serverless, aws, javascript, monitoring
Published: 2022-03-18 09:48:06

Sometimes when writing a Lambda function, we need to send [a custom metric](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/publishingMetrics.html) to CloudWatch Metrics. This could be a technical metric (e.g., request time to an external service) or a metric about a business process (e.g., number of user logins). The obvious way to do this is to use the [`putMetricData` method from the AWS SDK](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CloudWatch.html#putMetricData-property):

```javascript
const AWS = require("aws-sdk");
const cloudwatch = new AWS.CloudWatch();

exports.handler = async () => {
  // business logic

  const metric = {
    Namespace: "Service1",
    MetricData: [
      {
        MetricName: "loginAttempts",
        Dimensions: [
          {
            Name: "tenant",
            Value: "client1",
          },
        ],
        Unit: "Count",
        Value: 1,
      },
    ],
  };

  await cloudwatch.putMetricData(metric).promise();

  // more business logic
};
```

This solution is sufficient if we occasionally send such metrics. The problem starts if we want to send a lot of those metrics, from different places in the function code. Calling the `putMetricData` method the same way as calling any other method from AWS SDK, increases the runtime of the function, thus increasing its cost. Besides, we can only send 40kB of data in a single call.
At the end of 2019, AWS has enabled metrics reporting without using AWS SDK.

## Embedded Metric Format

This can be done by logging the data to `stdout` in the specific format called [Embedded Metric Format](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html). For example:

```json
{
  "_aws": {
    "Timestamp": 1579211886742,
    "CloudWatchMetrics": [
      {
        "Dimensions": [["tenant"]],
        "Metrics": [
          {
            "Name": "loginAttempts",
            "Unit": "Count"
          }
        ],
        "Namespace": "Service1"
      }
    ]
  },
  "loginAttempts": 1,
  "tenant": "client1"
}
```

To make it easier to create such an object, AWS has provided libraries for [Node.js](https://github.com/awslabs/aws-embedded-metrics-node), [Python](https://github.com/awslabs/aws-embedded-metrics-python), [Java](https://github.com/awslabs/aws-embedded-metrics-java), and [.NET](https://github.com/awslabs/aws-embedded-metrics-dotnet). The above example using the AWS SDK can now be written as follows:

```javascript
const { createMetricsLogger, Unit } = require("aws-embedded-metrics");

exports.handler = async (event, context) => {
  // business logic

  const metrics = createMetricsLogger();
  metrics.setNamespace("Service1");
  metrics.putMetric("loginAttempts", 1, Unit.Count);
  metrics.setDimensions({ tenant: "client1" });
  await metrics.flush();

  // more business logic
};
```

There are no network calls to AWS involved, so the function call time does not increase. The data is logged to `stdout` and the rest is handled by CloudWatch, processing it and publishing it as metrics. Additionally, our function does not need the `cloudwatch:PutMetricData` permission.

The `aws-embedded-metrics` library also provides a wrapper for Lambda functions that eliminates the need to manually call the `flush()` method. This allows reporting metrics to be spread throughout the code and sending them to `stdout` will only happen once at the end of the Lambda execution.

```javascript
const { metricScope } = require("aws-embedded-metrics");

exports.handler = metricScope((metrics) => async () => {
  // business logic

  metrics.setNamespace("Service2");
  metrics.putMetric("loginAttempts", 1, Unit.Count);
  metrics.setDimensions({ tenant: "client2" });
  metrics.setProperty("RequestId", context.awsRequestId);

  // more business logic
});
```

Additionally, using the `setProperty` method, we can add optional parameters that we can later search for in _CloudWatch Logs Insights_.

In summary, with _Embedded Metric Format_, we can optimally send a lot of custom metrics to the _CloudWatch_ service without extending the function duration.

--- 

## Stay on top of your logs 🌩

![Cloudash screenshot](https://cloudash.dev/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fcloudash_function.6e0454ea.png&w=3840&q=75)

Introducing [Cloudash](https://cloudash.dev), a desktop app for monitoring your serverless services performance, invocations, errors and more.

Did a production incident happen last week? Or 20 seconds ago? With Cloudash you can search, filter and browse your serverless logs and metrics effortlessly.

Search for whatever you want, whenever you want. Cloudash comes with built-in filtering capabilities enabling to get to the bottom of your problems faster than ever before.

Get started [here](https://cloudash.dev).