Skip to content

Commit 3f6c380

Browse files
committed
feat(sdk): add configureLogger method with modeAware option
Replace setCustomLogger() with configureLogger() that accepts LoggerConfig object with customLogger and modeAware options. The modeAware flag controls whether logs are suppressed during replay mode (default: false). Changes: - Add configureLogger(config: LoggerConfig) method to DurableContext - Add LoggerConfig interface with customLogger and modeAware properties - Update createModeAwareLogger to accept modeAwareEnabled parameter - Change default modeAware behavior to false (show logs during replay) - Update all tests to reflect new default behavior - Add logger-test example with file-based logging (local-only tests) - Update documentation: README, API_SPECIFICATION.md with new API - Add TSDoc comments for Logger types and configureLogger method
1 parent efa419a commit 3f6c380

File tree

18 files changed

+776
-133
lines changed

18 files changed

+776
-133
lines changed

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

Lines changed: 0 additions & 25 deletions
This file was deleted.

packages/aws-durable-execution-sdk-js-examples/src/examples/logger-example/logger-example.ts

Lines changed: 0 additions & 70 deletions
This file was deleted.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { handler } from "./logger-after-callback";
2+
import { createTests } from "../../../utils/test-helper";
3+
import {
4+
InvocationType,
5+
WaitingOperationStatus,
6+
} from "@aws/durable-execution-sdk-js-testing";
7+
import * as fs from "fs";
8+
import * as path from "path";
9+
import * as os from "os";
10+
11+
createTests({
12+
name: "logger-after-callback",
13+
functionName: "logger-after-callback",
14+
handler,
15+
invocationType: InvocationType.Event,
16+
tests: (runner, isCloud) => {
17+
if (!isCloud) {
18+
it("should log after createCallback in execution mode with modeAware=true", async () => {
19+
const logFilePath = path.join(
20+
os.tmpdir(),
21+
`logger-test-${Date.now()}.log`,
22+
);
23+
24+
if (fs.existsSync(logFilePath)) {
25+
fs.unlinkSync(logFilePath);
26+
}
27+
28+
try {
29+
const executionPromise = runner.run({
30+
payload: { logFilePath, modeAware: true },
31+
});
32+
33+
const callbackOp = runner.getOperationByIndex(0);
34+
await callbackOp.waitForData(WaitingOperationStatus.STARTED);
35+
await callbackOp.sendCallbackSuccess("test-result");
36+
37+
const execution = await executionPromise;
38+
39+
const result = execution.getResult() as any;
40+
expect(result.message).toBe("Success");
41+
expect(result.callbackId).toBeDefined();
42+
expect(result.result).toBe("test-result");
43+
44+
const logContent = fs.readFileSync(logFilePath, "utf-8");
45+
const logLines = logContent
46+
.trim()
47+
.split("\n")
48+
.map((line) => JSON.parse(line));
49+
50+
const beforeCallbackLogs = logLines.filter(
51+
(log) => log.message === "Before createCallback",
52+
);
53+
const afterCallbackLogs = logLines.filter(
54+
(log) => log.message === "After createCallback",
55+
);
56+
57+
// With modeAware: true:
58+
// - "Before createCallback" appears once (execution mode, before createCallback)
59+
// - "After createCallback" appears twice (once after createCallback returns, once after callback resolves)
60+
expect(beforeCallbackLogs.length).toBe(1);
61+
expect(afterCallbackLogs.length).toBe(2);
62+
} finally {
63+
if (fs.existsSync(logFilePath)) {
64+
fs.unlinkSync(logFilePath);
65+
}
66+
}
67+
});
68+
}
69+
70+
it("should execute successfully", async () => {
71+
const executionPromise = runner.run();
72+
73+
const callbackOp = runner.getOperationByIndex(0);
74+
await callbackOp.waitForData(WaitingOperationStatus.STARTED);
75+
await callbackOp.sendCallbackSuccess("test-result");
76+
77+
const execution = await executionPromise;
78+
const result = execution.getResult() as any;
79+
expect(result.message).toBe("Success");
80+
});
81+
},
82+
});
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { withDurableExecution, Logger } from "@aws/durable-execution-sdk-js";
2+
import { ExampleConfig } from "../../../types";
3+
import * as fs from "fs";
4+
5+
export const config: ExampleConfig = {
6+
name: "Logger After Callback",
7+
description: "Test logger mode switching after createCallback operation",
8+
};
9+
10+
interface LoggerTestEvent {
11+
logFilePath?: string;
12+
modeAware?: boolean;
13+
}
14+
15+
export const handler = withDurableExecution(
16+
async (event: LoggerTestEvent, context) => {
17+
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+
};
50+
51+
context.configureLogger({
52+
customLogger: fileLogger,
53+
modeAware: event.modeAware ?? true,
54+
});
55+
} else {
56+
context.configureLogger({ modeAware: event.modeAware ?? true });
57+
}
58+
59+
context.logger.info("Before createCallback");
60+
61+
const [callbackPromise, callbackId] =
62+
await context.createCallback<string>();
63+
64+
context.logger.info("After createCallback");
65+
66+
const result = await callbackPromise;
67+
68+
return { message: "Success", callbackId, result };
69+
},
70+
);
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { handler } from "./logger-after-wait";
2+
import { createTests } from "../../../utils/test-helper";
3+
import * as fs from "fs";
4+
import * as path from "path";
5+
import * as os from "os";
6+
7+
createTests({
8+
name: "logger-after-wait",
9+
functionName: "logger-after-wait",
10+
handler,
11+
tests: (runner, isCloud) => {
12+
if (!isCloud) {
13+
it("should log after wait in execution mode with modeAware=true", async () => {
14+
const logFilePath = path.join(
15+
os.tmpdir(),
16+
`logger-test-${Date.now()}.log`,
17+
);
18+
19+
if (fs.existsSync(logFilePath)) {
20+
fs.unlinkSync(logFilePath);
21+
}
22+
23+
try {
24+
const execution = await runner.run({
25+
payload: { logFilePath, modeAware: true },
26+
});
27+
28+
expect(execution.getResult()).toEqual({ message: "Success" });
29+
30+
const logContent = fs.readFileSync(logFilePath, "utf-8");
31+
const logLines = logContent
32+
.trim()
33+
.split("\n")
34+
.map((line) => JSON.parse(line));
35+
36+
const beforeWaitLogs = logLines.filter(
37+
(log) => log.message === "Before wait",
38+
);
39+
const afterWaitLogs = logLines.filter(
40+
(log) => log.message === "After wait",
41+
);
42+
43+
// With modeAware: true, both logs appear once (execution mode only)
44+
expect(beforeWaitLogs.length).toBe(1);
45+
expect(afterWaitLogs.length).toBe(1);
46+
} finally {
47+
if (fs.existsSync(logFilePath)) {
48+
fs.unlinkSync(logFilePath);
49+
}
50+
}
51+
});
52+
}
53+
54+
it("should execute successfully", async () => {
55+
const execution = await runner.run();
56+
expect(execution.getResult()).toEqual({ message: "Success" });
57+
});
58+
},
59+
});
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { withDurableExecution, Logger } from "@aws/durable-execution-sdk-js";
2+
import { ExampleConfig } from "../../../types";
3+
import * as fs from "fs";
4+
5+
export const config: ExampleConfig = {
6+
name: "Logger After Wait",
7+
description: "Test logger mode switching after wait operation",
8+
};
9+
10+
interface LoggerTestEvent {
11+
logFilePath?: string;
12+
modeAware?: boolean;
13+
}
14+
15+
export const handler = withDurableExecution(
16+
async (event: LoggerTestEvent, context) => {
17+
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+
};
50+
51+
context.configureLogger({
52+
customLogger: fileLogger,
53+
modeAware: event.modeAware ?? true,
54+
});
55+
} else {
56+
context.configureLogger({ modeAware: event.modeAware ?? true });
57+
}
58+
59+
context.logger.info("Before wait");
60+
await context.wait({ seconds: 2 });
61+
context.logger.info("After wait");
62+
63+
return { message: "Success" };
64+
},
65+
);

0 commit comments

Comments
 (0)