Skip to content

Commit ea62314

Browse files
shdqdreamorosi
authored andcommitted
feat(logger): add clearState() method (#2408)
Co-authored-by: Andrea Amorosi <[email protected]>
1 parent 9d0dd3b commit ea62314

File tree

5 files changed

+888
-138
lines changed

5 files changed

+888
-138
lines changed

Diff for: packages/logger/src/Logger.ts

+102-34
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ class Logger extends Utility implements LoggerInterface {
172172
* Standard attributes managed by Powertools that will be logged in all log items.
173173
*/
174174
private powertoolsLogData: PowertoolsLogData = <PowertoolsLogData>{};
175+
/**
176+
* Temporary log attributes that can be appended with `appendKeys()` method.
177+
*/
178+
private temporaryLogAttributes: LogAttributes = {};
175179
/**
176180
* Buffer used to store logs until the logger is initialized.
177181
*
@@ -183,6 +187,13 @@ class Logger extends Utility implements LoggerInterface {
183187
* Flag used to determine if the logger is initialized.
184188
*/
185189
#isInitialized = false;
190+
/**
191+
* Map used to hold the list of keys and their type.
192+
*
193+
* Because keys of different types can be overwritten, we keep a list of keys that were added and their last
194+
* type. We then use this map at log preparation time to pick the last one.
195+
*/
196+
#keys: Map<string, 'temp' | 'persistent'> = new Map();
186197

187198
/**
188199
* Log level used by the current instance of Logger.
@@ -234,23 +245,40 @@ class Logger extends Utility implements LoggerInterface {
234245
}
235246

236247
/**
237-
* It adds the given attributes (key-value pairs) to all log items generated by this Logger instance.
248+
* It adds the given persistent attributes (key-value pairs) to all log items generated by this Logger instance.
249+
*
250+
* @deprecated This method is deprecated and will be removed in the future major versions, please use {@link appendPersistentKeys()} instead.
238251
*
239252
* @param {LogAttributes} attributes
240253
* @returns {void}
241254
*/
242-
public addPersistentLogAttributes(attributes?: LogAttributes): void {
243-
merge(this.persistentLogAttributes, attributes);
255+
public addPersistentLogAttributes(attributes: LogAttributes): void {
256+
this.appendPersistentKeys(attributes);
244257
}
245258

246259
/**
247-
* Alias for addPersistentLogAttributes.
260+
* It adds the given temporary attributes (key-value pairs) to all log items generated by this Logger instance.
248261
*
249262
* @param {LogAttributes} attributes
250263
* @returns {void}
251264
*/
252-
public appendKeys(attributes?: LogAttributes): void {
253-
this.addPersistentLogAttributes(attributes);
265+
public appendKeys(attributes: LogAttributes): void {
266+
for (const attributeKey of Object.keys(attributes)) {
267+
this.#keys.set(attributeKey, 'temp');
268+
}
269+
merge(this.temporaryLogAttributes, attributes);
270+
}
271+
272+
/**
273+
* It adds the given persistent attributes (key-value pairs) to all log items generated by this Logger instance.
274+
*
275+
* @param attributes - The attributes to add to all log items.
276+
*/
277+
public appendPersistentKeys(attributes: LogAttributes): void {
278+
for (const attributeKey of Object.keys(attributes)) {
279+
this.#keys.set(attributeKey, 'persistent');
280+
}
281+
merge(this.persistentLogAttributes, attributes);
254282
}
255283

256284
/**
@@ -274,6 +302,7 @@ class Logger extends Utility implements LoggerInterface {
274302
customConfigService: this.getCustomConfigService(),
275303
environment: this.powertoolsLogData.environment,
276304
persistentLogAttributes: this.persistentLogAttributes,
305+
temporaryLogAttributes: this.temporaryLogAttributes,
277306
},
278307
options
279308
)
@@ -417,13 +446,6 @@ class Logger extends Utility implements LoggerInterface {
417446
context,
418447
callback
419448
) {
420-
let initialPersistentAttributes = {};
421-
if (options && options.clearState === true) {
422-
initialPersistentAttributes = {
423-
...loggerRef.getPersistentLogAttributes(),
424-
};
425-
}
426-
427449
Logger.injectLambdaContextBefore(loggerRef, event, context, options);
428450

429451
let result: unknown;
@@ -432,25 +454,25 @@ class Logger extends Utility implements LoggerInterface {
432454
} catch (error) {
433455
throw error;
434456
} finally {
435-
Logger.injectLambdaContextAfterOrOnError(
436-
loggerRef,
437-
initialPersistentAttributes,
438-
options
439-
);
457+
if (options?.clearState) loggerRef.resetState();
440458
}
441459

442460
return result;
443461
};
444462
};
445463
}
446464

465+
/**
466+
* @deprecated This method is deprecated and will be removed in the future major versions. Use {@link resetState()} instead.
467+
*/
468+
/* istanbul ignore next */
447469
public static injectLambdaContextAfterOrOnError(
448470
logger: Logger,
449-
initialPersistentAttributes: LogAttributes,
471+
_persistentAttributes: LogAttributes,
450472
options?: InjectLambdaContextOptions
451473
): void {
452474
if (options && options.clearState === true) {
453-
logger.setPersistentLogAttributes(initialPersistentAttributes);
475+
logger.resetState();
454476
}
455477
}
456478

@@ -493,27 +515,53 @@ class Logger extends Utility implements LoggerInterface {
493515
}
494516

495517
/**
496-
* Alias for removePersistentLogAttributes.
518+
* It removes temporary attributes based on provided keys to all log items generated by this Logger instance.
497519
*
498520
* @param {string[]} keys
499521
* @returns {void}
500522
*/
501523
public removeKeys(keys: string[]): void {
502-
this.removePersistentLogAttributes(keys);
524+
for (const key of keys) {
525+
this.temporaryLogAttributes[key] = undefined;
526+
527+
if (this.persistentLogAttributes[key]) {
528+
this.#keys.set(key, 'persistent');
529+
} else {
530+
this.#keys.delete(key);
531+
}
532+
}
503533
}
504534

505535
/**
506-
* It removes attributes based on provided keys to all log items generated by this Logger instance.
536+
* It removes persistent attributes based on provided keys to all log items generated by this Logger instance.
507537
*
508538
* @param {string[]} keys
509539
* @returns {void}
510540
*/
511541
public removePersistentLogAttributes(keys: string[]): void {
512542
for (const key of keys) {
513-
if (this.persistentLogAttributes && key in this.persistentLogAttributes) {
514-
delete this.persistentLogAttributes[key];
543+
this.persistentLogAttributes[key] = undefined;
544+
545+
if (this.temporaryLogAttributes[key]) {
546+
this.#keys.set(key, 'temp');
547+
} else {
548+
this.#keys.delete(key);
549+
}
550+
}
551+
}
552+
553+
/**
554+
* It resets the state, by removing all temporary log attributes added with `appendKeys()` method.
555+
*/
556+
public resetState(): void {
557+
for (const key of Object.keys(this.temporaryLogAttributes)) {
558+
if (this.persistentLogAttributes[key]) {
559+
this.#keys.set(key, 'persistent');
560+
} else {
561+
this.#keys.delete(key);
515562
}
516563
}
564+
this.temporaryLogAttributes = {};
517565
}
518566

519567
/**
@@ -537,6 +585,8 @@ class Logger extends Utility implements LoggerInterface {
537585
* It sets the given attributes (key-value pairs) to all log items generated by this Logger instance.
538586
* Note: this replaces the pre-existing value.
539587
*
588+
* @deprecated This method is deprecated and will be removed in the future major versions, please use {@link appendPersistentKeys()} instead.
589+
*
540590
* @param {LogAttributes} attributes
541591
* @returns {void}
542592
*/
@@ -665,10 +715,18 @@ class Logger extends Utility implements LoggerInterface {
665715
...this.getPowertoolsLogData(),
666716
};
667717

668-
// gradually merge additional attributes starting from customer-provided persistent attributes
669-
let additionalLogAttributes = { ...this.getPersistentLogAttributes() };
718+
const additionalAttributes: LogAttributes = {};
719+
// gradually add additional attributes picking only the last added for each key
720+
for (const [key, type] of this.#keys) {
721+
if (type === 'persistent') {
722+
additionalAttributes[key] = this.persistentLogAttributes[key];
723+
} else {
724+
additionalAttributes[key] = this.temporaryLogAttributes[key];
725+
}
726+
}
727+
670728
// if the main input is not a string, then it's an object with additional attributes, so we merge it
671-
additionalLogAttributes = merge(additionalLogAttributes, otherInput);
729+
merge(additionalAttributes, otherInput);
672730
// then we merge the extra input attributes (if any)
673731
for (const item of extraInput) {
674732
const attributes: LogAttributes =
@@ -678,12 +736,12 @@ class Logger extends Utility implements LoggerInterface {
678736
? { extra: item }
679737
: item;
680738

681-
additionalLogAttributes = merge(additionalLogAttributes, attributes);
739+
merge(additionalAttributes, attributes);
682740
}
683741

684742
return this.getLogFormatter().formatAttributes(
685743
unformattedBaseAttributes,
686-
additionalLogAttributes
744+
additionalAttributes
687745
);
688746
}
689747

@@ -1023,13 +1081,23 @@ class Logger extends Utility implements LoggerInterface {
10231081
serviceName,
10241082
sampleRateValue,
10251083
logFormatter,
1026-
persistentLogAttributes,
1084+
persistentKeys,
1085+
persistentLogAttributes, // deprecated in favor of persistentKeys
10271086
environment,
10281087
} = options;
10291088

1089+
if (persistentLogAttributes && persistentKeys) {
1090+
this.warn(
1091+
'Both persistentLogAttributes and persistentKeys options were provided. Using persistentKeys as persistentLogAttributes is deprecated and will be removed in future releases'
1092+
);
1093+
}
1094+
10301095
// configurations that affect log content
1031-
this.setPowertoolsLogData(serviceName, environment);
1032-
this.addPersistentLogAttributes(persistentLogAttributes);
1096+
this.setPowertoolsLogData(
1097+
serviceName,
1098+
environment,
1099+
persistentKeys || persistentLogAttributes
1100+
);
10331101

10341102
// configurations that affect Logger behavior
10351103
this.setLogEvent();
@@ -1070,7 +1138,7 @@ class Logger extends Utility implements LoggerInterface {
10701138
this.getEnvVarsService().getServiceName() ||
10711139
this.getDefaultServiceName(),
10721140
});
1073-
this.addPersistentLogAttributes(persistentLogAttributes);
1141+
this.appendPersistentKeys(persistentLogAttributes);
10741142
}
10751143
}
10761144

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

+3-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Logger } from '../Logger.js';
2-
import type { LogAttributes } from '../types/Log.js';
32
import type { InjectLambdaContextOptions } from '../types/Logger.js';
43
import { LOGGER_KEY } from '@aws-lambda-powertools/commons';
54
import type {
@@ -37,7 +36,6 @@ const injectLambdaContext = (
3736
options?: InjectLambdaContextOptions
3837
): MiddlewareLikeObj => {
3938
const loggers = target instanceof Array ? target : [target];
40-
const persistentAttributes: LogAttributes[] = [];
4139
const isClearState = options && options.clearState === true;
4240

4341
/**
@@ -55,12 +53,8 @@ const injectLambdaContext = (
5553
const injectLambdaContextBefore = async (
5654
request: MiddyLikeRequest
5755
): Promise<void> => {
58-
loggers.forEach((logger: Logger, index: number) => {
56+
loggers.forEach((logger: Logger) => {
5957
if (isClearState) {
60-
persistentAttributes[index] = {
61-
...logger.getPersistentLogAttributes(),
62-
};
63-
6458
setCleanupFunction(request);
6559
}
6660
Logger.injectLambdaContextBefore(
@@ -74,12 +68,8 @@ const injectLambdaContext = (
7468

7569
const injectLambdaContextAfterOrOnError = async (): Promise<void> => {
7670
if (isClearState) {
77-
loggers.forEach((logger: Logger, index: number) => {
78-
Logger.injectLambdaContextAfterOrOnError(
79-
logger,
80-
persistentAttributes[index],
81-
options
82-
);
71+
loggers.forEach((logger: Logger) => {
72+
logger.resetState();
8373
});
8474
}
8575
};

Diff for: packages/logger/src/types/Logger.ts

+27-2
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,41 @@ type InjectLambdaContextOptions = {
2121
clearState?: boolean;
2222
};
2323

24-
type ConstructorOptions = {
24+
type BaseConstructorOptions = {
2525
logLevel?: LogLevel;
2626
serviceName?: string;
2727
sampleRateValue?: number;
2828
logFormatter?: LogFormatterInterface;
2929
customConfigService?: ConfigServiceInterface;
30-
persistentLogAttributes?: LogAttributes;
3130
environment?: Environment;
3231
};
3332

33+
type PersistentKeysOption = {
34+
persistentKeys?: LogAttributes;
35+
persistentLogAttributes?: never;
36+
};
37+
38+
type DeprecatedOption = {
39+
persistentLogAttributes?: LogAttributes;
40+
persistentKeys?: never;
41+
};
42+
43+
/**
44+
* Options for the Logger class constructor.
45+
*
46+
* @type {Object} ConstructorOptions
47+
* @property {LogLevel} [logLevel] - The log level.
48+
* @property {string} [serviceName] - The service name.
49+
* @property {number} [sampleRateValue] - The sample rate value.
50+
* @property {LogFormatterInterface} [logFormatter] - The custom log formatter.
51+
* @property {ConfigServiceInterface} [customConfigService] - The custom config service.
52+
* @property {Environment} [environment] - The environment.
53+
* @property {LogAttributes} [persistentKeys] - The keys that will be persisted in all log items.
54+
* @property {LogAttributes} [persistentLogAttributes] - **Deprecated!** Use `persistentKeys`.
55+
*/
56+
type ConstructorOptions = BaseConstructorOptions &
57+
(PersistentKeysOption | DeprecatedOption);
58+
3459
type LambdaFunctionContext = Pick<
3560
Context,
3661
| 'functionName'

0 commit comments

Comments
 (0)