@aws-lambda-powertools/metrics
Advanced tools
+42
-0
@@ -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.`); |
+42
-0
@@ -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
@@ -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.`); |
+2
-3
| { | ||
| "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
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
2
-33.33%230874
-8.25%46
-28.12%4948
-7.84%39
5.41%+ Added
- Removed
- Removed
- Removed