Skip to content

Commit dbb1ad7

Browse files
authored
fix(sdk): implement custom logger for all durable log messages (#309)
*Issue #, if available:* #305 *Description of changes:* Refactoring logger in the language SDK to fix default log behavior to emit in a proper structure following expected nodejs and Lambda practices. Also making it clearer for consumers on how to implement custom loggers. The key logic that was updated was default-logger.ts: https://github.com/aws/aws-durable-execution-sdk-js/blob/958437827fc38b5bfa711385743f53a415f2d57c/packages/aws-durable-execution-sdk-js/src/utils/logger/default-logger. I added some comprehensive logging tests to verify all the outputs, and tested in eu-south-1 as well. Before: - The default log data emitted did not match the expected log structure of Lambda - It did not emit errors in the same format as Lambda with snake_case instead of camelCase - All fields were nested in the `message` field instead of being on the structured output itself. - See: [the docs](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-logging.html#nodejs-logging-examples) and [the RIC implementation](https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/blob/962ed28eefbc052389c4de4366b1c0c49ee08a13/src/LogPatch.js) for the default Lambda behaviour. - It was not possible for customers to log more than 1-2 things in the context/step loggers, and they were restricted in what they could log - The Logger interface was confusing to implement for consumers, since all durable context data was passed in the first argument, and arbitrary `data` and `error` fields were passed, but no additional data was possible. This doesn't align with `console.log` or other popular loggers - Attempt showed `0` for the first attempt, instead of `1`, and `1` instead of `2` for the second attempt and so on. It was always 1 behind the actual running attempt. After: - The default logger now implements custom logic which is based on the same logic that the RIC does. It emits structured log data directly to stdout/stderr, and all data matches the expected behaviour based on the existing non-durable function behaviour. It behaves much more similarly to the base nodejs `console` functions to make it easier for developers to understand. - This logic includes respecting the `AWS_LAMBDA_LOG_LEVEL` for log filtering - Handling of circular objects - `util.format` for multiple log parameters - Structured error serialization with `errorType`, `errorMessage`, and `stackTrace` fields - Adding `requestId` and `tenantId` to the log entry - Adding `operationId` and `executionArn` and `attempt` to the log entry - All context loggers now implement the `DurableLogger` interface (renamed from `Logger` to disambiguate with other libraries). This DurableLogger may also implement `configureDurableLoggingContext` which can be used for determining when to log with `shouldLog`, and the durable metadata available with `getDurableLogData`. - If it's not implemented, - Fixed `attempt` passing in stepContext and waitForConditionContext to pass the current attempt + 1, since the logger will be running in the next attempt, not the previous attempt from step data ## configureLogger Customers can pass any logger into configureLogger as long as it satisfies the DurableLogger interface. Optionally, if they also implement `configureDurableLoggingContext`, they can implement mode-aware logging with `shouldLog` and `getDurableLogData`. Additionally, they can specify the logger as a type in `DurableContext` or `withDurableExecution` which will provide the type hints for their custom logger. The type parameters for their custom logger will be preserved if they use configureLogger. ## Powertools compatibility: configureLogger is compatible with Powertools by default, but it will not have mode-aware logging or include any durable metadata. Customers will have to create a simple wrapper around Powertools to have this work, or Powertools will need to add some feature which supports the durable logger. See a basic example here: https://github.com/aws/aws-durable-execution-sdk-js/pull/309/files#diff-4dbcb748719fbfb0d3443e575cf18dd9437b4e2806f59a552bb8205bc4bed893 By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
1 parent e109c9c commit dbb1ad7

File tree

75 files changed

+4799
-1209
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+4799
-1209
lines changed

package-lock.json

Lines changed: 59 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/aws-durable-execution-sdk-js-examples/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"prettier": "prettier --write './src/**/*.*' && prettier --write './test/**/*.*'"
4848
},
4949
"dependencies": {
50+
"@aws-lambda-powertools/logger": "^2.29.0",
5051
"@aws-sdk/client-dynamodb": "^3.777.0",
5152
"@aws-sdk/client-lambda": "^3.777.0",
5253
"@aws/durable-execution-sdk-js": "*",

packages/aws-durable-execution-sdk-js-examples/src/examples/logger-test/after-callback/logger-after-callback.ts

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { withDurableExecution, Logger } from "@aws/durable-execution-sdk-js";
1+
import { withDurableExecution } from "@aws/durable-execution-sdk-js";
22
import { ExampleConfig } from "../../../types";
3-
import * as fs from "fs";
3+
import { FileLogger } from "../../../utils/file-logger";
44

55
export const config: ExampleConfig = {
66
name: "Logger After Callback",
@@ -15,38 +15,7 @@ interface LoggerTestEvent {
1515
export const handler = withDurableExecution(
1616
async (event: LoggerTestEvent, context) => {
1717
if (event.logFilePath) {
18-
const fileLogger: Logger = {
19-
log: (level, message, data) => {
20-
fs.appendFileSync(
21-
event.logFilePath!,
22-
JSON.stringify({ level, message, data }) + "\n",
23-
);
24-
},
25-
info: (message, data) => {
26-
fs.appendFileSync(
27-
event.logFilePath!,
28-
JSON.stringify({ level: "info", message, data }) + "\n",
29-
);
30-
},
31-
error: (message, error, data) => {
32-
fs.appendFileSync(
33-
event.logFilePath!,
34-
JSON.stringify({ level: "error", message, error, data }) + "\n",
35-
);
36-
},
37-
warn: (message, data) => {
38-
fs.appendFileSync(
39-
event.logFilePath!,
40-
JSON.stringify({ level: "warn", message, data }) + "\n",
41-
);
42-
},
43-
debug: (message, data) => {
44-
fs.appendFileSync(
45-
event.logFilePath!,
46-
JSON.stringify({ level: "debug", message, data }) + "\n",
47-
);
48-
},
49-
};
18+
const fileLogger = new FileLogger(event.logFilePath);
5019

5120
context.configureLogger({
5221
customLogger: fileLogger,

packages/aws-durable-execution-sdk-js-examples/src/examples/logger-test/after-wait/logger-after-wait.ts

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { withDurableExecution, Logger } from "@aws/durable-execution-sdk-js";
1+
import { withDurableExecution } from "@aws/durable-execution-sdk-js";
22
import { ExampleConfig } from "../../../types";
3-
import * as fs from "fs";
3+
import { FileLogger } from "../../../utils/file-logger";
44

55
export const config: ExampleConfig = {
66
name: "Logger After Wait",
@@ -15,38 +15,7 @@ interface LoggerTestEvent {
1515
export const handler = withDurableExecution(
1616
async (event: LoggerTestEvent, context) => {
1717
if (event.logFilePath) {
18-
const fileLogger: Logger = {
19-
log: (level, message, data) => {
20-
fs.appendFileSync(
21-
event.logFilePath!,
22-
JSON.stringify({ level, message, data }) + "\n",
23-
);
24-
},
25-
info: (message, data) => {
26-
fs.appendFileSync(
27-
event.logFilePath!,
28-
JSON.stringify({ level: "info", message, data }) + "\n",
29-
);
30-
},
31-
error: (message, error, data) => {
32-
fs.appendFileSync(
33-
event.logFilePath!,
34-
JSON.stringify({ level: "error", message, error, data }) + "\n",
35-
);
36-
},
37-
warn: (message, data) => {
38-
fs.appendFileSync(
39-
event.logFilePath!,
40-
JSON.stringify({ level: "warn", message, data }) + "\n",
41-
);
42-
},
43-
debug: (message, data) => {
44-
fs.appendFileSync(
45-
event.logFilePath!,
46-
JSON.stringify({ level: "debug", message, data }) + "\n",
47-
);
48-
},
49-
};
18+
const fileLogger = new FileLogger(event.logFilePath);
5019

5120
context.configureLogger({
5221
customLogger: fileLogger,

0 commit comments

Comments
 (0)