Skip to content

Commit 58b0877

Browse files
authored
feat(metrics): publish metrics when other middlewares return early (#1546)
1 parent 74ddb09 commit 58b0877

File tree

2 files changed

+62
-21
lines changed

2 files changed

+62
-21
lines changed

Diff for: packages/metrics/src/middleware/middy.ts

+15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { METRICS_KEY } from '@aws-lambda-powertools/commons/lib/middleware';
12
import type { Metrics } from '../Metrics';
23
import type { ExtraOptions } from '../types';
34
import type {
@@ -38,6 +39,18 @@ const logMetrics = (
3839
): MiddlewareLikeObj => {
3940
const metricsInstances = target instanceof Array ? target : [target];
4041

42+
/**
43+
* Set the cleanup function to be called in case other middlewares return early.
44+
*
45+
* @param request - The request object
46+
*/
47+
const setCleanupFunction = (request: MiddyLikeRequest): void => {
48+
request.internal = {
49+
...request.internal,
50+
[METRICS_KEY]: logMetricsAfterOrError,
51+
};
52+
};
53+
4154
const logMetricsBefore = async (request: MiddyLikeRequest): Promise<void> => {
4255
metricsInstances.forEach((metrics: Metrics) => {
4356
metrics.setFunctionName(request.context.functionName);
@@ -53,6 +66,8 @@ const logMetrics = (
5366
metrics.captureColdStartMetric();
5467
}
5568
});
69+
70+
setCleanupFunction(request);
5671
};
5772

5873
const logMetricsAfterOrError = async (): Promise<void> => {

Diff for: packages/metrics/tests/unit/middleware/middy.test.ts

+47-21
Original file line numberDiff line numberDiff line change
@@ -11,34 +11,16 @@ import {
1111
} from '../../../../metrics/src';
1212
import middy from '@middy/core';
1313
import { ExtraOptions } from '../../../src/types';
14+
import { cleanupMiddlewares } from '@aws-lambda-powertools/commons/lib/middleware';
15+
import { helloworldContext as dummyContext } from '../../../../commons/src/samples/resources/contexts/hello-world';
16+
import { CustomEvent as dummyEvent } from '../../../../commons/src/samples/resources/events/custom/index';
1417

1518
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
1619
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
1720
const mockDate = new Date(1466424490000);
1821
jest.spyOn(global, 'Date').mockImplementation(() => mockDate);
1922

2023
describe('Middy middleware', () => {
21-
const dummyEvent = {
22-
key1: 'value1',
23-
key2: 'value2',
24-
key3: 'value3',
25-
};
26-
const dummyContext = {
27-
callbackWaitsForEmptyEventLoop: true,
28-
functionVersion: '$LATEST',
29-
functionName: 'foo-bar-function',
30-
memoryLimitInMB: '128',
31-
logGroupName: '/aws/lambda/foo-bar-function-123456abcdef',
32-
logStreamName: '2021/03/09/[$LATEST]abcdef123456abcdef123456abcdef123456',
33-
invokedFunctionArn:
34-
'arn:aws:lambda:eu-west-1:123456789012:function:Example',
35-
awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678',
36-
getRemainingTimeInMillis: () => 1234,
37-
done: () => console.log('Done!'),
38-
fail: () => console.log('Failed!'),
39-
succeed: () => console.log('Succeeded!'),
40-
};
41-
4224
beforeEach(() => {
4325
jest.resetModules();
4426
jest.clearAllMocks();
@@ -399,6 +381,50 @@ describe('Middy middleware', () => {
399381
})
400382
);
401383
});
384+
385+
test('when enabled, and another middleware returns early, it still publishes the metrics at the end of the execution', async () => {
386+
// Prepare
387+
const metrics = new Metrics({
388+
namespace: 'serverlessAirline',
389+
serviceName: 'orders',
390+
});
391+
const publishStoredMetricsSpy = jest.spyOn(
392+
metrics,
393+
'publishStoredMetrics'
394+
);
395+
const myCustomMiddleware = (): middy.MiddlewareObj => {
396+
const before = async (
397+
request: middy.Request
398+
): Promise<undefined | string> => {
399+
// Return early on the second invocation
400+
if (request.event.idx === 1) {
401+
// Cleanup Powertools resources
402+
await cleanupMiddlewares(request);
403+
404+
// Then return early
405+
return 'foo';
406+
}
407+
};
408+
409+
return {
410+
before,
411+
};
412+
};
413+
const handler = middy(
414+
(_event: typeof dummyEvent & { idx: number }): void => {
415+
metrics.addMetric('successfulBooking', MetricUnits.Count, 1);
416+
}
417+
)
418+
.use(logMetrics(metrics))
419+
.use(myCustomMiddleware());
420+
421+
// Act
422+
await handler({ ...dummyEvent, idx: 0 }, dummyContext);
423+
await handler({ ...dummyEvent, idx: 1 }, dummyContext);
424+
425+
// Assess
426+
expect(publishStoredMetricsSpy).toBeCalledTimes(2);
427+
});
402428
});
403429
describe('Metrics resolution', () => {
404430
test('serialized metrics in EMF format should not contain `StorageResolution` as key if `60` is set', async () => {

0 commit comments

Comments
 (0)