Skip to content

Commit db26958

Browse files
improv(logger): streamline Logger types (#3054)
Co-authored-by: Leandro Damascena <[email protected]>
1 parent c0d2158 commit db26958

13 files changed

+630
-526
lines changed

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

+104-166
Large diffs are not rendered by default.

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

-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import type { ConfigServiceInterface } from '../types/ConfigServiceInterface.js'
99
* These variables can be a mix of runtime environment variables set by AWS and
1010
* variables that can be set by the developer additionally.
1111
*
12-
* @class
13-
* @extends {CommonEnvironmentVariablesService}
1412
* @see https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime
1513
* @see https://docs.powertools.aws.dev/lambda/typescript/latest/#environment-variables
1614
*/

Diff for: packages/logger/src/formatter/LogFormatter.ts

+100-32
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
import type { EnvironmentVariablesService } from '../config/EnvironmentVariablesService.js';
2-
import type {
3-
LogAttributes,
4-
LogFormatterInterface,
5-
LogFormatterOptions,
6-
} from '../types/Log.js';
7-
import type { UnformattedAttributes } from '../types/Logger.js';
2+
import type { LogAttributes } from '../types/Logger.js';
3+
import type { LogFormatterOptions } from '../types/formatters.js';
4+
import type { UnformattedAttributes } from '../types/logKeys.js';
85
import type { LogItem } from './LogItem.js';
96

107
/**
11-
* This class defines and implements common methods for the formatting of log attributes.
8+
* Class that defines and implements common methods for the formatting of log attributes.
129
*
13-
* @class
10+
* When creating a custom log formatter, you should extend this class and implement the
11+
* {@link formatAttributes | formatAttributes()} method to define the structure of the log item.
12+
*
13+
* @abstract
1414
*/
15-
abstract class LogFormatter implements LogFormatterInterface {
15+
abstract class LogFormatter {
1616
/**
17-
* EnvironmentVariablesService instance.
18-
* If set, it allows to access environment variables.
17+
* Instance of the {@link EnvironmentVariablesService} to use for configuration.
1918
*/
2019
protected envVarsService?: EnvironmentVariablesService;
2120

@@ -24,21 +23,85 @@ abstract class LogFormatter implements LogFormatterInterface {
2423
}
2524

2625
/**
27-
* It formats key-value pairs of log attributes.
26+
* Format key-value pairs of log attributes.
27+
*
28+
* You should implement this method in a subclass to define the structure of the log item.
29+
*
30+
* @example
31+
* ```typescript
32+
* import { LogFormatter, LogItem } from '@aws-lambda-powertools/logger';
33+
* import type {
34+
* LogAttributes,
35+
* UnformattedAttributes,
36+
* } from '@aws-lambda-powertools/logger/types';
37+
*
38+
* class MyCompanyLogFormatter extends LogFormatter {
39+
* public formatAttributes(
40+
* attributes: UnformattedAttributes,
41+
* additionalLogAttributes: LogAttributes
42+
* ): LogItem {
43+
* const baseAttributes: MyCompanyLog = {
44+
* message: attributes.message,
45+
* service: attributes.serviceName,
46+
* environment: attributes.environment,
47+
* awsRegion: attributes.awsRegion,
48+
* correlationIds: {
49+
* awsRequestId: attributes.lambdaContext?.awsRequestId,
50+
* xRayTraceId: attributes.xRayTraceId,
51+
* },
52+
* lambdaFunction: {
53+
* name: attributes.lambdaContext?.functionName,
54+
* arn: attributes.lambdaContext?.invokedFunctionArn,
55+
* memoryLimitInMB: attributes.lambdaContext?.memoryLimitInMB,
56+
* version: attributes.lambdaContext?.functionVersion,
57+
* coldStart: attributes.lambdaContext?.coldStart,
58+
* },
59+
* logLevel: attributes.logLevel,
60+
* timestamp: this.formatTimestamp(attributes.timestamp), // You can extend this function
61+
* logger: {
62+
* sampleRateValue: attributes.sampleRateValue,
63+
* },
64+
* };
65+
*
66+
* const logItem = new LogItem({ attributes: baseAttributes });
67+
* // add any attributes not explicitly defined
68+
* logItem.addAttributes(additionalLogAttributes);
69+
*
70+
* return logItem;
71+
* }
72+
* }
2873
*
29-
* @param {UnformattedAttributes} attributes - unformatted attributes
30-
* @param {LogAttributes} additionalLogAttributes - additional log attributes
74+
* export { MyCompanyLogFormatter };
75+
* ```
76+
*
77+
* @param attributes - Unformatted attributes
78+
* @param additionalLogAttributes - Additional log attributes
3179
*/
3280
public abstract formatAttributes(
3381
attributes: UnformattedAttributes,
3482
additionalLogAttributes: LogAttributes
3583
): LogItem;
3684

3785
/**
38-
* Format a given Error parameter.
86+
* Format an error into a loggable object.
87+
*
88+
* @example
89+
* ```json
90+
* {
91+
* "name": "Error",
92+
* "location": "file.js:1",
93+
* "message": "An error occurred",
94+
* "stack": "Error: An error occurred\n at file.js:1\n at file.js:2\n at file.js:3",
95+
* "cause": {
96+
* "name": "OtherError",
97+
* "location": "file.js:2",
98+
* "message": "Another error occurred",
99+
* "stack": "Error: Another error occurred\n at file.js:2\n at file.js:3\n at file.js:4"
100+
* }
101+
* }
102+
* ```
39103
*
40-
* @param {Error} error - error to format
41-
* @returns {LogAttributes} formatted error
104+
* @param error - Error to format
42105
*/
43106
public formatError(error: Error): LogAttributes {
44107
return {
@@ -54,11 +117,14 @@ abstract class LogFormatter implements LogFormatterInterface {
54117
}
55118

56119
/**
57-
* Format a given date into an ISO 8601 string, considering the configured timezone.
58-
* If `envVarsService` is set and the configured timezone differs from 'UTC',
59-
* the date is formatted to that timezone. Otherwise, it defaults to 'UTC'.
120+
* Format a date into an ISO 8601 string with the configured timezone.
121+
*
122+
* If the log formatter is passed an {@link EnvironmentVariablesService} instance
123+
* during construction, the timezone is read from the `TZ` environment variable, if present.
60124
*
61-
* @param {Date} now - The date to format
125+
* Otherwise, the timezone defaults to ':UTC'.
126+
*
127+
* @param now - The date to format
62128
*/
63129
public formatTimestamp(now: Date): string {
64130
const defaultTimezone = 'UTC';
@@ -75,9 +141,9 @@ abstract class LogFormatter implements LogFormatterInterface {
75141
}
76142

77143
/**
78-
* Get a string containing the location of an error, given a particular stack trace.
144+
* Get the location of an error from a stack trace.
79145
*
80-
* @param {string} stack - stack trace
146+
* @param stack - stack trace to parse
81147
*/
82148
public getCodeLocation(stack?: string): string {
83149
if (!stack) {
@@ -100,14 +166,16 @@ abstract class LogFormatter implements LogFormatterInterface {
100166

101167
/**
102168
* Create a new Intl.DateTimeFormat object configured with the specified time zone
103-
* and formatting options. The time is displayed in 24-hour format (hour12: false).
169+
* and formatting options.
170+
*
171+
* The time is displayed in 24-hour format (hour12: false).
104172
*
105-
* @param {string} timeZone - IANA time zone identifier (e.g., "Asia/Dhaka").
173+
* @param timezone - IANA time zone identifier (e.g., "Asia/Dhaka").
106174
*/
107-
#getDateFormatter = (timeZone: string): Intl.DateTimeFormat => {
175+
#getDateFormatter = (timezone: string): Intl.DateTimeFormat => {
108176
const twoDigitFormatOption = '2-digit';
109-
const validTimeZone = Intl.supportedValuesOf('timeZone').includes(timeZone)
110-
? timeZone
177+
const validTimeZone = Intl.supportedValuesOf('timeZone').includes(timezone)
178+
? timezone
111179
: 'UTC';
112180

113181
return new Intl.DateTimeFormat('en', {
@@ -125,12 +193,12 @@ abstract class LogFormatter implements LogFormatterInterface {
125193
/**
126194
* Generate an ISO 8601 timestamp string with the specified time zone and the local time zone offset.
127195
*
128-
* @param {Date} date - date to format
129-
* @param {string} timeZone - IANA time zone identifier (e.g., "Asia/Dhaka").
196+
* @param date - date to format
197+
* @param timezone - IANA time zone identifier (e.g., "Asia/Dhaka").
130198
*/
131-
#generateISOTimestampWithOffset(date: Date, timeZone: string): string {
199+
#generateISOTimestampWithOffset(date: Date, timezone: string): string {
132200
const { year, month, day, hour, minute, second } = this.#getDateFormatter(
133-
timeZone
201+
timezone
134202
)
135203
.formatToParts(date)
136204
.reduce(

Diff for: packages/logger/src/formatter/LogItem.ts

+21-16
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
11
import merge from 'lodash.merge';
2-
import type { LogAttributes, LogItemInterface } from '../types/Log.js';
2+
import type { LogAttributes } from '../types/Logger.js';
33

44
/**
55
* LogItem is a class that holds the attributes of a log item.
6-
* It is used to store the attributes of a log item and to add additional attributes to it.
7-
* It is used by the LogFormatter to store the attributes of a log item.
86
*
9-
* @class
7+
* It is used by {@link LogFormatter} to store the attributes of a log item and to add additional attributes to it.
108
*/
11-
class LogItem implements LogItemInterface {
9+
class LogItem {
1210
/**
1311
* The attributes of the log item.
1412
*/
1513
private attributes: LogAttributes = {};
1614

1715
/**
1816
* Constructor for LogItem.
19-
* @param {Object} params - The parameters for the LogItem.
20-
* @param {LogAttributes} params.attributes - The initial attributes for the LogItem.
17+
*
18+
* Attributes are added in the following order:
19+
* - Standard keys provided by the logger (e.g. `message`, `level`, `timestamp`)
20+
* - Persistent attributes provided by developer, not formatted (done later)
21+
* - Ephemeral attributes provided as parameters for a single log item (done later)
22+
*
23+
* @param params - The parameters for the LogItem.
2124
*/
2225
public constructor(params: { attributes: LogAttributes }) {
23-
// Add attributes in the log item in this order:
24-
// - Base attributes supported by the Powertool by default
25-
// - Persistent attributes provided by developer, not formatted (done later)
26-
// - Ephemeral attributes provided as parameters for a single log item (done later)
2726
this.addAttributes(params.attributes);
2827
}
2928

3029
/**
3130
* Add attributes to the log item.
32-
* @param {LogAttributes} attributes - The attributes to add to the log item.
31+
*
32+
* @param attributes - The attributes to add to the log item.
3333
*/
3434
public addAttributes(attributes: LogAttributes): this {
3535
merge(this.attributes, attributes);
@@ -46,14 +46,18 @@ class LogItem implements LogItemInterface {
4646

4747
/**
4848
* Prepare the log item for printing.
49+
*
50+
* This operation removes empty keys from the log item, see {@link removeEmptyKeys | removeEmptyKeys()} for more information.
4951
*/
5052
public prepareForPrint(): void {
5153
this.setAttributes(this.removeEmptyKeys(this.getAttributes()));
5254
}
5355

5456
/**
55-
* Remove empty keys from the log item.
56-
* @param {LogAttributes} attributes - The attributes to remove empty keys from.
57+
* Remove empty keys from the log item, where empty keys are defined as keys with
58+
* values of `undefined`, empty strings (`''`), or `null`.
59+
*
60+
* @param attributes - The attributes to remove empty keys from.
5761
*/
5862
public removeEmptyKeys(attributes: LogAttributes): LogAttributes {
5963
const newAttributes: LogAttributes = {};
@@ -71,8 +75,9 @@ class LogItem implements LogItemInterface {
7175
}
7276

7377
/**
74-
* Set the attributes of the log item.
75-
* @param {LogAttributes} attributes - The attributes to set for the log item.
78+
* Replace the attributes of the log item.
79+
*
80+
* @param attributes - The attributes to set for the log item.
7681
*/
7782
public setAttributes(attributes: LogAttributes): void {
7883
this.attributes = attributes;

Diff for: packages/logger/src/formatter/PowertoolsLogFormatter.ts

+17-11
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
import type {
2+
LogRecordOrderKeys,
3+
PowertoolsLogFormatterOptions,
4+
} from '../types/formatters.js';
15
import type {
26
LogAttributes,
3-
PowerToolsLogFormatterOptions,
4-
PowertoolsLog,
5-
} from '../types/Log.js';
6-
import type { LogRecordOrder, UnformattedAttributes } from '../types/Logger.js';
7+
PowertoolsLambdaContextKeys,
8+
PowertoolsStandardKeys,
9+
UnformattedAttributes,
10+
} from '../types/logKeys.js';
711
import { LogFormatter } from './LogFormatter.js';
812
import { LogItem } from './LogItem.js';
913

@@ -16,13 +20,14 @@ import { LogItem } from './LogItem.js';
1620
*/
1721
class PowertoolsLogFormatter extends LogFormatter {
1822
/**
19-
* An array of keys that defines the order of the log record.
23+
* List of keys to order log attributes by.
24+
*
25+
* This can be a set of keys or an array of keys.
2026
*/
21-
#logRecordOrder?: LogRecordOrder;
27+
#logRecordOrder?: LogRecordOrderKeys;
2228

23-
public constructor(options?: PowerToolsLogFormatterOptions) {
29+
public constructor(options?: PowertoolsLogFormatterOptions) {
2430
super(options);
25-
2631
this.#logRecordOrder = options?.logRecordOrder;
2732
}
2833

@@ -36,7 +41,9 @@ class PowertoolsLogFormatter extends LogFormatter {
3641
attributes: UnformattedAttributes,
3742
additionalLogAttributes: LogAttributes
3843
): LogItem {
39-
const baseAttributes: PowertoolsLog = {
44+
const baseAttributes: Partial<PowertoolsStandardKeys> &
45+
Partial<PowertoolsLambdaContextKeys> &
46+
LogAttributes = {
4047
cold_start: attributes.lambdaContext?.coldStart,
4148
function_arn: attributes.lambdaContext?.invokedFunctionArn,
4249
function_memory_size: attributes.lambdaContext?.memoryLimitInMB,
@@ -57,8 +64,7 @@ class PowertoolsLogFormatter extends LogFormatter {
5764
);
5865
}
5966

60-
const orderedAttributes = {} as PowertoolsLog;
61-
67+
const orderedAttributes: LogAttributes = {};
6268
// If logRecordOrder is set, order the attributes in the log item
6369
for (const key of this.#logRecordOrder) {
6470
if (key in baseAttributes && !(key in orderedAttributes)) {

0 commit comments

Comments
 (0)