Skip to content

Commit 74ddb09

Browse files
authoredJun 22, 2023
feat(tracer): close & restore segments when other middlewares return (#1545)
* feat(tracer): close & restore segments when other middlewares return early * chore: fix unit test to actually return early
1 parent d5f3f13 commit 74ddb09

File tree

2 files changed

+80
-2
lines changed

2 files changed

+80
-2
lines changed
 

‎packages/tracer/src/middleware/middy.ts

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { TRACER_KEY } from '@aws-lambda-powertools/commons/lib/middleware';
12
import type { Tracer } from '../Tracer';
23
import type { Segment, Subsegment } from 'aws-xray-sdk-core';
34
import type { CaptureLambdaHandlerOptions } from '../types';
@@ -40,6 +41,18 @@ const captureLambdaHandler = (
4041
let lambdaSegment: Segment;
4142
let handlerSegment: Subsegment;
4243

44+
/**
45+
* Set the cleanup function to be called in case other middlewares return early.
46+
*
47+
* @param request - The request object
48+
*/
49+
const setCleanupFunction = (request: MiddyLikeRequest): void => {
50+
request.internal = {
51+
...request.internal,
52+
[TRACER_KEY]: close,
53+
};
54+
};
55+
4356
const open = (): void => {
4457
const segment = target.getSegment();
4558
if (segment === undefined) {
@@ -61,9 +74,12 @@ const captureLambdaHandler = (
6174
target.setSegment(lambdaSegment);
6275
};
6376

64-
const captureLambdaHandlerBefore = async (): Promise<void> => {
77+
const captureLambdaHandlerBefore = async (
78+
request: MiddyLikeRequest
79+
): Promise<void> => {
6580
if (target.isTracingEnabled()) {
6681
open();
82+
setCleanupFunction(request);
6783
target.annotateColdStart();
6884
target.addServiceNameAnnotation();
6985
}

‎packages/tracer/tests/unit/middy.test.ts

+63-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
*
44
* @group unit/tracer/all
55
*/
6-
76
import { captureLambdaHandler } from '../../src/middleware/middy';
87
import middy from '@middy/core';
98
import { Tracer } from './../../src';
@@ -13,6 +12,7 @@ import {
1312
setContextMissingStrategy,
1413
Subsegment,
1514
} from 'aws-xray-sdk-core';
15+
import { cleanupMiddlewares } from '@aws-lambda-powertools/commons/lib/middleware';
1616

1717
jest.spyOn(console, 'debug').mockImplementation(() => null);
1818
jest.spyOn(console, 'warn').mockImplementation(() => null);
@@ -306,4 +306,66 @@ describe('Middy middleware', () => {
306306
'hello-world'
307307
);
308308
});
309+
310+
test('when enabled, and another middleware returns early, it still closes and restores the segments correctly', async () => {
311+
// Prepare
312+
const tracer = new Tracer();
313+
const setSegmentSpy = jest
314+
.spyOn(tracer.provider, 'setSegment')
315+
.mockImplementation(() => ({}));
316+
jest.spyOn(tracer, 'annotateColdStart').mockImplementation(() => ({}));
317+
jest
318+
.spyOn(tracer, 'addServiceNameAnnotation')
319+
.mockImplementation(() => ({}));
320+
const facadeSegment1 = new Segment('facade');
321+
const handlerSubsegment1 = new Subsegment('## index.handlerA');
322+
jest
323+
.spyOn(facadeSegment1, 'addNewSubsegment')
324+
.mockImplementation(() => handlerSubsegment1);
325+
const facadeSegment2 = new Segment('facade');
326+
const handlerSubsegment2 = new Subsegment('## index.handlerB');
327+
jest
328+
.spyOn(facadeSegment2, 'addNewSubsegment')
329+
.mockImplementation(() => handlerSubsegment2);
330+
jest
331+
.spyOn(tracer.provider, 'getSegment')
332+
.mockImplementationOnce(() => facadeSegment1)
333+
.mockImplementationOnce(() => facadeSegment2);
334+
const myCustomMiddleware = (): middy.MiddlewareObj => {
335+
const before = async (
336+
request: middy.Request
337+
): Promise<undefined | string> => {
338+
// Return early on the second invocation
339+
if (request.event.idx === 1) {
340+
// Cleanup Powertools resources
341+
await cleanupMiddlewares(request);
342+
343+
// Then return early
344+
return 'foo';
345+
}
346+
};
347+
348+
return {
349+
before,
350+
};
351+
};
352+
const handler = middy((): void => {
353+
console.log('Hello world!');
354+
})
355+
.use(captureLambdaHandler(tracer, { captureResponse: false }))
356+
.use(myCustomMiddleware());
357+
358+
// Act
359+
await handler({ idx: 0 }, context);
360+
await handler({ idx: 1 }, context);
361+
362+
// Assess
363+
// Check that the subsegments are closed
364+
expect(handlerSubsegment1.isClosed()).toBe(true);
365+
expect(handlerSubsegment2.isClosed()).toBe(true);
366+
// Check that the segments are restored
367+
expect(setSegmentSpy).toHaveBeenCalledTimes(4);
368+
expect(setSegmentSpy).toHaveBeenNthCalledWith(2, facadeSegment1);
369+
expect(setSegmentSpy).toHaveBeenNthCalledWith(4, facadeSegment2);
370+
});
309371
});

0 commit comments

Comments
 (0)
Please sign in to comment.