diff --git a/package-lock.json b/package-lock.json index e743677bb0..8413791a0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3915,15 +3915,6 @@ "integrity": "sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g==", "dev": true }, - "node_modules/@types/lodash.clonedeep": { - "version": "4.5.7", - "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.7.tgz", - "integrity": "sha512-ccNqkPptFIXrpVqUECi60/DFxjNKsfoQxSQsgcBJCX/fuX1wgyQieojkcWH/KpE3xzLoWN/2k+ZeGqIN3paSvw==", - "dev": true, - "dependencies": { - "@types/lodash": "*" - } - }, "node_modules/@types/lodash.merge": { "version": "4.6.7", "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.7.tgz", @@ -11059,11 +11050,6 @@ "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", "dev": true }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, "node_modules/lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -15542,28 +15528,30 @@ }, "packages/commons": { "name": "@aws-lambda-powertools/commons", - "version": "1.4.0", + "version": "1.4.1", "license": "MIT-0" }, "packages/logger": { "name": "@aws-lambda-powertools/logger", - "version": "1.4.0", + "version": "1.4.1", "license": "MIT", "dependencies": { - "lodash.clonedeep": "^4.5.0", + "@aws-lambda-powertools/commons": "^1.4.1", "lodash.merge": "^4.6.2", "lodash.pickby": "^4.6.0" }, "devDependencies": { - "@types/lodash.clonedeep": "^4.5.7", "@types/lodash.merge": "^4.6.7", "@types/lodash.pickby": "^4.6.7" } }, "packages/metrics": { "name": "@aws-lambda-powertools/metrics", - "version": "1.4.0", + "version": "1.4.1", "license": "MIT-0", + "dependencies": { + "@aws-lambda-powertools/commons": "^1.4.1" + }, "devDependencies": { "@types/promise-retry": "^1.1.3", "promise-retry": "^2.0.1" @@ -15571,9 +15559,10 @@ }, "packages/tracer": { "name": "@aws-lambda-powertools/tracer", - "version": "1.4.0", + "version": "1.4.1", "license": "MIT-0", "dependencies": { + "@aws-lambda-powertools/commons": "^1.4.1", "aws-xray-sdk-core": "^3.3.6" }, "devDependencies": { @@ -15800,10 +15789,9 @@ "@aws-lambda-powertools/logger": { "version": "file:packages/logger", "requires": { - "@types/lodash.clonedeep": "^4.5.7", + "@aws-lambda-powertools/commons": "^1.4.1", "@types/lodash.merge": "^4.6.7", "@types/lodash.pickby": "^4.6.7", - "lodash.clonedeep": "^4.5.0", "lodash.merge": "^4.6.2", "lodash.pickby": "^4.6.0" } @@ -15811,6 +15799,7 @@ "@aws-lambda-powertools/metrics": { "version": "file:packages/metrics", "requires": { + "@aws-lambda-powertools/commons": "^1.4.1", "@types/promise-retry": "^1.1.3", "promise-retry": "^2.0.1" } @@ -15818,6 +15807,7 @@ "@aws-lambda-powertools/tracer": { "version": "file:packages/tracer", "requires": { + "@aws-lambda-powertools/commons": "^1.4.1", "@aws-sdk/client-dynamodb": "^3.100.0", "@types/promise-retry": "^1.1.3", "aws-xray-sdk-core": "^3.3.6", @@ -18772,15 +18762,6 @@ "integrity": "sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g==", "dev": true }, - "@types/lodash.clonedeep": { - "version": "4.5.7", - "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.7.tgz", - "integrity": "sha512-ccNqkPptFIXrpVqUECi60/DFxjNKsfoQxSQsgcBJCX/fuX1wgyQieojkcWH/KpE3xzLoWN/2k+ZeGqIN3paSvw==", - "dev": true, - "requires": { - "@types/lodash": "*" - } - }, "@types/lodash.merge": { "version": "4.6.7", "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.7.tgz", @@ -24326,11 +24307,6 @@ "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", "dev": true }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", diff --git a/packages/logger/package.json b/packages/logger/package.json index 84478e69b9..211969e096 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -32,7 +32,6 @@ "types": "./lib/index.d.ts", "typedocMain": "src/index.ts", "devDependencies": { - "@types/lodash.clonedeep": "^4.5.7", "@types/lodash.merge": "^4.6.7", "@types/lodash.pickby": "^4.6.7" }, @@ -48,7 +47,6 @@ }, "dependencies": { "@aws-lambda-powertools/commons": "^1.4.1", - "lodash.clonedeep": "^4.5.0", "lodash.merge": "^4.6.2", "lodash.pickby": "^4.6.0" }, diff --git a/packages/logger/src/Logger.ts b/packages/logger/src/Logger.ts index 57b9ba1a57..6a57f92eb4 100644 --- a/packages/logger/src/Logger.ts +++ b/packages/logger/src/Logger.ts @@ -3,7 +3,6 @@ import type { Context, Handler } from 'aws-lambda'; import { Utility } from '@aws-lambda-powertools/commons'; import { LogFormatterInterface, PowertoolLogFormatter } from './formatter'; import { LogItem } from './log'; -import cloneDeep from 'lodash.clonedeep'; import merge from 'lodash.merge'; import { ConfigServiceInterface, EnvironmentVariablesService } from './config'; import { LogJsonIndent } from './types'; @@ -151,7 +150,6 @@ class Logger extends Utility implements ClassThatLogs { */ public constructor(options: ConstructorOptions = {}) { super(); - this.setOptions(options); } @@ -205,7 +203,17 @@ class Logger extends Utility implements ClassThatLogs { * @returns {Logger} */ public createChild(options: ConstructorOptions = {}): Logger { - return cloneDeep(this).setOptions(options); + const parentsPowertoolsLogData = this.getPowertoolLogData(); + const childLogger = new Logger(merge({}, parentsPowertoolsLogData, options)); + + const parentsPersistentLogAttributes = this.getPersistentLogAttributes(); + childLogger.addPersistentLogAttributes(parentsPersistentLogAttributes); + + if (parentsPowertoolsLogData.lambdaContext) { + childLogger.addContext(parentsPowertoolsLogData.lambdaContext as Context); + } + + return childLogger; } /** diff --git a/packages/logger/tests/unit/Logger.test.ts b/packages/logger/tests/unit/Logger.test.ts index e5fe9b39b7..03d7c7ae9b 100644 --- a/packages/logger/tests/unit/Logger.test.ts +++ b/packages/logger/tests/unit/Logger.test.ts @@ -1236,6 +1236,107 @@ describe('Class: Logger', () => { describe('Method: createChild', () => { + test('Child and grandchild loggers should have all ancestor\'s options', () => { + // Prepare + const INDENTATION = LogJsonIndent.COMPACT; + const loggerOptions = { + serviceName: 'parent-service-name', + sampleRateValue: 0.01, + }; + const parentLogger = new Logger(loggerOptions); + + // Act + const childLoggerOptions = { sampleRateValue: 1 }; + const childLogger = parentLogger.createChild(childLoggerOptions); + + const grandchildLoggerOptions = { serviceName: 'grandchild-logger-name' }; + const grandchildLogger = childLogger.createChild(grandchildLoggerOptions); + + // Assess + expect(parentLogger === childLogger).toBe(false); + expect(childLogger === grandchildLogger).toBe(false); + expect(parentLogger === grandchildLogger).toBe(false); + + expect(parentLogger).toEqual({ + console: expect.any(Console), + coldStart: true, + customConfigService: undefined, + defaultServiceName: 'service_undefined', + envVarsService: expect.any(EnvironmentVariablesService), + logEvent: false, + logIndentation: INDENTATION, + logFormatter: expect.any(PowertoolLogFormatter), + logLevel: 'DEBUG', + logLevelThresholds: { + DEBUG: 8, + ERROR: 20, + INFO: 12, + WARN: 16, + }, + logsSampled: false, + persistentLogAttributes: {}, + powertoolLogData: { + awsRegion: 'eu-west-1', + environment: '', + sampleRateValue: 0.01, + serviceName: 'parent-service-name', + }, + }); + + expect(childLogger).toEqual({ + console: expect.any(Console), + coldStart: true, + customConfigService: undefined, + defaultServiceName: 'service_undefined', + envVarsService: expect.any(EnvironmentVariablesService), + logEvent: false, + logIndentation: INDENTATION, + logFormatter: expect.any(PowertoolLogFormatter), + logLevel: 'DEBUG', + logLevelThresholds: { + DEBUG: 8, + ERROR: 20, + INFO: 12, + WARN: 16, + }, + logsSampled: true, + persistentLogAttributes: {}, + powertoolLogData: { + awsRegion: 'eu-west-1', + environment: '', + sampleRateValue: 1, + serviceName: 'parent-service-name', + }, + }); + + expect(grandchildLogger).toEqual({ + console: expect.any(Console), + coldStart: true, + customConfigService: undefined, + defaultServiceName: 'service_undefined', + envVarsService: expect.any(EnvironmentVariablesService), + logEvent: false, + logIndentation: INDENTATION, + logFormatter: expect.any(PowertoolLogFormatter), + logLevel: 'DEBUG', + logLevelThresholds: { + DEBUG: 8, + ERROR: 20, + INFO: 12, + WARN: 16, + }, + logsSampled: true, + persistentLogAttributes: {}, + powertoolLogData: { + awsRegion: 'eu-west-1', + environment: '', + sampleRateValue: 1, + serviceName: 'grandchild-logger-name', + }, + }); + + }); + test('when called, it returns a DISTINCT clone of the logger instance', () => { // Prepare @@ -1244,17 +1345,23 @@ describe('Class: Logger', () => { // Act const childLogger = parentLogger.createChild(); - const childLoggerWithPermanentAttributes = parentLogger.createChild({ + + const optionsWithPermanentAttributes = { persistentLogAttributes: { extra: 'This is an attribute that will be logged only by the child logger', }, - }); - const childLoggerWithSampleRateEnabled = parentLogger.createChild({ + }; + const childLoggerWithPermanentAttributes = parentLogger.createChild(optionsWithPermanentAttributes); + + const optionsWithSampleRateEnabled = { sampleRateValue: 1, // 100% probability to make sure that the logs are sampled - }); - const childLoggerWithErrorLogLevel = parentLogger.createChild({ + }; + const childLoggerWithSampleRateEnabled = parentLogger.createChild(optionsWithSampleRateEnabled); + + const optionsWithErrorLogLevel = { logLevel: 'ERROR', - }); + }; + const childLoggerWithErrorLogLevel = parentLogger.createChild(optionsWithErrorLogLevel); // Assess expect(parentLogger === childLogger).toBe(false); @@ -1374,6 +1481,181 @@ describe('Class: Logger', () => { }); + test('child logger should have parent\'s keys in persistentLogAttributes', () => { + + // Prepare + const INDENTATION = LogJsonIndent.COMPACT; + const parentLogger = new Logger(); + const childLogger = parentLogger.createChild(); + + // Act + parentLogger.appendKeys({ + aws_account_id: '123456789012', + aws_region: 'eu-west-1', + logger: { + name: 'aws-lambda-powertool-typescript', + version: '0.2.4', + }, + test_key: 'key-for-test' + }); + const childLoggerWithKeys = parentLogger.createChild(); + childLoggerWithKeys.removeKeys(['test_key']); + + // Assess + expect(childLogger).toEqual({ + console: expect.any(Console), + coldStart: true, + customConfigService: undefined, + defaultServiceName: 'service_undefined', + envVarsService: expect.any(EnvironmentVariablesService), + logEvent: false, + logIndentation: INDENTATION, + logFormatter: expect.any(PowertoolLogFormatter), + logLevel: 'DEBUG', + logLevelThresholds: { + DEBUG: 8, + ERROR: 20, + INFO: 12, + WARN: 16, + }, + logsSampled: false, + persistentLogAttributes: {}, + powertoolLogData: { + awsRegion: 'eu-west-1', + environment: '', + sampleRateValue: undefined, + serviceName: 'hello-world', + }, + }); + + expect(childLoggerWithKeys).toEqual({ + console: expect.any(Console), + coldStart: true, + customConfigService: undefined, + defaultServiceName: 'service_undefined', + envVarsService: expect.any(EnvironmentVariablesService), + logEvent: false, + logIndentation: INDENTATION, + logFormatter: expect.any(PowertoolLogFormatter), + logLevel: 'DEBUG', + logLevelThresholds: { + DEBUG: 8, + ERROR: 20, + INFO: 12, + WARN: 16, + }, + logsSampled: false, + persistentLogAttributes: { + aws_account_id: '123456789012', + aws_region: 'eu-west-1', + logger: { + name: 'aws-lambda-powertool-typescript', + version: '0.2.4', + }, + }, + powertoolLogData: { + awsRegion: 'eu-west-1', + environment: '', + sampleRateValue: undefined, + serviceName: 'hello-world', + }, + }); + + expect(parentLogger).toEqual({ + console: expect.any(Console), + coldStart: true, + customConfigService: undefined, + defaultServiceName: 'service_undefined', + envVarsService: expect.any(EnvironmentVariablesService), + logEvent: false, + logIndentation: INDENTATION, + logFormatter: expect.any(PowertoolLogFormatter), + logLevel: 'DEBUG', + logLevelThresholds: { + DEBUG: 8, + ERROR: 20, + INFO: 12, + WARN: 16, + }, + logsSampled: false, + persistentLogAttributes: { + aws_account_id: '123456789012', + aws_region: 'eu-west-1', + logger: { + name: 'aws-lambda-powertool-typescript', + version: '0.2.4', + }, + test_key: 'key-for-test', + }, + powertoolLogData: { + awsRegion: 'eu-west-1', + environment: '', + sampleRateValue: undefined, + serviceName: 'hello-world', + }, + }); + }); + + const context = { + callbackWaitsForEmptyEventLoop: true, + functionVersion: '$LATEST', + functionName: 'foo-bar-function-with-cold-start', + memoryLimitInMB: '128', + logGroupName: '/aws/lambda/foo-bar-function-with-cold-start', + logStreamName: '2021/03/09/[$LATEST]1-5759e988-bd862e3fe1be46a994272793', + invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function-with-cold-start', + awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678', + getRemainingTimeInMillis: () => 1234, + done: () => console.log('Done!'), + fail: () => console.log('Failed!'), + succeed: () => console.log('Succeeded!'), + }; + + test('child logger should have parent\'s context in PowertoolLogData', () => { + + // Prepare + const parentLogger = new Logger(); + + // Act + parentLogger.addContext(context); + const childLoggerWithContext = parentLogger.createChild(); + + // Assess + expect(childLoggerWithContext).toEqual({ + console: expect.any(Console), + coldStart: false, // This is now false because the `coldStart` attribute has been already accessed once by the `addContext` method + customConfigService: undefined, + defaultServiceName: 'service_undefined', + envVarsService: expect.any(EnvironmentVariablesService), + logEvent: false, + logIndentation: 0, + logFormatter: expect.any(PowertoolLogFormatter), + logLevel: 'DEBUG', + logLevelThresholds: { + DEBUG: 8, + ERROR: 20, + INFO: 12, + WARN: 16, + }, + logsSampled: false, + persistentLogAttributes: {}, + powertoolLogData: { + awsRegion: 'eu-west-1', + environment: '', + lambdaContext: { + awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678', + coldStart: true, + functionName: 'foo-bar-function-with-cold-start', + functionVersion: '$LATEST', + invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function-with-cold-start', + memoryLimitInMB: 128, + }, + sampleRateValue: undefined, + serviceName: 'hello-world', + }, + }); + }); + }); describe('Method: logEventIfEnabled', () => { diff --git a/packages/logger/tests/unit/helpers.test.ts b/packages/logger/tests/unit/helpers.test.ts index 3a4298421b..4f953620fc 100644 --- a/packages/logger/tests/unit/helpers.test.ts +++ b/packages/logger/tests/unit/helpers.test.ts @@ -230,7 +230,7 @@ describe('Helper: createLogger function', () => { test('when no log level is set, returns a Logger instance with INFO level', () => { // Prepare - const loggerOptions:ConstructorOptions = {}; + const loggerOptions: ConstructorOptions = {}; delete process.env.LOG_LEVEL; // Act