Skip to content

Commit 5b3d6a5

Browse files
committed
fix: middleware now stores initial persistent attributes correctly
1 parent af55f01 commit 5b3d6a5

File tree

3 files changed

+119
-11
lines changed

3 files changed

+119
-11
lines changed

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ const injectLambdaContext = (target: Logger | Logger[], options?: HandlerOptions
3535
const persistentAttributes: LogAttributes[] = [];
3636

3737
const injectLambdaContextBefore = async (request: MiddyLikeRequest): Promise<void> => {
38-
loggers.forEach((logger: Logger) => {
38+
loggers.forEach((logger: Logger, index: number) => {
3939
if (options && options.clearState === true) {
40-
persistentAttributes.push({ ...logger.getPersistentLogAttributes() });
40+
persistentAttributes[index] = ({ ...logger.getPersistentLogAttributes() });
4141
}
4242
Logger.injectLambdaContextBefore(logger, request.event, request.context, options);
4343
});

Diff for: packages/logger/tests/unit/Logger.test.ts

+68-9
Original file line numberDiff line numberDiff line change
@@ -1227,11 +1227,10 @@ describe('Class: Logger', () => {
12271227
test('when used as decorator on an async method, the method is awaited correctly', async () => {
12281228

12291229
// Prepare
1230-
const injectLambdaContextAfterOrOnErrorMock = jest.fn().mockReturnValue('worked');
1231-
// Temporarily override the cleanup static method so that we can "spy" on it.
1232-
// This method is always called after the handler has returned in the decorator
1233-
// implementation.
1234-
Logger.injectLambdaContextAfterOrOnError = injectLambdaContextAfterOrOnErrorMock;
1230+
const injectLambdaContextAfterOrOnErrorSpy = jest.spyOn(
1231+
Logger,
1232+
'injectLambdaContextAfterOrOnError'
1233+
);
12351234
const logger = new Logger({
12361235
logLevel: 'DEBUG',
12371236
});
@@ -1259,10 +1258,70 @@ describe('Class: Logger', () => {
12591258

12601259
// Assess
12611260
expect(consoleSpy).toBeCalledTimes(1);
1262-
// Here we assert that the logger.info method is called before the cleanup function that should awlays
1263-
// be called after the handler has returned. If logger.info is called after it means the decorator is
1264-
// NOT awaiting the handler which would cause the test to fail.
1265-
expect(consoleSpy.mock.invocationCallOrder[0]).toBeLessThan(injectLambdaContextAfterOrOnErrorMock.mock.invocationCallOrder[0]);
1261+
// Here we assert that the logger.info method is called before the cleanup function that should always
1262+
// be called ONLY after the handler has returned. If logger.info is called after the cleanup function
1263+
// it means the decorator is NOT awaiting the handler which would cause the test to fail.
1264+
expect(consoleSpy.mock.invocationCallOrder[0])
1265+
.toBeLessThan(injectLambdaContextAfterOrOnErrorSpy.mock.invocationCallOrder[0]);
1266+
1267+
});
1268+
1269+
test('when logEvent and clearState are both TRUE, and the logger has persistent attributes, any key added in the handler is cleared properly', async () => {
1270+
1271+
// Prepare
1272+
const logger = new Logger({
1273+
persistentLogAttributes: {
1274+
version: '1.0.0',
1275+
}
1276+
});
1277+
const consoleSpy = jest.spyOn(logger['console'], 'info').mockImplementation();
1278+
class LambdaFunction implements LambdaInterface {
1279+
@logger.injectLambdaContext({ clearState: true, logEvent: true })
1280+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
1281+
// @ts-ignore
1282+
public async handler(event: { foo: string }, _context: unknown): Promise<unknown> {
1283+
logger.appendKeys({ foo: event.foo });
1284+
1285+
return;
1286+
}
1287+
}
1288+
const lambda = new LambdaFunction();
1289+
const handler = lambda.handler.bind(lambda);
1290+
1291+
// Act
1292+
await handler({ foo: 'bar' }, {} as Context);
1293+
await handler({ foo: 'baz' }, {} as Context);
1294+
await handler({ foo: 'biz' }, {} as Context);
1295+
await handler({ foo: 'buz' }, {} as Context);
1296+
await handler({ foo: 'boz' }, {} as Context);
1297+
1298+
expect(consoleSpy).toBeCalledTimes(5);
1299+
for (let i = 1; i === 5; i++) {
1300+
expect(consoleSpy).toHaveBeenNthCalledWith(
1301+
i,
1302+
expect.stringContaining('\"message\":\"Lambda invocation event\"'),
1303+
);
1304+
expect(consoleSpy).toHaveBeenNthCalledWith(
1305+
i,
1306+
expect.stringContaining('\"version\":\"1.0.0\"'),
1307+
);
1308+
}
1309+
expect(consoleSpy).toHaveBeenNthCalledWith(
1310+
2,
1311+
expect.not.stringContaining('\"foo\":\"bar\"')
1312+
);
1313+
expect(consoleSpy).toHaveBeenNthCalledWith(
1314+
3,
1315+
expect.not.stringContaining('\"foo\":\"baz\"')
1316+
);
1317+
expect(consoleSpy).toHaveBeenNthCalledWith(
1318+
4,
1319+
expect.not.stringContaining('\"foo\":\"biz\"')
1320+
);
1321+
expect(consoleSpy).toHaveBeenNthCalledWith(
1322+
5,
1323+
expect.not.stringContaining('\"foo\":\"buz\"')
1324+
);
12661325

12671326
});
12681327

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

+49
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { Logger } from './../../../src';
1010
import middy from '@middy/core';
1111
import { PowertoolLogFormatter } from '../../../src/formatter';
1212
import { Console } from 'console';
13+
import { Context } from 'aws-lambda';
1314

1415
const mockDate = new Date(1466424490000);
1516
const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate);
@@ -491,6 +492,54 @@ describe('Middy middleware', () => {
491492
}));
492493
});
493494

495+
test('when logEvent and clearState are both TRUE, and the logger has persistent attributes, any key added in the handler is cleared properly', async () => {
496+
497+
const logger = new Logger({
498+
persistentLogAttributes: {
499+
version: '1.0.0',
500+
}
501+
});
502+
const consoleSpy = jest.spyOn(logger['console'], 'info').mockImplementation();
503+
const handler = middy(async (event: { foo: string }, _context: Context) => {
504+
logger.appendKeys({ foo: event.foo });
505+
}).use(injectLambdaContext(logger, { logEvent: true, clearState: true }));
506+
507+
await handler({ foo: 'bar' }, {} as Context);
508+
await handler({ foo: 'baz' }, {} as Context);
509+
await handler({ foo: 'biz' }, {} as Context);
510+
await handler({ foo: 'buz' }, {} as Context);
511+
await handler({ foo: 'boz' }, {} as Context);
512+
513+
expect(consoleSpy).toBeCalledTimes(5);
514+
for (let i = 1; i === 5; i++) {
515+
expect(consoleSpy).toHaveBeenNthCalledWith(
516+
i,
517+
expect.stringContaining('\"message\":\"Lambda invocation event\"'),
518+
);
519+
expect(consoleSpy).toHaveBeenNthCalledWith(
520+
i,
521+
expect.stringContaining('\"version\":\"1.0.0\"'),
522+
);
523+
}
524+
expect(consoleSpy).toHaveBeenNthCalledWith(
525+
2,
526+
expect.not.stringContaining('\"foo\":\"bar\"')
527+
);
528+
expect(consoleSpy).toHaveBeenNthCalledWith(
529+
3,
530+
expect.not.stringContaining('\"foo\":\"baz\"')
531+
);
532+
expect(consoleSpy).toHaveBeenNthCalledWith(
533+
4,
534+
expect.not.stringContaining('\"foo\":\"biz\"')
535+
);
536+
expect(consoleSpy).toHaveBeenNthCalledWith(
537+
5,
538+
expect.not.stringContaining('\"foo\":\"buz\"')
539+
);
540+
541+
});
542+
494543
});
495544

496545
});

0 commit comments

Comments
 (0)