@@ -126,23 +126,31 @@ class Logger extends Utility implements LoggerInterface {
126
126
* @private
127
127
*/
128
128
private console ! : Console ;
129
-
129
+ /**
130
+ * Custom config service instance used to configure the logger.
131
+ */
130
132
private customConfigService ?: ConfigServiceInterface ;
131
-
132
- // envVarsService is always initialized in the constructor in setOptions()
133
- private envVarsService ! : EnvironmentVariablesService ;
134
-
133
+ /**
134
+ * Environment variables service instance used to fetch environment variables.
135
+ */
136
+ private envVarsService = new EnvironmentVariablesService ( ) ;
137
+ /**
138
+ * Whether to print the Lambda invocation event in the logs.
139
+ */
135
140
private logEvent = false ;
136
-
141
+ /**
142
+ * Formatter used to format the log items.
143
+ * @default new PowertoolsLogFormatter()
144
+ */
137
145
private logFormatter ?: LogFormatterInterface ;
138
-
146
+ /**
147
+ * JSON indentation used to format the logs.
148
+ */
139
149
private logIndentation : number = LogJsonIndent . COMPACT ;
140
-
141
150
/**
142
151
* Log level used internally by the current instance of Logger.
143
152
*/
144
153
private logLevel = 12 ;
145
-
146
154
/**
147
155
* Log level thresholds used internally by the current instance of Logger.
148
156
*
@@ -156,10 +164,25 @@ class Logger extends Utility implements LoggerInterface {
156
164
CRITICAL : 24 ,
157
165
SILENT : 28 ,
158
166
} ;
159
-
167
+ /**
168
+ * Persistent log attributes that will be logged in all log items.
169
+ */
160
170
private persistentLogAttributes : LogAttributes = { } ;
161
-
171
+ /**
172
+ * Standard attributes managed by Powertools that will be logged in all log items.
173
+ */
162
174
private powertoolsLogData : PowertoolsLogData = < PowertoolsLogData > { } ;
175
+ /**
176
+ * Buffer used to store logs until the logger is initialized.
177
+ *
178
+ * Sometimes we need to log warnings before the logger is fully initialized, however we can't log them
179
+ * immediately because the logger is not ready yet. This buffer stores those logs until the logger is ready.
180
+ */
181
+ #buffer: [ number , Parameters < Logger [ 'createAndPopulateLogItem' ] > ] [ ] = [ ] ;
182
+ /**
183
+ * Flag used to determine if the logger is initialized.
184
+ */
185
+ #isInitialized = false ;
163
186
164
187
/**
165
188
* Log level used by the current instance of Logger.
@@ -178,7 +201,16 @@ class Logger extends Utility implements LoggerInterface {
178
201
*/
179
202
public constructor ( options : ConstructorOptions = { } ) {
180
203
super ( ) ;
181
- this . setOptions ( options ) ;
204
+ const { customConfigService, ...rest } = options ;
205
+ this . setCustomConfigService ( customConfigService ) ;
206
+ // all logs are buffered until the logger is initialized
207
+ this . setOptions ( rest ) ;
208
+ this . #isInitialized = true ;
209
+ for ( const [ level , log ] of this . #buffer) {
210
+ // we call the method directly and create the log item just in time
211
+ this . printLog ( level , this . createAndPopulateLogItem ( ...log ) ) ;
212
+ }
213
+ this . #buffer = [ ] ;
182
214
}
183
215
184
216
/**
@@ -596,41 +628,49 @@ class Logger extends Utility implements LoggerInterface {
596
628
}
597
629
598
630
/**
599
- * It processes a particular log item so that it can be printed to stdout:
600
- * - Merges ephemeral log attributes with persistent log attributes (printed for all logs) and additional info;
601
- * - Formats all the log attributes;
631
+ * Create a log item and populate it with the given log level, input, and extra input.
602
632
*
603
- * @private
604
- * @param {number } logLevel
605
- * @param {LogItemMessage } input
606
- * @param {LogItemExtraInput } extraInput
607
- * @returns {LogItem }
633
+ * We start with creating an object with base attributes managed by Powertools.
634
+ * Then we create a second object with persistent attributes provided by customers either
635
+ * directly to the log entry or through initial configuration and `appendKeys` method.
636
+ *
637
+ * Once we have the two objects, we pass them to the formatter that will apply the desired
638
+ * formatting to the log item.
639
+ *
640
+ * @param logLevel The log level of the log item to be printed
641
+ * @param input The main input of the log item, this can be a string or an object with additional attributes
642
+ * @param extraInput Additional attributes to be added to the log item
608
643
*/
609
644
private createAndPopulateLogItem (
610
645
logLevel : number ,
611
646
input : LogItemMessage ,
612
647
extraInput : LogItemExtraInput
613
648
) : LogItem {
614
- // TODO: this method's logic is hard to understand, there is an opportunity here to simplify this logic.
615
- const unformattedBaseAttributes = merge (
616
- {
617
- logLevel : this . getLogLevelNameFromNumber ( logLevel ) ,
618
- timestamp : new Date ( ) ,
619
- message : typeof input === 'string' ? input : input . message ,
620
- xRayTraceId : this . envVarsService . getXrayTraceId ( ) ,
621
- } ,
622
- this . getPowertoolsLogData ( )
623
- ) ;
624
-
625
- let additionalLogAttributes : LogAttributes = { } ;
626
- additionalLogAttributes = merge (
627
- additionalLogAttributes ,
628
- this . getPersistentLogAttributes ( )
629
- ) ;
630
- if ( typeof input !== 'string' ) {
631
- additionalLogAttributes = merge ( additionalLogAttributes , input ) ;
649
+ let message = '' ;
650
+ let otherInput : { [ key : string ] : unknown } = { } ;
651
+ if ( typeof input === 'string' ) {
652
+ message = input ;
653
+ } else {
654
+ const { message : inputMessage , ...rest } = input ;
655
+ message = inputMessage ;
656
+ otherInput = rest ;
632
657
}
633
- extraInput . forEach ( ( item : Error | LogAttributes | string ) => {
658
+
659
+ // create base attributes
660
+ const unformattedBaseAttributes = {
661
+ logLevel : this . getLogLevelNameFromNumber ( logLevel ) ,
662
+ timestamp : new Date ( ) ,
663
+ message,
664
+ xRayTraceId : this . envVarsService . getXrayTraceId ( ) ,
665
+ ...this . getPowertoolsLogData ( ) ,
666
+ } ;
667
+
668
+ // gradually merge additional attributes starting from customer-provided persistent attributes
669
+ let additionalLogAttributes = { ...this . getPersistentLogAttributes ( ) } ;
670
+ // 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 ) ;
672
+ // then we merge the extra input attributes (if any)
673
+ for ( const item of extraInput ) {
634
674
const attributes : LogAttributes =
635
675
item instanceof Error
636
676
? { error : item }
@@ -639,14 +679,12 @@ class Logger extends Utility implements LoggerInterface {
639
679
: item ;
640
680
641
681
additionalLogAttributes = merge ( additionalLogAttributes , attributes ) ;
642
- } ) ;
682
+ }
643
683
644
- const logItem = this . getLogFormatter ( ) . formatAttributes (
684
+ return this . getLogFormatter ( ) . formatAttributes (
645
685
unformattedBaseAttributes ,
646
686
additionalLogAttributes
647
687
) ;
648
-
649
- return logItem ;
650
688
}
651
689
652
690
/**
@@ -816,10 +854,14 @@ class Logger extends Utility implements LoggerInterface {
816
854
extraInput : LogItemExtraInput
817
855
) : void {
818
856
if ( logLevel >= this . logLevel ) {
819
- this . printLog (
820
- logLevel ,
821
- this . createAndPopulateLogItem ( logLevel , input , extraInput )
822
- ) ;
857
+ if ( this . #isInitialized) {
858
+ this . printLog (
859
+ logLevel ,
860
+ this . createAndPopulateLogItem ( logLevel , input , extraInput )
861
+ ) ;
862
+ } else {
863
+ this . #buffer. push ( [ logLevel , [ logLevel , input , extraInput ] ] ) ;
864
+ }
823
865
}
824
866
}
825
867
@@ -857,17 +899,6 @@ class Logger extends Utility implements LoggerInterface {
857
899
: undefined ;
858
900
}
859
901
860
- /**
861
- * Sets the Logger's custom config service instance, which will be used
862
- * to fetch environment variables.
863
- *
864
- * @private
865
- * @returns {void }
866
- */
867
- private setEnvVarsService ( ) : void {
868
- this . envVarsService = new EnvironmentVariablesService ( ) ;
869
- }
870
-
871
902
/**
872
903
* Sets the initial Logger log level based on the following order:
873
904
* 1. If a log level is set using AWS Lambda Advanced Logging Controls, it sets it.
@@ -965,7 +996,7 @@ class Logger extends Utility implements LoggerInterface {
965
996
}
966
997
967
998
/**
968
- * If the `POWERTOOLS_DEV' env variable is set,
999
+ * If the `POWERTOOLS_DEV` env variable is set,
969
1000
* it adds JSON indentation for pretty printing logs.
970
1001
*
971
1002
* @private
@@ -982,31 +1013,33 @@ class Logger extends Utility implements LoggerInterface {
982
1013
* and the content of all logs.
983
1014
*
984
1015
* @private
985
- * @param {ConstructorOptions } options
986
- * @returns {Logger }
1016
+ * @param options Options to configure the Logger instance
987
1017
*/
988
- private setOptions ( options : ConstructorOptions ) : Logger {
1018
+ private setOptions (
1019
+ options : Omit < ConstructorOptions , 'customConfigService' >
1020
+ ) : this {
989
1021
const {
990
1022
logLevel,
991
1023
serviceName,
992
1024
sampleRateValue,
993
1025
logFormatter,
994
- customConfigService,
995
1026
persistentLogAttributes,
996
1027
environment,
997
1028
} = options ;
998
1029
999
- // order is important, EnvVarsService() is used by other methods
1000
- this . setEnvVarsService ( ) ;
1001
- this . setConsole ( ) ;
1002
- this . setCustomConfigService ( customConfigService ) ;
1003
- this . setInitialLogLevel ( logLevel ) ;
1004
- this . setLogFormatter ( logFormatter ) ;
1030
+ // configurations that affect log content
1005
1031
this . setPowertoolsLogData ( serviceName , environment ) ;
1006
- this . setInitialSampleRate ( sampleRateValue ) ;
1032
+ this . addPersistentLogAttributes ( persistentLogAttributes ) ;
1033
+
1034
+ // configurations that affect Logger behavior
1007
1035
this . setLogEvent ( ) ;
1036
+ this . setInitialLogLevel ( logLevel ) ;
1037
+ this . setInitialSampleRate ( sampleRateValue ) ;
1038
+
1039
+ // configurations that affect how logs are printed
1040
+ this . setLogFormatter ( logFormatter ) ;
1041
+ this . setConsole ( ) ;
1008
1042
this . setLogIndentation ( ) ;
1009
- this . addPersistentLogAttributes ( persistentLogAttributes ) ;
1010
1043
1011
1044
return this ;
1012
1045
}
0 commit comments