@middy/cloudwatch-metrics
Version:
Embedded CloudWatch metrics middleware for the middy framework
89 lines (75 loc) • 2.75 kB
JavaScript
// Copyright 2017 - 2026 will Farrell, Luciano Mammino, and Middy contributors.
// SPDX-License-Identifier: MIT
import { validateOptions } from "@middy/util";
import awsEmbeddedMetrics from "aws-embedded-metrics";
const name = "cloudwatch-metrics";
const pkg = `@middy/${name}`;
// Stryker disable next-line ObjectLiteral: value is already undefined, so dropping the key is observably identical after spread + destructuring
const defaults = {
onFlushError: undefined,
};
// `dimensions` accepts either a single dimension set (object of
// string->string) or an array of dimension sets, matching the documented
// API and aws-embedded-metrics' `setDimensions(dimensionSets)` signature.
const dimensionSetSchema = {
type: "object",
maxProperties: 30,
additionalProperties: { type: "string", minLength: 1, maxLength: 1024 },
};
const optionSchema = {
type: "object",
properties: {
namespace: { type: "string", minLength: 1, maxLength: 256 },
dimensions: {
oneOf: [dimensionSetSchema, { type: "array", items: dimensionSetSchema }],
},
onFlushError: { instanceof: "Function" },
},
additionalProperties: false,
};
export const cloudwatchMetricsValidateOptions = (options) =>
validateOptions(pkg, optionSchema, options);
const cloudwatchMetricsMiddleware = (opts = {}) => {
const { namespace, dimensions, onFlushError } = { ...defaults, ...opts };
if (dimensions) {
const dimensionSets = Array.isArray(dimensions) ? dimensions : [dimensions];
for (const set of dimensionSets) {
if (Object.keys(set).length > 30) {
throw new Error(
`${pkg} a dimension set may contain at most 30 dimensions`,
{ cause: { package: pkg } },
);
}
}
}
const cloudwatchMetricsBefore = async (request) => {
const metrics = awsEmbeddedMetrics.createMetricsLogger();
// If not set, defaults to aws-embedded-metrics
if (namespace) {
metrics.setNamespace(namespace);
}
// If not set, keeps defaults as defined here https://github.com/awslabs/aws-embedded-metrics-node/#configuration
if (dimensions) {
metrics.setDimensions(dimensions);
}
Object.assign(request.context, { metrics });
};
const flushMetrics = async (request) => {
try {
await request.context.metrics?.flush();
} catch (err) {
// Flush errors are swallowed to prevent metrics from crashing the
// handler. Users who need visibility (e.g., to catch IAM or network
// misconfigurations) can pass `onFlushError` to observe them.
onFlushError?.(err);
}
};
const cloudwatchMetricsAfter = flushMetrics;
const cloudwatchMetricsOnError = flushMetrics;
return {
before: cloudwatchMetricsBefore,
after: cloudwatchMetricsAfter,
onError: cloudwatchMetricsOnError,
};
};
export default cloudwatchMetricsMiddleware;