Skip to content

Commit e7aa2e2

Browse files
dothomsonsvozza
andauthored
feat(metrics): return metrics instance from metrics functions (#4930)
Co-authored-by: Stefano Vozza <svozza@amazon.com>
1 parent b4acd18 commit e7aa2e2

8 files changed

Lines changed: 178 additions & 152 deletions

File tree

packages/metrics/src/Metrics.ts

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,10 @@ import type {
7171
* });
7272
*
7373
* export const handler = async (event: { requestId: string }) => {
74-
* metrics.addMetadata('request_id', event.requestId);
75-
* metrics.addMetric('successfulBooking', MetricUnit.Count, 1);
76-
* metrics.publishStoredMetrics();
74+
* metrics
75+
* .addMetadata('request_id', event.requestId)
76+
* .addMetric('successfulBooking', MetricUnit.Count, 1)
77+
* .publishStoredMetrics();
7778
* };
7879
* ```
7980
*
@@ -110,7 +111,7 @@ import type {
110111
* ```
111112
*
112113
* Note that decorators are a Stage 3 proposal for JavaScript and are not yet part of the ECMAScript standard.
113-
* The current implmementation in this library is based on the legacy TypeScript decorator syntax enabled by the [`experimentalDecorators` flag](https://www.typescriptlang.org/tsconfig/#experimentalDecorators)
114+
* The current implementation in this library is based on the legacy TypeScript decorator syntax enabled by the [`experimentalDecorators` flag](https://www.typescriptlang.org/tsconfig/#experimentalDecorators)
114115
* set to `true` in the `tsconfig.json` file.
115116
*
116117
* **Middy.js middleware**
@@ -239,12 +240,12 @@ class Metrics extends Utility implements MetricsInterface {
239240
* @param name - The name of the dimension
240241
* @param value - The value of the dimension
241242
*/
242-
public addDimension(name: string, value: string): void {
243+
public addDimension(name: string, value: string): this {
243244
if (isStringUndefinedNullEmpty(name) || isStringUndefinedNullEmpty(value)) {
244245
this.#logger.warn(
245246
`The dimension ${name} doesn't meet the requirements and won't be added. Ensure the dimension name and value are non empty strings`
246247
);
247-
return;
248+
return this;
248249
}
249250
if (MAX_DIMENSION_COUNT <= this.#dimensionsStore.getDimensionCount()) {
250251
throw new RangeError(
@@ -262,6 +263,7 @@ class Metrics extends Utility implements MetricsInterface {
262263
);
263264
}
264265
this.#dimensionsStore.addDimension(name, value);
266+
return this;
265267
}
266268

267269
/**
@@ -276,7 +278,7 @@ class Metrics extends Utility implements MetricsInterface {
276278
*
277279
* @param dimensions - An object with key-value pairs of dimensions
278280
*/
279-
public addDimensions(dimensions: Dimensions): void {
281+
public addDimensions(dimensions: Dimensions): this {
280282
const newDimensions = this.#sanitizeDimensions(dimensions);
281283
const currentCount = this.#dimensionsStore.getDimensionCount();
282284
const newSetCount = Object.keys(newDimensions).length;
@@ -287,6 +289,7 @@ class Metrics extends Utility implements MetricsInterface {
287289
}
288290

289291
this.#dimensionsStore.addDimensionSet(newDimensions);
292+
return this;
290293
}
291294

292295
/**
@@ -316,8 +319,9 @@ class Metrics extends Utility implements MetricsInterface {
316319
* @param key - The key of the metadata
317320
* @param value - The value of the metadata
318321
*/
319-
public addMetadata(key: string, value: string): void {
322+
public addMetadata(key: string, value: string): this {
320323
this.#metadataStore.set(key, value);
324+
return this;
321325
}
322326

323327
/**
@@ -359,9 +363,10 @@ class Metrics extends Utility implements MetricsInterface {
359363
unit: MetricUnit,
360364
value: number,
361365
resolution: MetricResolution = MetricResolutions.Standard
362-
): void {
366+
): this {
363367
this.storeMetric(name, unit, value, resolution);
364368
if (this.isSingleMetric) this.publishStoredMetrics();
369+
return this;
365370
}
366371

367372
/**
@@ -450,7 +455,7 @@ class Metrics extends Utility implements MetricsInterface {
450455
*
451456
* // ...
452457
*
453-
* metrics.clearDimensions(); // olnly the region dimension is removed
458+
* metrics.clearDimensions(); // only the region dimension is removed
454459
* };
455460
* ```
456461
*
@@ -595,7 +600,7 @@ class Metrics extends Utility implements MetricsInterface {
595600
* };
596601
* ```
597602
*/
598-
public publishStoredMetrics(): void {
603+
public publishStoredMetrics(): this {
599604
const hasMetrics = this.hasStoredMetrics();
600605
if (!this.shouldThrowOnEmptyMetrics && !hasMetrics) {
601606
this.#logger.warn(
@@ -613,6 +618,7 @@ class Metrics extends Utility implements MetricsInterface {
613618
this.clearMetrics();
614619
this.clearDimensions();
615620
this.clearMetadata();
621+
return this;
616622
}
617623

618624
/**
@@ -645,14 +651,15 @@ class Metrics extends Utility implements MetricsInterface {
645651
* ```
646652
* @param timestamp - The timestamp to set, which can be a number or a Date object.
647653
*/
648-
public setTimestamp(timestamp: number | Date): void {
654+
public setTimestamp(timestamp: number | Date): this {
649655
if (!this.#validateEmfTimestamp(timestamp)) {
650656
this.#logger.warn(
651657
"This metric doesn't meet the requirements and will be skipped by Amazon CloudWatch. " +
652658
'Ensure the timestamp is within 14 days in the past or up to 2 hours in the future and is also a valid number or Date object.'
653659
);
654660
}
655661
this.#metricsStore.setTimestamp(timestamp);
662+
return this;
656663
}
657664

658665
/**
@@ -786,7 +793,7 @@ class Metrics extends Utility implements MetricsInterface {
786793
*
787794
* @param dimensions - The dimensions to be added to the default dimensions object
788795
*/
789-
public setDefaultDimensions(dimensions: Dimensions): void {
796+
public setDefaultDimensions(dimensions: Dimensions): this {
790797
const newDimensions = this.#sanitizeDimensions(dimensions);
791798
const currentDefaultDimensions =
792799
this.#dimensionsStore.getDefaultDimensions();
@@ -802,6 +809,7 @@ class Metrics extends Utility implements MetricsInterface {
802809
...currentDefaultDimensions,
803810
...newDimensions,
804811
});
812+
return this;
805813
}
806814

807815
/**
@@ -823,8 +831,9 @@ class Metrics extends Utility implements MetricsInterface {
823831
*
824832
* @param enabled - Whether to throw an error if no metrics are emitted
825833
*/
826-
public setThrowOnEmptyMetrics(enabled: boolean): void {
834+
public setThrowOnEmptyMetrics(enabled: boolean): this {
827835
this.shouldThrowOnEmptyMetrics = enabled;
836+
return this;
828837
}
829838

830839
/**
@@ -1042,11 +1051,15 @@ class Metrics extends Utility implements MetricsInterface {
10421051
throw new RangeError(`${value} is not a valid number`);
10431052
if (!Object.values(MetricUnits).includes(unit))
10441053
throw new RangeError(
1045-
`Invalid metric unit '${unit}', expected either option: ${Object.values(MetricUnits).join(',')}`
1054+
`Invalid metric unit '${unit}', expected either option: ${Object.values(
1055+
MetricUnits
1056+
).join(',')}`
10461057
);
10471058
if (!Object.values(MetricResolutions).includes(resolution))
10481059
throw new RangeError(
1049-
`Invalid metric resolution '${resolution}', expected either option: ${Object.values(MetricResolutions).join(',')}`
1060+
`Invalid metric resolution '${resolution}', expected either option: ${Object.values(
1061+
MetricResolutions
1062+
).join(',')}`
10501063
);
10511064

10521065
if (this.#metricsStore.getMetricsCount() >= MAX_METRICS_SIZE) {

packages/metrics/src/types/Metrics.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ interface MetricsInterface {
179179
* @param name - The name of the dimension
180180
* @param value - The value of the dimension
181181
*/
182-
addDimension(name: string, value: string): void;
182+
addDimension(name: string, value: string): MetricsInterface;
183183
/**
184184
* Add multiple dimensions to the metrics.
185185
*
@@ -191,7 +191,7 @@ interface MetricsInterface {
191191
*
192192
* @param dimensions - An object with key-value pairs of dimensions
193193
*/
194-
addDimensions(dimensions: Dimensions): void;
194+
addDimensions(dimensions: Dimensions): MetricsInterface;
195195
/**
196196
* A metadata key-value pair to be included with metrics.
197197
*
@@ -219,7 +219,7 @@ interface MetricsInterface {
219219
* @param key - The key of the metadata
220220
* @param value - The value of the metadata
221221
*/
222-
addMetadata(key: string, value: string): void;
222+
addMetadata(key: string, value: string): MetricsInterface;
223223
/**
224224
* Add a metric to the metrics buffer.
225225
*
@@ -260,7 +260,7 @@ interface MetricsInterface {
260260
unit: MetricUnit,
261261
value: number,
262262
resolution?: MetricResolution
263-
): void;
263+
): MetricsInterface;
264264
/**
265265
* Immediately emit a `ColdStart` metric if this is a cold start invocation.
266266
*
@@ -335,7 +335,7 @@ interface MetricsInterface {
335335
*
336336
* // ...
337337
*
338-
* metrics.clearDimensions(); // olnly the region dimension is removed
338+
* metrics.clearDimensions(); // only the region dimension is removed
339339
* };
340340
* ```
341341
*
@@ -506,7 +506,7 @@ interface MetricsInterface {
506506
* ```
507507
* @param timestamp - The timestamp to set, which can be a number or a Date object.
508508
*/
509-
setTimestamp(timestamp: number | Date): void;
509+
setTimestamp(timestamp: number | Date): MetricsInterface;
510510
/**
511511
* Create a new Metrics instance configured to immediately flush a single metric.
512512
*
@@ -539,14 +539,14 @@ interface MetricsInterface {
539539
}
540540

541541
export type {
542-
MetricsOptions,
543542
Dimensions,
544543
EmfOutput,
545544
ExtraOptions,
546-
StoredMetrics,
547-
StoredMetric,
548545
MetricDefinition,
549546
MetricResolution,
550-
MetricUnit,
551547
MetricsInterface,
548+
MetricsOptions,
549+
MetricUnit,
550+
StoredMetric,
551+
StoredMetrics,
552552
};

packages/metrics/tests/e2e/basicFeatures.manual.test.functionCode.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,17 @@ const metrics = new Metrics({ namespace: namespace, serviceName: serviceName });
2727

2828
export const handler = (_event: unknown, _context: Context) => {
2929
metrics.captureColdStartMetric();
30-
metrics.throwOnEmptyMetrics();
31-
metrics.setDefaultDimensions(JSON.parse(defaultDimensions));
32-
metrics.addMetric(metricName, metricUnit, Number.parseInt(metricValue, 10));
33-
metrics.addDimension(
34-
Object.entries(JSON.parse(extraDimension))[0][0],
35-
Object.entries(JSON.parse(extraDimension))[0][1] as string
36-
);
30+
31+
metrics
32+
.setThrowOnEmptyMetrics(true)
33+
.setDefaultDimensions(JSON.parse(defaultDimensions));
34+
35+
metrics
36+
.addMetric(metricName, metricUnit, Number.parseInt(metricValue, 10))
37+
.addDimension(
38+
Object.entries(JSON.parse(extraDimension))[0][0],
39+
Object.entries(JSON.parse(extraDimension))[0][1] as string
40+
);
3741

3842
const metricWithItsOwnDimensions = metrics.singleMetric();
3943
metricWithItsOwnDimensions.addDimension(

packages/metrics/tests/unit/concurrency/metrics.test.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,10 @@ describe('Metrics concurrent invocation isolation', () => {
5353
() => {
5454
// Metrics must be created inside ALS context because reset() clears InvokeStore
5555
metrics = new Metrics({ singleMetric: false });
56-
metrics.addDimension('env', 'prod');
57-
metrics.addMetric('count', MetricUnit.Count, 1);
58-
metrics.addMetadata('key', 'value1');
56+
metrics
57+
.addDimension('env', 'prod')
58+
.addMetric('count', MetricUnit.Count, 1)
59+
.addMetadata('key', 'value1');
5960
},
6061
],
6162
return: () => metrics.publishStoredMetrics(),
@@ -132,8 +133,9 @@ describe('Metrics concurrent invocation isolation', () => {
132133
sideEffects: [
133134
() => {},
134135
() => {
135-
metrics.setTimestamp(timestamp2);
136-
metrics.addMetric('count', MetricUnit.Count, 2);
136+
metrics
137+
.setTimestamp(timestamp2)
138+
.addMetric('count', MetricUnit.Count, 2);
137139
},
138140
() => metrics.publishStoredMetrics(),
139141
],

packages/metrics/tests/unit/creatingMetrics.test.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,11 @@ describe('Creating metrics', () => {
6161
const metrics = new Metrics({ singleMetric: false });
6262

6363
// Act
64-
metrics.addMetric('test', MetricUnit.Count, 1);
65-
metrics.addMetric('test', MetricUnit.Count, 2);
66-
metrics.addMetric('another', MetricUnit.Bytes, 3);
67-
metrics.publishStoredMetrics();
64+
metrics
65+
.addMetric('test', MetricUnit.Count, 1)
66+
.addMetric('test', MetricUnit.Count, 2)
67+
.addMetric('another', MetricUnit.Bytes, 3)
68+
.publishStoredMetrics();
6869

6970
// Assess
7071
expect(console.log).toHaveBeenCalledTimes(1);
@@ -101,8 +102,7 @@ describe('Creating metrics', () => {
101102
metrics.addMetric('test', MetricUnit.Count, 1);
102103
metrics.addMetric('test', MetricUnit.Count, 2);
103104
metrics.clearMetrics();
104-
metrics.addMetric('another', MetricUnit.Count, 3);
105-
metrics.publishStoredMetrics();
105+
metrics.addMetric('another', MetricUnit.Count, 3).publishStoredMetrics();
106106

107107
// Assess
108108
expect(console.log).toHaveBeenCalledTimes(1);
@@ -126,11 +126,11 @@ describe('Creating metrics', () => {
126126
const metrics = new Metrics({ singleMetric: false });
127127

128128
// Act
129-
metrics.addMetric('test', MetricUnit.Count, 1);
130-
metrics.addMetric('test', MetricUnit.Count, 2);
131-
metrics.publishStoredMetrics();
132-
metrics.addMetric('another', MetricUnit.Count, 3);
133-
metrics.publishStoredMetrics();
129+
metrics
130+
.addMetric('test', MetricUnit.Count, 1)
131+
.addMetric('test', MetricUnit.Count, 2)
132+
.publishStoredMetrics();
133+
metrics.addMetric('another', MetricUnit.Count, 3).publishStoredMetrics();
134134

135135
// Assess
136136
expect(console.log).toHaveBeenCalledTimes(2);
@@ -328,7 +328,9 @@ describe('Creating metrics', () => {
328328
// @ts-expect-error - Testing runtime behavior with invalid metric unit
329329
expect(() => metrics.addMetric('test', 'invalid-unit', 1)).toThrowError(
330330
new RangeError(
331-
`Invalid metric unit 'invalid-unit', expected either option: ${Object.values(MetricUnit).join(',')}`
331+
`Invalid metric unit 'invalid-unit', expected either option: ${Object.values(
332+
MetricUnit
333+
).join(',')}`
332334
)
333335
);
334336
});
@@ -343,7 +345,9 @@ describe('Creating metrics', () => {
343345
metrics.addMetric('test', MetricUnit.Count, 1, 'invalid-resolution')
344346
).toThrowError(
345347
new RangeError(
346-
`Invalid metric resolution 'invalid-resolution', expected either option: ${Object.values(MetricResolution).join(',')}`
348+
`Invalid metric resolution 'invalid-resolution', expected either option: ${Object.values(
349+
MetricResolution
350+
).join(',')}`
347351
)
348352
);
349353
});

packages/metrics/tests/unit/customTimestamp.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,7 @@ describe('Setting custom timestamp', () => {
8484
});
8585

8686
// Act
87-
metrics.setTimestamp(Number.NaN);
88-
metrics.addMetric('test', MetricUnit.Count, 1);
87+
metrics.setTimestamp(Number.NaN).addMetric('test', MetricUnit.Count, 1);
8988

9089
// Assess
9190
expect(console.warn).toHaveBeenCalledTimes(1);
@@ -111,8 +110,9 @@ describe('Setting custom timestamp', () => {
111110
});
112111

113112
// Act
114-
metrics.setTimestamp(Date.now() + 0.5);
115-
metrics.addMetric('test', MetricUnit.Count, 1);
113+
metrics
114+
.setTimestamp(Date.now() + 0.5)
115+
.addMetric('test', MetricUnit.Count, 1);
116116

117117
// Assess
118118
expect(console.warn).toHaveBeenCalledTimes(1);

0 commit comments

Comments
 (0)