Skip to content

Commit ff65048

Browse files
shdqdreamorosi
authored andcommitted
feat(logger): align sampling debug logs feature implementation with the other runtimes (#1744)
* test(logger): remove logsSampled field, add default sampleRateValue * test(logger): add tests for sampling debug logs feature * feat(logger): change implementation to make sampling decision at per-function level * refactor(logger): remove redundant createLogger method * refactor(logger): remove getSampleRateValue method * test(logger): improve tests * refactor(logger): return createLogger() back with the detailed comment of the method importance * test(logger): add constructor/custom config/env var priority tests for sampling rate feature, improve description * refactor(logger): address review comments * feat(logger): add refreshSampleRateCalculation method and tests * test(logger): adjust end-to-end tests
1 parent 5b24fb0 commit ff65048

File tree

7 files changed

+552
-175
lines changed

7 files changed

+552
-175
lines changed

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

+78-90
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,6 @@ class Logger extends Utility implements ClassThatLogs {
156156
SILENT: 28,
157157
};
158158

159-
private logsSampled = false;
160-
161159
private persistentLogAttributes?: LogAttributes = {};
162160

163161
private powertoolLogData: PowertoolLogData = <PowertoolLogData>{};
@@ -236,6 +234,7 @@ class Logger extends Utility implements ClassThatLogs {
236234
logLevel: this.getLevelName(),
237235
customConfigService: this.getCustomConfigService(),
238236
logFormatter: this.getLogFormatter(),
237+
sampleRateValue: this.powertoolLogData.sampleRateValue,
239238
};
240239
const parentsPowertoolsLogData = this.getPowertoolLogData();
241240
const childLogger = this.createLogger(
@@ -309,15 +308,6 @@ class Logger extends Utility implements ClassThatLogs {
309308
return this.logEvent;
310309
}
311310

312-
/**
313-
* It returns a boolean value, if true all the logs will be printed.
314-
*
315-
* @returns {boolean}
316-
*/
317-
public getLogsSampled(): boolean {
318-
return this.logsSampled;
319-
}
320-
321311
/**
322312
* It returns the persistent log attributes, which are the attributes
323313
* that will be logged in all log items.
@@ -457,15 +447,14 @@ class Logger extends Utility implements ClassThatLogs {
457447
}
458448

459449
/**
460-
* If the sample rate feature is enabled, the calculation that determines whether the logs
461-
* will actually be printed or not for this invocation is done when the Logger class is
462-
* initialized.
463-
* This method will repeat that calculation (with possible different outcome).
450+
* This method allows recalculating the initial sampling decision for changing
451+
* the log level to DEBUG based on a sample rate value used during initialization,
452+
* potentially yielding a different outcome.
464453
*
465454
* @returns {void}
466455
*/
467456
public refreshSampleRateCalculation(): void {
468-
this.setLogsSampled();
457+
this.setInitialSampleRate(this.powertoolLogData.sampleRateValue);
469458
}
470459

471460
/**
@@ -520,19 +509,6 @@ class Logger extends Utility implements ClassThatLogs {
520509
this.persistentLogAttributes = attributes;
521510
}
522511

523-
/**
524-
* It sets the user-provided sample rate value.
525-
*
526-
* @param {number} [sampleRateValue]
527-
* @returns {void}
528-
*/
529-
public setSampleRateValue(sampleRateValue?: number): void {
530-
this.powertoolLogData.sampleRateValue =
531-
sampleRateValue ||
532-
this.getCustomConfigService()?.getSampleRateValue() ||
533-
this.getEnvVarsService().getSampleRateValue();
534-
}
535-
536512
/**
537513
* It checks whether the current Lambda invocation event should be printed in the logs or not.
538514
*
@@ -560,36 +536,31 @@ class Logger extends Utility implements ClassThatLogs {
560536
}
561537

562538
/**
563-
* Creates a new Logger instance.
539+
* Factory method for instantiating logger instances. Used by `createChild` method.
540+
* Important for customization and subclassing. It allows subclasses, like `MyOwnLogger`,
541+
* to override its behavior while keeping the main business logic in `createChild` intact.
564542
*
565-
* @param {ConstructorOptions} [options]
566-
* @returns {Logger}
543+
* @example
544+
* ```typescript
545+
* // MyOwnLogger subclass
546+
* class MyOwnLogger extends Logger {
547+
* protected createLogger(options?: ConstructorOptions): MyOwnLogger {
548+
* return new MyOwnLogger(options);
549+
* }
550+
* // No need to re-implement business logic from `createChild` and keep track on changes
551+
* public createChild(options?: ConstructorOptions): MyOwnLogger {
552+
* return super.createChild(options) as MyOwnLogger;
553+
* }
554+
* }
555+
* ```
556+
*
557+
* @param {ConstructorOptions} [options] Logger configuration options.
558+
* @returns {Logger} A new logger instance.
567559
*/
568560
protected createLogger(options?: ConstructorOptions): Logger {
569561
return new Logger(options);
570562
}
571563

572-
/**
573-
* Decides whether the current log item should be printed or not.
574-
*
575-
* The decision is based on the log level and the sample rate value.
576-
* A log item will be printed if:
577-
* 1. The log level is greater than or equal to the Logger's log level.
578-
* 2. The log level is less than the Logger's log level, but the
579-
* current sampling value is set to `true`.
580-
*
581-
* @param {number} logLevel
582-
* @returns {boolean}
583-
* @protected
584-
*/
585-
protected shouldPrint(logLevel: number): boolean {
586-
if (logLevel >= this.logLevel) {
587-
return true;
588-
}
589-
590-
return this.getLogsSampled();
591-
}
592-
593564
/**
594565
* It stores information that is printed in all log items.
595566
*
@@ -779,20 +750,6 @@ class Logger extends Utility implements ClassThatLogs {
779750
};
780751
}
781752

782-
/**
783-
* It returns the numeric sample rate value.
784-
*
785-
* @private
786-
* @returns {number}
787-
*/
788-
private getSampleRateValue(): number {
789-
if (!this.powertoolLogData.sampleRateValue) {
790-
this.setSampleRateValue();
791-
}
792-
793-
return this.powertoolLogData.sampleRateValue as number;
794-
}
795-
796753
/**
797754
* It returns true and type guards the log level if a given log level is valid.
798755
*
@@ -806,6 +763,23 @@ class Logger extends Utility implements ClassThatLogs {
806763
return typeof logLevel === 'string' && logLevel in this.logLevelThresholds;
807764
}
808765

766+
/**
767+
* It returns true and type guards the sample rate value if a given value is valid.
768+
*
769+
* @param sampleRateValue
770+
* @private
771+
* @returns {boolean}
772+
*/
773+
private isValidSampleRate(
774+
sampleRateValue?: number
775+
): sampleRateValue is number {
776+
return (
777+
typeof sampleRateValue === 'number' &&
778+
0 <= sampleRateValue &&
779+
sampleRateValue <= 1
780+
);
781+
}
782+
809783
/**
810784
* It prints a given log with given log level.
811785
*
@@ -846,13 +820,12 @@ class Logger extends Utility implements ClassThatLogs {
846820
input: LogItemMessage,
847821
extraInput: LogItemExtraInput
848822
): void {
849-
if (!this.shouldPrint(logLevel)) {
850-
return;
823+
if (logLevel >= this.logLevel) {
824+
this.printLog(
825+
logLevel,
826+
this.createAndPopulateLogItem(logLevel, input, extraInput)
827+
);
851828
}
852-
this.printLog(
853-
logLevel,
854-
this.createAndPopulateLogItem(logLevel, input, extraInput)
855-
);
856829
}
857830

858831
/**
@@ -938,6 +911,37 @@ class Logger extends Utility implements ClassThatLogs {
938911
}
939912
}
940913

914+
/**
915+
* It sets sample rate value with the following prioprity:
916+
* 1. Constructor value
917+
* 2. Custom config service value
918+
* 3. Environment variable value
919+
* 4. Default value (zero)
920+
*
921+
* @private
922+
* @param {number} [sampleRateValue]
923+
* @returns {void}
924+
*/
925+
private setInitialSampleRate(sampleRateValue?: number): void {
926+
this.powertoolLogData.sampleRateValue = 0;
927+
const constructorValue = sampleRateValue;
928+
const customConfigValue =
929+
this.getCustomConfigService()?.getSampleRateValue();
930+
const envVarsValue = this.getEnvVarsService().getSampleRateValue();
931+
for (const value of [constructorValue, customConfigValue, envVarsValue]) {
932+
if (this.isValidSampleRate(value)) {
933+
this.powertoolLogData.sampleRateValue = value;
934+
935+
if (value && randomInt(0, 100) / 100 <= value) {
936+
this.setLogLevel('DEBUG');
937+
this.debug('Setting log level to DEBUG due to sampling rate');
938+
}
939+
940+
return;
941+
}
942+
}
943+
}
944+
941945
/**
942946
* If the log event feature is enabled via env variable, it sets a property that tracks whether
943947
* the event passed to the Lambda function handler should be logged or not.
@@ -976,20 +980,6 @@ class Logger extends Utility implements ClassThatLogs {
976980
}
977981
}
978982

979-
/**
980-
* If the sample rate feature is enabled, it sets a property that tracks whether this Lambda function invocation
981-
* will print logs or not.
982-
*
983-
* @private
984-
* @returns {void}
985-
*/
986-
private setLogsSampled(): void {
987-
const sampleRateValue = this.getSampleRateValue();
988-
this.logsSampled =
989-
sampleRateValue !== undefined &&
990-
(sampleRateValue === 1 || randomInt(0, 100) / 100 <= sampleRateValue);
991-
}
992-
993983
/**
994984
* It configures the Logger instance settings that will affect the Logger's behaviour
995985
* and the content of all logs.
@@ -1014,10 +1004,9 @@ class Logger extends Utility implements ClassThatLogs {
10141004
this.setConsole();
10151005
this.setCustomConfigService(customConfigService);
10161006
this.setInitialLogLevel(logLevel);
1017-
this.setSampleRateValue(sampleRateValue);
1018-
this.setLogsSampled();
10191007
this.setLogFormatter(logFormatter);
10201008
this.setPowertoolLogData(serviceName, environment);
1009+
this.setInitialSampleRate(sampleRateValue);
10211010
this.setLogEvent();
10221011
this.setLogIndentation();
10231012

@@ -1047,7 +1036,6 @@ class Logger extends Utility implements ClassThatLogs {
10471036
environment ||
10481037
this.getCustomConfigService()?.getCurrentEnvironment() ||
10491038
this.getEnvVarsService().getCurrentEnvironment(),
1050-
sampleRateValue: this.getSampleRateValue(),
10511039
serviceName:
10521040
serviceName ||
10531041
this.getCustomConfigService()?.getServiceName() ||

Diff for: packages/logger/src/config/EnvironmentVariablesService.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ class EnvironmentVariablesService
125125
/**
126126
* It returns the value of the POWERTOOLS_LOGGER_SAMPLE_RATE environment variable.
127127
*
128-
* @returns {string|undefined}
128+
* @returns {number|undefined}
129129
*/
130130
public getSampleRateValue(): number | undefined {
131131
const value = this.get(this.sampleRateValueVariable);

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ type LambdaFunctionContext = {
4747
type PowertoolLogData = LogAttributes & {
4848
environment?: Environment;
4949
serviceName: string;
50-
sampleRateValue?: number;
50+
sampleRateValue: number;
5151
lambdaFunctionContext: LambdaFunctionContext;
5252
xRayTraceId?: string;
5353
awsRegion: string;

Diff for: packages/logger/tests/e2e/sampleRate.decorator.test.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,12 @@ describe(`Logger E2E tests, sample rate and injectLambdaContext()`, () => {
8181

8282
if (logMessages.length === 1 && logMessages[0].includes('ERROR')) {
8383
countNotSampled++;
84-
} else if (logMessages.length === 4) {
84+
} else if (
85+
logMessages.length === 5 &&
86+
logMessages[0].includes(
87+
'Setting log level to DEBUG due to sampling rate'
88+
)
89+
) {
8590
countSampled++;
8691
} else {
8792
console.error(`Log group ${logGroupName} contains missing log`);

0 commit comments

Comments
 (0)