Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

@aws-lambda-powertools/metrics

Package Overview
Dependencies
Maintainers
2
Versions
125
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@aws-lambda-powertools/metrics - npm Package Compare versions

Comparing version
2.28.0
to
2.28.1
+42
-0
lib/cjs/Metrics.d.ts

@@ -117,2 +117,17 @@ import { Utility } from '@aws-lambda-powertools/commons';

/**
* Default dimensions to be added to all metrics
* @default {}
*/
private defaultDimensions;
/**
* Additional dimensions for the current metrics context
* @default {}
*/
private dimensions;
/**
* Additional dimension sets for the current metrics context
* @default []
*/
private dimensionSets;
/**
* Name of the Lambda function

@@ -127,2 +142,7 @@ */

/**
* Additional metadata to be included with metrics
* @default {}
*/
private metadata;
/**
* Namespace for the metrics

@@ -137,2 +157,7 @@ */

/**
* Storage for metrics before they are published
* @default {}
*/
private storedMetrics;
/**
* Whether to disable metrics

@@ -520,2 +545,6 @@ */

/**
* Gets the current number of dimensions count.
*/
private getCurrentDimensionsCount;
/**
* Get the custom config service if it exists.

@@ -525,2 +554,15 @@ */

/**
* Check if a metric is new or not.
*
* A metric is considered new if there is no metric with the same name already stored.
*
* When a metric is not new, we also check if the unit is consistent with the stored metric with
* the same name. If the units are inconsistent, we throw an error as this is likely a bug or typo.
* This can happen if a metric is added without using the `MetricUnit` helper in JavaScript codebases.
*
* @param name - The name of the metric
* @param unit - The unit of the metric
*/
private isNewMetric;
/**
* Initialize the console property as an instance of the internal version of `Console()` class (PR #748)

@@ -527,0 +569,0 @@ * or as the global node console if the `POWERTOOLS_DEV' env variable is set and has truthy value.

+133
-64

@@ -8,5 +8,2 @@ "use strict";

const constants_js_1 = require("./constants.js");
const DimensionsStore_js_1 = require("./DimensionsStore.js");
const MetadataStore_js_1 = require("./MetadataStore.js");
const MetricsStore_js_1 = require("./MetricsStore.js");
/**

@@ -124,6 +121,17 @@ * The Metrics utility creates custom metrics asynchronously by logging metrics to standard output following {@link https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format.html | Amazon CloudWatch Embedded Metric Format (EMF)}.

/**
* Storage for dimensions
* Default dimensions to be added to all metrics
* @default {}
*/
#dimensionsStore = new DimensionsStore_js_1.DimensionsStore();
defaultDimensions = {};
/**
* Additional dimensions for the current metrics context
* @default {}
*/
dimensions = {};
/**
* Additional dimension sets for the current metrics context
* @default []
*/
dimensionSets = [];
/**
* Name of the Lambda function

@@ -145,4 +153,5 @@ */

* Additional metadata to be included with metrics
* @default {}
*/
#metadataStore = new MetadataStore_js_1.MetadataStore();
metadata = {};
/**

@@ -159,4 +168,5 @@ * Namespace for the metrics

* Storage for metrics before they are published
* @default {}
*/
#metricsStore = new MetricsStore_js_1.MetricsStore();
storedMetrics = {};
/**

@@ -177,4 +187,9 @@ * Whether to disable metrics

};
/**
* Custom timestamp for the metrics
*/
#timestamp;
constructor(options = {}) {
super();
this.dimensions = {};
this.setEnvConfig();

@@ -203,12 +218,10 @@ this.setConsole();

}
if (constants_js_1.MAX_DIMENSION_COUNT <= this.#dimensionsStore.getDimensionCount()) {
if (constants_js_1.MAX_DIMENSION_COUNT <= this.getCurrentDimensionsCount()) {
throw new RangeError(`The number of metric dimensions must be lower than ${constants_js_1.MAX_DIMENSION_COUNT}`);
}
const dimensions = this.#dimensionsStore.getDimensions();
const defaultDimensions = this.#dimensionsStore.getDefaultDimensions();
if (Object.hasOwn(dimensions, name) ||
Object.hasOwn(defaultDimensions, name)) {
if (Object.hasOwn(this.dimensions, name) ||
Object.hasOwn(this.defaultDimensions, name)) {
this.#logger.warn(`Dimension "${name}" has already been added. The previous value will be overwritten.`);
}
this.#dimensionsStore.addDimension(name, value);
this.dimensions[name] = value;
}

@@ -229,3 +242,3 @@ /**

const newDimensions = this.#sanitizeDimensions(dimensions);
const currentCount = this.#dimensionsStore.getDimensionCount();
const currentCount = this.getCurrentDimensionsCount();
const newSetCount = Object.keys(newDimensions).length;

@@ -235,3 +248,3 @@ if (currentCount + newSetCount >= constants_js_1.MAX_DIMENSION_COUNT) {

}
this.#dimensionsStore.addDimensionSet(newDimensions);
this.dimensionSets.push(newDimensions);
}

@@ -266,3 +279,3 @@ /**

addMetadata(key, value) {
this.#metadataStore.set(key, value);
this.metadata[key] = value;
}

@@ -368,3 +381,3 @@ /**

clearDefaultDimensions() {
this.#dimensionsStore.clearDefaultDimensions();
this.defaultDimensions = {};
}

@@ -401,3 +414,4 @@ /**

clearDimensions() {
this.#dimensionsStore.clearRequestDimensions();
this.dimensions = {};
this.dimensionSets = [];
}

@@ -413,3 +427,3 @@ /**

clearMetadata() {
this.#metadataStore.clear();
this.metadata = {};
}

@@ -424,3 +438,3 @@ /**

clearMetrics() {
this.#metricsStore.clearMetrics();
this.storedMetrics = {};
}

@@ -431,3 +445,3 @@ /**

hasStoredMetrics() {
return this.#metricsStore.hasMetrics();
return Object.keys(this.storedMetrics).length > 0;
}

@@ -579,3 +593,3 @@ /**

}
this.#metricsStore.setTimestamp(timestamp);
this.#timestamp = this.#convertTimestampToEmfFormat(timestamp);
}

@@ -595,13 +609,10 @@ /**

serializeMetrics() {
const metricDefinitions = this.#metricsStore
.getAllMetrics()
.map((metricDefinition) => {
return {
Name: metricDefinition.name,
Unit: metricDefinition.unit,
...(metricDefinition.resolution === constants_js_1.MetricResolution.High
? { StorageResolution: metricDefinition.resolution }
: {}),
};
});
// Storage resolution is included only for High resolution metrics
const metricDefinitions = Object.values(this.storedMetrics).map((metricDefinition) => ({
Name: metricDefinition.name,
Unit: metricDefinition.unit,
...(metricDefinition.resolution === constants_js_1.MetricResolution.High
? { StorageResolution: metricDefinition.resolution }
: {}),
}));
if (metricDefinitions.length === 0 && this.shouldThrowOnEmptyMetrics) {

@@ -614,3 +625,3 @@ throw new RangeError('The number of metrics recorded must be higher than zero');

// name as the key and the value as the value.
const metricValues = this.#metricsStore.getAllMetrics().reduce((result, { name, value, }) => {
const metricValues = Object.values(this.storedMetrics).reduce((result, { name, value }) => {
result[name] = value;

@@ -620,15 +631,12 @@ return result;

const dimensionNames = [];
const dimensions = this.#dimensionsStore.getDimensions();
const dimensionSets = this.#dimensionsStore.getDimensionSets();
const defaultDimensions = this.#dimensionsStore.getDefaultDimensions();
const allDimensionKeys = new Set([
...Object.keys(defaultDimensions),
...Object.keys(dimensions),
...Object.keys(this.defaultDimensions),
...Object.keys(this.dimensions),
]);
if (Object.keys(dimensions).length > 0) {
if (Object.keys(this.dimensions).length > 0) {
dimensionNames.push([...allDimensionKeys]);
}
for (const dimensionSet of dimensionSets) {
for (const dimensionSet of this.dimensionSets) {
const dimensionSetKeys = new Set([
...Object.keys(defaultDimensions),
...Object.keys(this.defaultDimensions),
...Object.keys(dimensionSet),

@@ -639,8 +647,8 @@ ]);

if (dimensionNames.length === 0 &&
Object.keys(defaultDimensions).length > 0) {
dimensionNames.push(Object.keys(defaultDimensions));
Object.keys(this.defaultDimensions).length > 0) {
dimensionNames.push([...Object.keys(this.defaultDimensions)]);
}
return {
_aws: {
Timestamp: this.#metricsStore.getTimestamp() ?? Date.now(),
Timestamp: this.#timestamp ?? Date.now(),
CloudWatchMetrics: [

@@ -654,6 +662,6 @@ {

},
...defaultDimensions,
...dimensions,
...this.defaultDimensions,
...this.dimensions,
// Merge all dimension sets efficiently by mutating the accumulator
...dimensionSets.reduce((acc, dims) => {
...this.dimensionSets.reduce((acc, dims) => {
for (const [key, value] of Object.entries(dims)) {

@@ -665,3 +673,3 @@ acc[key] = value;

...metricValues,
...this.#metadataStore.getAll(),
...this.metadata,
};

@@ -695,4 +703,3 @@ }

const newDimensions = this.#sanitizeDimensions(dimensions);
const currentDefaultDimensions = this.#dimensionsStore.getDefaultDimensions();
const currentCount = Object.keys(currentDefaultDimensions).length;
const currentCount = Object.keys(this.defaultDimensions).length;
const newSetCount = Object.keys(newDimensions).length;

@@ -702,6 +709,6 @@ if (currentCount + newSetCount >= constants_js_1.MAX_DIMENSION_COUNT) {

}
this.#dimensionsStore.setDefaultDimensions({
...currentDefaultDimensions,
this.defaultDimensions = {
...this.defaultDimensions,
...newDimensions,
});
};
}

@@ -760,3 +767,3 @@ /**

namespace: this.namespace,
defaultDimensions: this.#dimensionsStore.getDefaultDimensions(),
defaultDimensions: this.defaultDimensions,
singleMetric: true,

@@ -773,2 +780,11 @@ logger: this.#logger,

/**
* Gets the current number of dimensions count.
*/
getCurrentDimensionsCount() {
const dimensionSetsCount = this.dimensionSets.reduce((total, dimensionSet) => total + Object.keys(dimensionSet).length, 0);
return (Object.keys(this.dimensions).length +
Object.keys(this.defaultDimensions).length +
dimensionSetsCount);
}
/**
* Get the custom config service if it exists.

@@ -780,2 +796,24 @@ */

/**
* Check if a metric is new or not.
*
* A metric is considered new if there is no metric with the same name already stored.
*
* When a metric is not new, we also check if the unit is consistent with the stored metric with
* the same name. If the units are inconsistent, we throw an error as this is likely a bug or typo.
* This can happen if a metric is added without using the `MetricUnit` helper in JavaScript codebases.
*
* @param name - The name of the metric
* @param unit - The unit of the metric
*/
isNewMetric(name, unit) {
if (this.storedMetrics[name]) {
if (this.storedMetrics[name].unit !== unit) {
const currentUnit = this.storedMetrics[name].unit;
throw new Error(`Metric "${name}" has already been added with unit "${currentUnit}", but we received unit "${unit}". Did you mean to use metric unit "${currentUnit}"?`);
}
return false;
}
return true;
}
/**
* Initialize the console property as an instance of the internal version of `Console()` class (PR #748)

@@ -916,10 +954,23 @@ * or as the global node console if the `POWERTOOLS_DEV' env variable is set and has truthy value.

throw new RangeError(`Invalid metric resolution '${resolution}', expected either option: ${Object.values(constants_js_1.MetricResolution).join(',')}`);
if (this.#metricsStore.getMetricsCount() >= constants_js_1.MAX_METRICS_SIZE) {
if (Object.keys(this.storedMetrics).length >= constants_js_1.MAX_METRICS_SIZE) {
this.publishStoredMetrics();
}
const storedMetric = this.#metricsStore.setMetric(name, unit, value, resolution);
if (Array.isArray(storedMetric.value) &&
storedMetric.value.length === constants_js_1.MAX_METRIC_VALUES_SIZE) {
this.publishStoredMetrics();
if (this.isNewMetric(name, unit)) {
this.storedMetrics[name] = {
unit,
value,
name,
resolution,
};
}
else {
const storedMetric = this.storedMetrics[name];
if (!Array.isArray(storedMetric.value)) {
storedMetric.value = [storedMetric.value];
}
storedMetric.value.push(value);
if (storedMetric.value.length === constants_js_1.MAX_METRIC_VALUES_SIZE) {
this.publishStoredMetrics();
}
}
}

@@ -948,2 +999,22 @@ /**

/**
* Converts a given timestamp to EMF compatible format.
*
* @param timestamp - The timestamp to convert, which can be either a number (in milliseconds) or a Date object.
* @returns The timestamp in milliseconds. If the input is invalid, returns 0.
*/
#convertTimestampToEmfFormat(timestamp) {
if ((0, commons_1.isIntegerNumber)(timestamp)) {
return timestamp;
}
if (timestamp instanceof Date) {
return timestamp.getTime();
}
/**
* If this point is reached, it indicates timestamp was neither a valid number nor Date
* Returning zero represents the initial date of epoch time,
* which will be skipped by Amazon CloudWatch.
*/
return 0;
}
/**
* Sanitizes the dimensions by removing invalid entries and skipping duplicates.

@@ -955,3 +1026,2 @@ *

const newDimensions = {};
const currentDimensions = this.#dimensionsStore.getDimensions();
for (const [key, value] of Object.entries(dimensions)) {

@@ -963,5 +1033,4 @@ if ((0, commons_1.isStringUndefinedNullEmpty)(key) ||

}
const defaultDimensions = this.#dimensionsStore.getDefaultDimensions();
if (Object.hasOwn(currentDimensions, key) ||
Object.hasOwn(defaultDimensions, key) ||
if (Object.hasOwn(this.dimensions, key) ||
Object.hasOwn(this.defaultDimensions, key) ||
Object.hasOwn(newDimensions, key)) {

@@ -968,0 +1037,0 @@ this.#logger.warn(`Dimension "${key}" has already been added. The previous value will be overwritten.`);

@@ -117,2 +117,17 @@ import { Utility } from '@aws-lambda-powertools/commons';

/**
* Default dimensions to be added to all metrics
* @default {}
*/
private defaultDimensions;
/**
* Additional dimensions for the current metrics context
* @default {}
*/
private dimensions;
/**
* Additional dimension sets for the current metrics context
* @default []
*/
private dimensionSets;
/**
* Name of the Lambda function

@@ -127,2 +142,7 @@ */

/**
* Additional metadata to be included with metrics
* @default {}
*/
private metadata;
/**
* Namespace for the metrics

@@ -137,2 +157,7 @@ */

/**
* Storage for metrics before they are published
* @default {}
*/
private storedMetrics;
/**
* Whether to disable metrics

@@ -520,2 +545,6 @@ */

/**
* Gets the current number of dimensions count.
*/
private getCurrentDimensionsCount;
/**
* Get the custom config service if it exists.

@@ -525,2 +554,15 @@ */

/**
* Check if a metric is new or not.
*
* A metric is considered new if there is no metric with the same name already stored.
*
* When a metric is not new, we also check if the unit is consistent with the stored metric with
* the same name. If the units are inconsistent, we throw an error as this is likely a bug or typo.
* This can happen if a metric is added without using the `MetricUnit` helper in JavaScript codebases.
*
* @param name - The name of the metric
* @param unit - The unit of the metric
*/
private isNewMetric;
/**
* Initialize the console property as an instance of the internal version of `Console()` class (PR #748)

@@ -527,0 +569,0 @@ * or as the global node console if the `POWERTOOLS_DEV' env variable is set and has truthy value.

@@ -5,5 +5,2 @@ import { Console } from 'node:console';

import { COLD_START_METRIC, DEFAULT_NAMESPACE, EMF_MAX_TIMESTAMP_FUTURE_AGE, EMF_MAX_TIMESTAMP_PAST_AGE, MAX_DIMENSION_COUNT, MAX_METRIC_NAME_LENGTH, MAX_METRIC_VALUES_SIZE, MAX_METRICS_SIZE, MetricResolution as MetricResolutions, MetricUnit as MetricUnits, MIN_METRIC_NAME_LENGTH, } from './constants.js';
import { DimensionsStore } from './DimensionsStore.js';
import { MetadataStore } from './MetadataStore.js';
import { MetricsStore } from './MetricsStore.js';
/**

@@ -121,6 +118,17 @@ * The Metrics utility creates custom metrics asynchronously by logging metrics to standard output following {@link https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format.html | Amazon CloudWatch Embedded Metric Format (EMF)}.

/**
* Storage for dimensions
* Default dimensions to be added to all metrics
* @default {}
*/
#dimensionsStore = new DimensionsStore();
defaultDimensions = {};
/**
* Additional dimensions for the current metrics context
* @default {}
*/
dimensions = {};
/**
* Additional dimension sets for the current metrics context
* @default []
*/
dimensionSets = [];
/**
* Name of the Lambda function

@@ -142,4 +150,5 @@ */

* Additional metadata to be included with metrics
* @default {}
*/
#metadataStore = new MetadataStore();
metadata = {};
/**

@@ -156,4 +165,5 @@ * Namespace for the metrics

* Storage for metrics before they are published
* @default {}
*/
#metricsStore = new MetricsStore();
storedMetrics = {};
/**

@@ -174,4 +184,9 @@ * Whether to disable metrics

};
/**
* Custom timestamp for the metrics
*/
#timestamp;
constructor(options = {}) {
super();
this.dimensions = {};
this.setEnvConfig();

@@ -200,12 +215,10 @@ this.setConsole();

}
if (MAX_DIMENSION_COUNT <= this.#dimensionsStore.getDimensionCount()) {
if (MAX_DIMENSION_COUNT <= this.getCurrentDimensionsCount()) {
throw new RangeError(`The number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`);
}
const dimensions = this.#dimensionsStore.getDimensions();
const defaultDimensions = this.#dimensionsStore.getDefaultDimensions();
if (Object.hasOwn(dimensions, name) ||
Object.hasOwn(defaultDimensions, name)) {
if (Object.hasOwn(this.dimensions, name) ||
Object.hasOwn(this.defaultDimensions, name)) {
this.#logger.warn(`Dimension "${name}" has already been added. The previous value will be overwritten.`);
}
this.#dimensionsStore.addDimension(name, value);
this.dimensions[name] = value;
}

@@ -226,3 +239,3 @@ /**

const newDimensions = this.#sanitizeDimensions(dimensions);
const currentCount = this.#dimensionsStore.getDimensionCount();
const currentCount = this.getCurrentDimensionsCount();
const newSetCount = Object.keys(newDimensions).length;

@@ -232,3 +245,3 @@ if (currentCount + newSetCount >= MAX_DIMENSION_COUNT) {

}
this.#dimensionsStore.addDimensionSet(newDimensions);
this.dimensionSets.push(newDimensions);
}

@@ -263,3 +276,3 @@ /**

addMetadata(key, value) {
this.#metadataStore.set(key, value);
this.metadata[key] = value;
}

@@ -365,3 +378,3 @@ /**

clearDefaultDimensions() {
this.#dimensionsStore.clearDefaultDimensions();
this.defaultDimensions = {};
}

@@ -398,3 +411,4 @@ /**

clearDimensions() {
this.#dimensionsStore.clearRequestDimensions();
this.dimensions = {};
this.dimensionSets = [];
}

@@ -410,3 +424,3 @@ /**

clearMetadata() {
this.#metadataStore.clear();
this.metadata = {};
}

@@ -421,3 +435,3 @@ /**

clearMetrics() {
this.#metricsStore.clearMetrics();
this.storedMetrics = {};
}

@@ -428,3 +442,3 @@ /**

hasStoredMetrics() {
return this.#metricsStore.hasMetrics();
return Object.keys(this.storedMetrics).length > 0;
}

@@ -576,3 +590,3 @@ /**

}
this.#metricsStore.setTimestamp(timestamp);
this.#timestamp = this.#convertTimestampToEmfFormat(timestamp);
}

@@ -592,13 +606,10 @@ /**

serializeMetrics() {
const metricDefinitions = this.#metricsStore
.getAllMetrics()
.map((metricDefinition) => {
return {
Name: metricDefinition.name,
Unit: metricDefinition.unit,
...(metricDefinition.resolution === MetricResolutions.High
? { StorageResolution: metricDefinition.resolution }
: {}),
};
});
// Storage resolution is included only for High resolution metrics
const metricDefinitions = Object.values(this.storedMetrics).map((metricDefinition) => ({
Name: metricDefinition.name,
Unit: metricDefinition.unit,
...(metricDefinition.resolution === MetricResolutions.High
? { StorageResolution: metricDefinition.resolution }
: {}),
}));
if (metricDefinitions.length === 0 && this.shouldThrowOnEmptyMetrics) {

@@ -611,3 +622,3 @@ throw new RangeError('The number of metrics recorded must be higher than zero');

// name as the key and the value as the value.
const metricValues = this.#metricsStore.getAllMetrics().reduce((result, { name, value, }) => {
const metricValues = Object.values(this.storedMetrics).reduce((result, { name, value }) => {
result[name] = value;

@@ -617,15 +628,12 @@ return result;

const dimensionNames = [];
const dimensions = this.#dimensionsStore.getDimensions();
const dimensionSets = this.#dimensionsStore.getDimensionSets();
const defaultDimensions = this.#dimensionsStore.getDefaultDimensions();
const allDimensionKeys = new Set([
...Object.keys(defaultDimensions),
...Object.keys(dimensions),
...Object.keys(this.defaultDimensions),
...Object.keys(this.dimensions),
]);
if (Object.keys(dimensions).length > 0) {
if (Object.keys(this.dimensions).length > 0) {
dimensionNames.push([...allDimensionKeys]);
}
for (const dimensionSet of dimensionSets) {
for (const dimensionSet of this.dimensionSets) {
const dimensionSetKeys = new Set([
...Object.keys(defaultDimensions),
...Object.keys(this.defaultDimensions),
...Object.keys(dimensionSet),

@@ -636,8 +644,8 @@ ]);

if (dimensionNames.length === 0 &&
Object.keys(defaultDimensions).length > 0) {
dimensionNames.push(Object.keys(defaultDimensions));
Object.keys(this.defaultDimensions).length > 0) {
dimensionNames.push([...Object.keys(this.defaultDimensions)]);
}
return {
_aws: {
Timestamp: this.#metricsStore.getTimestamp() ?? Date.now(),
Timestamp: this.#timestamp ?? Date.now(),
CloudWatchMetrics: [

@@ -651,6 +659,6 @@ {

},
...defaultDimensions,
...dimensions,
...this.defaultDimensions,
...this.dimensions,
// Merge all dimension sets efficiently by mutating the accumulator
...dimensionSets.reduce((acc, dims) => {
...this.dimensionSets.reduce((acc, dims) => {
for (const [key, value] of Object.entries(dims)) {

@@ -662,3 +670,3 @@ acc[key] = value;

...metricValues,
...this.#metadataStore.getAll(),
...this.metadata,
};

@@ -692,4 +700,3 @@ }

const newDimensions = this.#sanitizeDimensions(dimensions);
const currentDefaultDimensions = this.#dimensionsStore.getDefaultDimensions();
const currentCount = Object.keys(currentDefaultDimensions).length;
const currentCount = Object.keys(this.defaultDimensions).length;
const newSetCount = Object.keys(newDimensions).length;

@@ -699,6 +706,6 @@ if (currentCount + newSetCount >= MAX_DIMENSION_COUNT) {

}
this.#dimensionsStore.setDefaultDimensions({
...currentDefaultDimensions,
this.defaultDimensions = {
...this.defaultDimensions,
...newDimensions,
});
};
}

@@ -757,3 +764,3 @@ /**

namespace: this.namespace,
defaultDimensions: this.#dimensionsStore.getDefaultDimensions(),
defaultDimensions: this.defaultDimensions,
singleMetric: true,

@@ -770,2 +777,11 @@ logger: this.#logger,

/**
* Gets the current number of dimensions count.
*/
getCurrentDimensionsCount() {
const dimensionSetsCount = this.dimensionSets.reduce((total, dimensionSet) => total + Object.keys(dimensionSet).length, 0);
return (Object.keys(this.dimensions).length +
Object.keys(this.defaultDimensions).length +
dimensionSetsCount);
}
/**
* Get the custom config service if it exists.

@@ -777,2 +793,24 @@ */

/**
* Check if a metric is new or not.
*
* A metric is considered new if there is no metric with the same name already stored.
*
* When a metric is not new, we also check if the unit is consistent with the stored metric with
* the same name. If the units are inconsistent, we throw an error as this is likely a bug or typo.
* This can happen if a metric is added without using the `MetricUnit` helper in JavaScript codebases.
*
* @param name - The name of the metric
* @param unit - The unit of the metric
*/
isNewMetric(name, unit) {
if (this.storedMetrics[name]) {
if (this.storedMetrics[name].unit !== unit) {
const currentUnit = this.storedMetrics[name].unit;
throw new Error(`Metric "${name}" has already been added with unit "${currentUnit}", but we received unit "${unit}". Did you mean to use metric unit "${currentUnit}"?`);
}
return false;
}
return true;
}
/**
* Initialize the console property as an instance of the internal version of `Console()` class (PR #748)

@@ -913,10 +951,23 @@ * or as the global node console if the `POWERTOOLS_DEV' env variable is set and has truthy value.

throw new RangeError(`Invalid metric resolution '${resolution}', expected either option: ${Object.values(MetricResolutions).join(',')}`);
if (this.#metricsStore.getMetricsCount() >= MAX_METRICS_SIZE) {
if (Object.keys(this.storedMetrics).length >= MAX_METRICS_SIZE) {
this.publishStoredMetrics();
}
const storedMetric = this.#metricsStore.setMetric(name, unit, value, resolution);
if (Array.isArray(storedMetric.value) &&
storedMetric.value.length === MAX_METRIC_VALUES_SIZE) {
this.publishStoredMetrics();
if (this.isNewMetric(name, unit)) {
this.storedMetrics[name] = {
unit,
value,
name,
resolution,
};
}
else {
const storedMetric = this.storedMetrics[name];
if (!Array.isArray(storedMetric.value)) {
storedMetric.value = [storedMetric.value];
}
storedMetric.value.push(value);
if (storedMetric.value.length === MAX_METRIC_VALUES_SIZE) {
this.publishStoredMetrics();
}
}
}

@@ -945,2 +996,22 @@ /**

/**
* Converts a given timestamp to EMF compatible format.
*
* @param timestamp - The timestamp to convert, which can be either a number (in milliseconds) or a Date object.
* @returns The timestamp in milliseconds. If the input is invalid, returns 0.
*/
#convertTimestampToEmfFormat(timestamp) {
if (isIntegerNumber(timestamp)) {
return timestamp;
}
if (timestamp instanceof Date) {
return timestamp.getTime();
}
/**
* If this point is reached, it indicates timestamp was neither a valid number nor Date
* Returning zero represents the initial date of epoch time,
* which will be skipped by Amazon CloudWatch.
*/
return 0;
}
/**
* Sanitizes the dimensions by removing invalid entries and skipping duplicates.

@@ -952,3 +1023,2 @@ *

const newDimensions = {};
const currentDimensions = this.#dimensionsStore.getDimensions();
for (const [key, value] of Object.entries(dimensions)) {

@@ -960,5 +1030,4 @@ if (isStringUndefinedNullEmpty(key) ||

}
const defaultDimensions = this.#dimensionsStore.getDefaultDimensions();
if (Object.hasOwn(currentDimensions, key) ||
Object.hasOwn(defaultDimensions, key) ||
if (Object.hasOwn(this.dimensions, key) ||
Object.hasOwn(this.defaultDimensions, key) ||
Object.hasOwn(newDimensions, key)) {

@@ -965,0 +1034,0 @@ this.#logger.warn(`Dimension "${key}" has already been added. The previous value will be overwritten.`);

{
"name": "@aws-lambda-powertools/metrics",
"version": "2.28.0",
"version": "2.28.1",
"description": "The metrics package for the Powertools for AWS Lambda (TypeScript) library",

@@ -27,4 +27,3 @@ "author": {

"dependencies": {
"@aws-lambda-powertools/commons": "2.28.0",
"@aws/lambda-invoke-store": "0.1.0"
"@aws-lambda-powertools/commons": "2.28.1"
},

@@ -31,0 +30,0 @@ "peerDependencies": {

import type { Dimensions } from './types/Metrics.js';
/**
* Manages storage of metrics dimensions with automatic context detection.
*
* This class abstracts the storage mechanism for metrics, automatically
* choosing between AsyncLocalStorage (when in async context) and a fallback
* object (when outside async context). The decision is made at runtime on
* every method call to support Lambda's transition to async contexts.
*/
declare class DimensionsStore {
#private;
addDimension(name: string, value: string): string;
addDimensionSet(dimensionSet: Dimensions): Dimensions;
getDimensions(): Dimensions;
getDimensionSets(): Dimensions[];
clearRequestDimensions(): void;
clearDefaultDimensions(): void;
getDimensionCount(): number;
setDefaultDimensions(dimensions: Dimensions): void;
getDefaultDimensions(): Dimensions;
}
export { DimensionsStore };
//# sourceMappingURL=DimensionsStore.d.ts.map
{"version":3,"file":"DimensionsStore.d.ts","sourceRoot":"","sources":["../../src/DimensionsStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD;;;;;;;GAOG;AACH,cAAM,eAAe;;IAoCZ,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAKjD,eAAe,CAAC,YAAY,EAAE,UAAU,GAAG,UAAU;IAKrD,aAAa,IAAI,UAAU;IAI3B,gBAAgB,IAAI,UAAU,EAAE;IAIhC,sBAAsB,IAAI,IAAI;IAW9B,sBAAsB,IAAI,IAAI;IAI9B,iBAAiB,IAAI,MAAM;IAc3B,oBAAoB,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI;IAIlD,oBAAoB,IAAI,UAAU;CAG1C;AAED,OAAO,EAAE,eAAe,EAAE,CAAC"}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DimensionsStore = void 0;
const lambda_invoke_store_1 = require("@aws/lambda-invoke-store");
/**
* Manages storage of metrics dimensions with automatic context detection.
*
* This class abstracts the storage mechanism for metrics, automatically
* choosing between AsyncLocalStorage (when in async context) and a fallback
* object (when outside async context). The decision is made at runtime on
* every method call to support Lambda's transition to async contexts.
*/
class DimensionsStore {
#dimensionsKey = Symbol('powertools.metrics.dimensions');
#dimensionSetsKey = Symbol('powertools.metrics.dimensionSets');
#fallbackDimensions = {};
#fallbackDimensionSets = [];
#defaultDimensions = {};
#getDimensions() {
if (lambda_invoke_store_1.InvokeStore.getContext() === undefined) {
return this.#fallbackDimensions;
}
let stored = lambda_invoke_store_1.InvokeStore.get(this.#dimensionsKey);
if (stored == null) {
stored = {};
lambda_invoke_store_1.InvokeStore.set(this.#dimensionsKey, stored);
}
return stored;
}
#getDimensionSets() {
if (lambda_invoke_store_1.InvokeStore.getContext() === undefined) {
return this.#fallbackDimensionSets;
}
let stored = lambda_invoke_store_1.InvokeStore.get(this.#dimensionSetsKey);
if (stored == null) {
stored = [];
lambda_invoke_store_1.InvokeStore.set(this.#dimensionSetsKey, stored);
}
return stored;
}
addDimension(name, value) {
this.#getDimensions()[name] = value;
return value;
}
addDimensionSet(dimensionSet) {
this.#getDimensionSets().push({ ...dimensionSet });
return dimensionSet;
}
getDimensions() {
return { ...this.#getDimensions() };
}
getDimensionSets() {
return this.#getDimensionSets().map((set) => ({ ...set }));
}
clearRequestDimensions() {
if (lambda_invoke_store_1.InvokeStore.getContext() === undefined) {
this.#fallbackDimensions = {};
this.#fallbackDimensionSets = [];
return;
}
lambda_invoke_store_1.InvokeStore.set(this.#dimensionsKey, {});
lambda_invoke_store_1.InvokeStore.set(this.#dimensionSetsKey, []);
}
clearDefaultDimensions() {
this.#defaultDimensions = {};
}
getDimensionCount() {
const dimensions = this.#getDimensions();
const dimensionSets = this.#getDimensionSets();
const dimensionSetsCount = dimensionSets.reduce((total, dimensionSet) => total + Object.keys(dimensionSet).length, 0);
return (Object.keys(dimensions).length +
Object.keys(this.#defaultDimensions).length +
dimensionSetsCount);
}
setDefaultDimensions(dimensions) {
this.#defaultDimensions = { ...dimensions };
}
getDefaultDimensions() {
return { ...this.#defaultDimensions };
}
}
exports.DimensionsStore = DimensionsStore;
/**
* Manages storage of metrics #metadata with automatic context detection.
*
* This class abstracts the storage mechanism for metrics, automatically
* choosing between AsyncLocalStorage (when in async context) and a fallback
* object (when outside async context). The decision is made at runtime on
* every method call to support Lambda's transition to async contexts.
*/
declare class MetadataStore {
#private;
set(key: string, value: string): string;
getAll(): Record<string, string>;
clear(): void;
}
export { MetadataStore };
//# sourceMappingURL=MetadataStore.d.ts.map
{"version":3,"file":"MetadataStore.d.ts","sourceRoot":"","sources":["../../src/MetadataStore.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,cAAM,aAAa;;IAoBV,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAKvC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAIhC,KAAK,IAAI,IAAI;CAQrB;AAED,OAAO,EAAE,aAAa,EAAE,CAAC"}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MetadataStore = void 0;
const lambda_invoke_store_1 = require("@aws/lambda-invoke-store");
/**
* Manages storage of metrics #metadata with automatic context detection.
*
* This class abstracts the storage mechanism for metrics, automatically
* choosing between AsyncLocalStorage (when in async context) and a fallback
* object (when outside async context). The decision is made at runtime on
* every method call to support Lambda's transition to async contexts.
*/
class MetadataStore {
#metadataKey = Symbol('powertools.metrics.metadata');
#fallbackStorage = {};
#getStorage() {
if (lambda_invoke_store_1.InvokeStore.getContext() === undefined) {
return this.#fallbackStorage;
}
let stored = lambda_invoke_store_1.InvokeStore.get(this.#metadataKey);
if (stored == null) {
stored = {};
lambda_invoke_store_1.InvokeStore.set(this.#metadataKey, stored);
}
return stored;
}
set(key, value) {
this.#getStorage()[key] = value;
return value;
}
getAll() {
return { ...this.#getStorage() };
}
clear() {
if (lambda_invoke_store_1.InvokeStore.getContext() === undefined) {
this.#fallbackStorage = {};
return;
}
lambda_invoke_store_1.InvokeStore.set(this.#metadataKey, {});
}
}
exports.MetadataStore = MetadataStore;
import type { MetricResolution, MetricUnit, StoredMetric } from './types/index.js';
/**
* Manages storage of metrics with automatic context detection.
*
* This class abstracts the storage mechanism for metrics, automatically
* choosing between AsyncLocalStorage (when in async context) and a fallback
* object (when outside async context). The decision is made at runtime on
* every method call to support Lambda's transition to async contexts.
*/
declare class MetricsStore {
#private;
getMetric(name: string): StoredMetric | undefined;
/**
* Adds a metric value to storage. If a metric with the same name already exists,
* the value is appended to an array. Validates that the unit matches any existing metric.
*
* @example
* ```typescript
* store.setMetric('latency', MetricUnit.Milliseconds, 100);
* // Returns: { name: 'latency', unit: 'Milliseconds', value: 100, resolution: 60 }
*
* store.setMetric('latency', MetricUnit.Milliseconds, 150);
* // Returns: { name: 'latency', unit: 'Milliseconds', value: [100, 150], resolution: 60 }
* ```
*
* @param name - The metric name
* @param unit - The metric unit (must match existing metric if present)
* @param value - The metric value to add
* @param resolution - The metric resolution (defaults to Standard)
* @returns The stored metric with updated values
* @throws Error if unit doesn't match existing metric
*/
setMetric(name: string, unit: MetricUnit, value: number, resolution?: MetricResolution): StoredMetric;
getMetricNames(): string[];
getAllMetrics(): StoredMetric[];
clearMetrics(): void;
hasMetrics(): boolean;
getMetricsCount(): number;
getTimestamp(): number | undefined;
setTimestamp(timestamp: number | Date): number;
}
export { MetricsStore };
//# sourceMappingURL=MetricsStore.d.ts.map
{"version":3,"file":"MetricsStore.d.ts","sourceRoot":"","sources":["../../src/MetricsStore.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EACV,YAAY,EAEb,MAAM,kBAAkB,CAAC;AAE1B;;;;;;;GAOG;AACH,cAAM,YAAY;;IAsBT,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIxD;;;;;;;;;;;;;;;;;;;OAmBG;IACI,SAAS,CACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,MAAM,EACb,UAAU,GAAE,gBAA6C,GACxD,YAAY;IA6BR,cAAc,IAAI,MAAM,EAAE;IAI1B,aAAa,IAAI,YAAY,EAAE;IAI/B,YAAY,IAAI,IAAI;IAWpB,UAAU,IAAI,OAAO;IAIrB,eAAe,IAAI,MAAM;IAIzB,YAAY,IAAI,MAAM,GAAG,SAAS;IAQlC,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;CAqBtD;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MetricsStore = void 0;
const lambda_invoke_store_1 = require("@aws/lambda-invoke-store");
const typeutils_1 = require("@aws-lambda-powertools/commons/typeutils");
const constants_js_1 = require("./constants.js");
/**
* Manages storage of metrics with automatic context detection.
*
* This class abstracts the storage mechanism for metrics, automatically
* choosing between AsyncLocalStorage (when in async context) and a fallback
* object (when outside async context). The decision is made at runtime on
* every method call to support Lambda's transition to async contexts.
*/
class MetricsStore {
#storedMetricsKey = Symbol('powertools.metrics.storedMetrics');
#timestampKey = Symbol('powertools.metrics.timestamp');
#fallbackStorage = {};
#fallbackTimestamp;
#getStorage() {
if (lambda_invoke_store_1.InvokeStore.getContext() === undefined) {
return this.#fallbackStorage;
}
let stored = lambda_invoke_store_1.InvokeStore.get(this.#storedMetricsKey);
if (stored == null) {
stored = {};
lambda_invoke_store_1.InvokeStore.set(this.#storedMetricsKey, stored);
}
return stored;
}
getMetric(name) {
return this.#getStorage()[name];
}
/**
* Adds a metric value to storage. If a metric with the same name already exists,
* the value is appended to an array. Validates that the unit matches any existing metric.
*
* @example
* ```typescript
* store.setMetric('latency', MetricUnit.Milliseconds, 100);
* // Returns: { name: 'latency', unit: 'Milliseconds', value: 100, resolution: 60 }
*
* store.setMetric('latency', MetricUnit.Milliseconds, 150);
* // Returns: { name: 'latency', unit: 'Milliseconds', value: [100, 150], resolution: 60 }
* ```
*
* @param name - The metric name
* @param unit - The metric unit (must match existing metric if present)
* @param value - The metric value to add
* @param resolution - The metric resolution (defaults to Standard)
* @returns The stored metric with updated values
* @throws Error if unit doesn't match existing metric
*/
setMetric(name, unit, value, resolution = constants_js_1.MetricResolution.Standard) {
const storage = this.#getStorage();
const existingMetric = storage[name];
if (existingMetric === undefined) {
const newMetric = {
name,
unit,
value,
resolution,
};
storage[name] = newMetric;
return { ...newMetric };
}
if (existingMetric.unit !== unit) {
const currentUnit = existingMetric.unit;
throw new Error(`Metric "${name}" has already been added with unit "${currentUnit}", but we received unit "${unit}". Did you mean to use metric unit "${currentUnit}"?`);
}
if (!Array.isArray(existingMetric.value)) {
existingMetric.value = [existingMetric.value];
}
existingMetric.value.push(value);
return { ...existingMetric, value: [...existingMetric.value] };
}
getMetricNames() {
return Object.keys(this.#getStorage());
}
getAllMetrics() {
return Object.values(this.#getStorage());
}
clearMetrics() {
if (lambda_invoke_store_1.InvokeStore.getContext() === undefined) {
this.#fallbackStorage = {};
this.#fallbackTimestamp = undefined;
return;
}
lambda_invoke_store_1.InvokeStore.set(this.#storedMetricsKey, {});
lambda_invoke_store_1.InvokeStore.set(this.#timestampKey, undefined);
}
hasMetrics() {
return this.getMetricNames().length > 0;
}
getMetricsCount() {
return this.getMetricNames().length;
}
getTimestamp() {
if (lambda_invoke_store_1.InvokeStore.getContext() === undefined) {
return this.#fallbackTimestamp;
}
return lambda_invoke_store_1.InvokeStore.get(this.#timestampKey);
}
setTimestamp(timestamp) {
const timestampMs = this.#convertTimestampToEmfFormat(timestamp);
if (lambda_invoke_store_1.InvokeStore.getContext() === undefined) {
this.#fallbackTimestamp = timestampMs;
return timestampMs;
}
lambda_invoke_store_1.InvokeStore.set(this.#timestampKey, timestampMs);
return timestampMs;
}
#convertTimestampToEmfFormat(timestamp) {
if ((0, typeutils_1.isIntegerNumber)(timestamp)) {
return timestamp;
}
if (timestamp instanceof Date) {
return timestamp.getTime();
}
return 0;
}
}
exports.MetricsStore = MetricsStore;
import type { Dimensions } from './types/Metrics.js';
/**
* Manages storage of metrics dimensions with automatic context detection.
*
* This class abstracts the storage mechanism for metrics, automatically
* choosing between AsyncLocalStorage (when in async context) and a fallback
* object (when outside async context). The decision is made at runtime on
* every method call to support Lambda's transition to async contexts.
*/
declare class DimensionsStore {
#private;
addDimension(name: string, value: string): string;
addDimensionSet(dimensionSet: Dimensions): Dimensions;
getDimensions(): Dimensions;
getDimensionSets(): Dimensions[];
clearRequestDimensions(): void;
clearDefaultDimensions(): void;
getDimensionCount(): number;
setDefaultDimensions(dimensions: Dimensions): void;
getDefaultDimensions(): Dimensions;
}
export { DimensionsStore };
//# sourceMappingURL=DimensionsStore.d.ts.map
{"version":3,"file":"DimensionsStore.d.ts","sourceRoot":"","sources":["../../src/DimensionsStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD;;;;;;;GAOG;AACH,cAAM,eAAe;;IAoCZ,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAKjD,eAAe,CAAC,YAAY,EAAE,UAAU,GAAG,UAAU;IAKrD,aAAa,IAAI,UAAU;IAI3B,gBAAgB,IAAI,UAAU,EAAE;IAIhC,sBAAsB,IAAI,IAAI;IAW9B,sBAAsB,IAAI,IAAI;IAI9B,iBAAiB,IAAI,MAAM;IAc3B,oBAAoB,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI;IAIlD,oBAAoB,IAAI,UAAU;CAG1C;AAED,OAAO,EAAE,eAAe,EAAE,CAAC"}
import { InvokeStore } from '@aws/lambda-invoke-store';
/**
* Manages storage of metrics dimensions with automatic context detection.
*
* This class abstracts the storage mechanism for metrics, automatically
* choosing between AsyncLocalStorage (when in async context) and a fallback
* object (when outside async context). The decision is made at runtime on
* every method call to support Lambda's transition to async contexts.
*/
class DimensionsStore {
#dimensionsKey = Symbol('powertools.metrics.dimensions');
#dimensionSetsKey = Symbol('powertools.metrics.dimensionSets');
#fallbackDimensions = {};
#fallbackDimensionSets = [];
#defaultDimensions = {};
#getDimensions() {
if (InvokeStore.getContext() === undefined) {
return this.#fallbackDimensions;
}
let stored = InvokeStore.get(this.#dimensionsKey);
if (stored == null) {
stored = {};
InvokeStore.set(this.#dimensionsKey, stored);
}
return stored;
}
#getDimensionSets() {
if (InvokeStore.getContext() === undefined) {
return this.#fallbackDimensionSets;
}
let stored = InvokeStore.get(this.#dimensionSetsKey);
if (stored == null) {
stored = [];
InvokeStore.set(this.#dimensionSetsKey, stored);
}
return stored;
}
addDimension(name, value) {
this.#getDimensions()[name] = value;
return value;
}
addDimensionSet(dimensionSet) {
this.#getDimensionSets().push({ ...dimensionSet });
return dimensionSet;
}
getDimensions() {
return { ...this.#getDimensions() };
}
getDimensionSets() {
return this.#getDimensionSets().map((set) => ({ ...set }));
}
clearRequestDimensions() {
if (InvokeStore.getContext() === undefined) {
this.#fallbackDimensions = {};
this.#fallbackDimensionSets = [];
return;
}
InvokeStore.set(this.#dimensionsKey, {});
InvokeStore.set(this.#dimensionSetsKey, []);
}
clearDefaultDimensions() {
this.#defaultDimensions = {};
}
getDimensionCount() {
const dimensions = this.#getDimensions();
const dimensionSets = this.#getDimensionSets();
const dimensionSetsCount = dimensionSets.reduce((total, dimensionSet) => total + Object.keys(dimensionSet).length, 0);
return (Object.keys(dimensions).length +
Object.keys(this.#defaultDimensions).length +
dimensionSetsCount);
}
setDefaultDimensions(dimensions) {
this.#defaultDimensions = { ...dimensions };
}
getDefaultDimensions() {
return { ...this.#defaultDimensions };
}
}
export { DimensionsStore };
/**
* Manages storage of metrics #metadata with automatic context detection.
*
* This class abstracts the storage mechanism for metrics, automatically
* choosing between AsyncLocalStorage (when in async context) and a fallback
* object (when outside async context). The decision is made at runtime on
* every method call to support Lambda's transition to async contexts.
*/
declare class MetadataStore {
#private;
set(key: string, value: string): string;
getAll(): Record<string, string>;
clear(): void;
}
export { MetadataStore };
//# sourceMappingURL=MetadataStore.d.ts.map
{"version":3,"file":"MetadataStore.d.ts","sourceRoot":"","sources":["../../src/MetadataStore.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,cAAM,aAAa;;IAoBV,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAKvC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAIhC,KAAK,IAAI,IAAI;CAQrB;AAED,OAAO,EAAE,aAAa,EAAE,CAAC"}
import { InvokeStore } from '@aws/lambda-invoke-store';
/**
* Manages storage of metrics #metadata with automatic context detection.
*
* This class abstracts the storage mechanism for metrics, automatically
* choosing between AsyncLocalStorage (when in async context) and a fallback
* object (when outside async context). The decision is made at runtime on
* every method call to support Lambda's transition to async contexts.
*/
class MetadataStore {
#metadataKey = Symbol('powertools.metrics.metadata');
#fallbackStorage = {};
#getStorage() {
if (InvokeStore.getContext() === undefined) {
return this.#fallbackStorage;
}
let stored = InvokeStore.get(this.#metadataKey);
if (stored == null) {
stored = {};
InvokeStore.set(this.#metadataKey, stored);
}
return stored;
}
set(key, value) {
this.#getStorage()[key] = value;
return value;
}
getAll() {
return { ...this.#getStorage() };
}
clear() {
if (InvokeStore.getContext() === undefined) {
this.#fallbackStorage = {};
return;
}
InvokeStore.set(this.#metadataKey, {});
}
}
export { MetadataStore };
import type { MetricResolution, MetricUnit, StoredMetric } from './types/index.js';
/**
* Manages storage of metrics with automatic context detection.
*
* This class abstracts the storage mechanism for metrics, automatically
* choosing between AsyncLocalStorage (when in async context) and a fallback
* object (when outside async context). The decision is made at runtime on
* every method call to support Lambda's transition to async contexts.
*/
declare class MetricsStore {
#private;
getMetric(name: string): StoredMetric | undefined;
/**
* Adds a metric value to storage. If a metric with the same name already exists,
* the value is appended to an array. Validates that the unit matches any existing metric.
*
* @example
* ```typescript
* store.setMetric('latency', MetricUnit.Milliseconds, 100);
* // Returns: { name: 'latency', unit: 'Milliseconds', value: 100, resolution: 60 }
*
* store.setMetric('latency', MetricUnit.Milliseconds, 150);
* // Returns: { name: 'latency', unit: 'Milliseconds', value: [100, 150], resolution: 60 }
* ```
*
* @param name - The metric name
* @param unit - The metric unit (must match existing metric if present)
* @param value - The metric value to add
* @param resolution - The metric resolution (defaults to Standard)
* @returns The stored metric with updated values
* @throws Error if unit doesn't match existing metric
*/
setMetric(name: string, unit: MetricUnit, value: number, resolution?: MetricResolution): StoredMetric;
getMetricNames(): string[];
getAllMetrics(): StoredMetric[];
clearMetrics(): void;
hasMetrics(): boolean;
getMetricsCount(): number;
getTimestamp(): number | undefined;
setTimestamp(timestamp: number | Date): number;
}
export { MetricsStore };
//# sourceMappingURL=MetricsStore.d.ts.map
{"version":3,"file":"MetricsStore.d.ts","sourceRoot":"","sources":["../../src/MetricsStore.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EACV,YAAY,EAEb,MAAM,kBAAkB,CAAC;AAE1B;;;;;;;GAOG;AACH,cAAM,YAAY;;IAsBT,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIxD;;;;;;;;;;;;;;;;;;;OAmBG;IACI,SAAS,CACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,MAAM,EACb,UAAU,GAAE,gBAA6C,GACxD,YAAY;IA6BR,cAAc,IAAI,MAAM,EAAE;IAI1B,aAAa,IAAI,YAAY,EAAE;IAI/B,YAAY,IAAI,IAAI;IAWpB,UAAU,IAAI,OAAO;IAIrB,eAAe,IAAI,MAAM;IAIzB,YAAY,IAAI,MAAM,GAAG,SAAS;IAQlC,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;CAqBtD;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
import { InvokeStore } from '@aws/lambda-invoke-store';
import { isIntegerNumber } from '@aws-lambda-powertools/commons/typeutils';
import { MetricResolution as MetricResolutions } from './constants.js';
/**
* Manages storage of metrics with automatic context detection.
*
* This class abstracts the storage mechanism for metrics, automatically
* choosing between AsyncLocalStorage (when in async context) and a fallback
* object (when outside async context). The decision is made at runtime on
* every method call to support Lambda's transition to async contexts.
*/
class MetricsStore {
#storedMetricsKey = Symbol('powertools.metrics.storedMetrics');
#timestampKey = Symbol('powertools.metrics.timestamp');
#fallbackStorage = {};
#fallbackTimestamp;
#getStorage() {
if (InvokeStore.getContext() === undefined) {
return this.#fallbackStorage;
}
let stored = InvokeStore.get(this.#storedMetricsKey);
if (stored == null) {
stored = {};
InvokeStore.set(this.#storedMetricsKey, stored);
}
return stored;
}
getMetric(name) {
return this.#getStorage()[name];
}
/**
* Adds a metric value to storage. If a metric with the same name already exists,
* the value is appended to an array. Validates that the unit matches any existing metric.
*
* @example
* ```typescript
* store.setMetric('latency', MetricUnit.Milliseconds, 100);
* // Returns: { name: 'latency', unit: 'Milliseconds', value: 100, resolution: 60 }
*
* store.setMetric('latency', MetricUnit.Milliseconds, 150);
* // Returns: { name: 'latency', unit: 'Milliseconds', value: [100, 150], resolution: 60 }
* ```
*
* @param name - The metric name
* @param unit - The metric unit (must match existing metric if present)
* @param value - The metric value to add
* @param resolution - The metric resolution (defaults to Standard)
* @returns The stored metric with updated values
* @throws Error if unit doesn't match existing metric
*/
setMetric(name, unit, value, resolution = MetricResolutions.Standard) {
const storage = this.#getStorage();
const existingMetric = storage[name];
if (existingMetric === undefined) {
const newMetric = {
name,
unit,
value,
resolution,
};
storage[name] = newMetric;
return { ...newMetric };
}
if (existingMetric.unit !== unit) {
const currentUnit = existingMetric.unit;
throw new Error(`Metric "${name}" has already been added with unit "${currentUnit}", but we received unit "${unit}". Did you mean to use metric unit "${currentUnit}"?`);
}
if (!Array.isArray(existingMetric.value)) {
existingMetric.value = [existingMetric.value];
}
existingMetric.value.push(value);
return { ...existingMetric, value: [...existingMetric.value] };
}
getMetricNames() {
return Object.keys(this.#getStorage());
}
getAllMetrics() {
return Object.values(this.#getStorage());
}
clearMetrics() {
if (InvokeStore.getContext() === undefined) {
this.#fallbackStorage = {};
this.#fallbackTimestamp = undefined;
return;
}
InvokeStore.set(this.#storedMetricsKey, {});
InvokeStore.set(this.#timestampKey, undefined);
}
hasMetrics() {
return this.getMetricNames().length > 0;
}
getMetricsCount() {
return this.getMetricNames().length;
}
getTimestamp() {
if (InvokeStore.getContext() === undefined) {
return this.#fallbackTimestamp;
}
return InvokeStore.get(this.#timestampKey);
}
setTimestamp(timestamp) {
const timestampMs = this.#convertTimestampToEmfFormat(timestamp);
if (InvokeStore.getContext() === undefined) {
this.#fallbackTimestamp = timestampMs;
return timestampMs;
}
InvokeStore.set(this.#timestampKey, timestampMs);
return timestampMs;
}
#convertTimestampToEmfFormat(timestamp) {
if (isIntegerNumber(timestamp)) {
return timestamp;
}
if (timestamp instanceof Date) {
return timestamp.getTime();
}
return 0;
}
}
export { MetricsStore };

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet