Skip to content

Commit 93e050d

Browse files
authored
feat(sdk): implement implicit mode-aware logging for custom loggers (#332)
*Issue #, if available:* *Description of changes:* For custom loggers, mode-aware logging had to be a custom implementation. Now, it is implicit in the language SDK and custom loggers will get this for free. The only thing that custom loggers need to (optionally) implement is getting durable context data. This is implemented by creating a wrapper around custom loggers which only has `warn/info/error/debug/log` methods. Now the custom logger is not returned directly to the user, and only our wrapper is returned. 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 b84d59d commit 93e050d

File tree

15 files changed

+510
-351
lines changed

15 files changed

+510
-351
lines changed

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

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,16 @@ const logger = new Logger({
2626

2727
function getLogMessage(
2828
message: LogItemMessage,
29-
obj: DurableLogData,
29+
obj?: DurableLogData,
3030
): LogItemMessage {
31-
const durableData = {
32-
execution_arn: obj.executionArn,
33-
request_id: obj.requestId,
34-
attempt: obj.attempt,
35-
operation_id: obj.operationId,
36-
};
31+
const durableData = obj
32+
? {
33+
execution_arn: obj.executionArn,
34+
request_id: obj.requestId,
35+
attempt: obj.attempt,
36+
operation_id: obj.operationId,
37+
}
38+
: {};
3739
if (typeof message === "string") {
3840
return {
3941
message,
@@ -57,39 +59,31 @@ class DurablePowertoolsLogger implements DurableLogger {
5759
}
5860

5961
info(input: LogItemMessage, ...extraInput: LogItemExtraInput): void {
60-
if (this.durableLoggingContext?.shouldLog()) {
61-
this.powertoolsLogger.info(
62-
getLogMessage(input, this.durableLoggingContext.getDurableLogData()),
63-
...extraInput,
64-
);
65-
}
62+
this.powertoolsLogger.info(
63+
getLogMessage(input, this.durableLoggingContext?.getDurableLogData()),
64+
...extraInput,
65+
);
6666
}
6767

6868
warn(input: LogItemMessage, ...extraInput: LogItemExtraInput): void {
69-
if (this.durableLoggingContext?.shouldLog()) {
70-
this.powertoolsLogger.warn(
71-
getLogMessage(input, this.durableLoggingContext.getDurableLogData()),
72-
...extraInput,
73-
);
74-
}
69+
this.powertoolsLogger.warn(
70+
getLogMessage(input, this.durableLoggingContext?.getDurableLogData()),
71+
...extraInput,
72+
);
7573
}
7674

7775
error(input: LogItemMessage, ...extraInput: LogItemExtraInput): void {
78-
if (this.durableLoggingContext?.shouldLog()) {
79-
this.powertoolsLogger.error(
80-
getLogMessage(input, this.durableLoggingContext.getDurableLogData()),
81-
...extraInput,
82-
);
83-
}
76+
this.powertoolsLogger.error(
77+
getLogMessage(input, this.durableLoggingContext?.getDurableLogData()),
78+
...extraInput,
79+
);
8480
}
8581

8682
debug(input: LogItemMessage, ...extraInput: LogItemExtraInput): void {
87-
if (this.durableLoggingContext?.shouldLog()) {
88-
this.powertoolsLogger.debug(
89-
getLogMessage(input, this.durableLoggingContext.getDurableLogData()),
90-
...extraInput,
91-
);
92-
}
83+
this.powertoolsLogger.debug(
84+
getLogMessage(input, this.durableLoggingContext?.getDurableLogData()),
85+
...extraInput,
86+
);
9387
}
9488
}
9589

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

Lines changed: 1 addition & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -84,44 +84,7 @@ createTests({
8484
service: "simple-logger",
8585
sampling_rate: 0,
8686
},
87-
// Replay - since the logger does not implement mode-aware logging, all logs are repeated
88-
{
89-
level: "INFO",
90-
message: "=== Simple Logger Demo Starting ===",
91-
timestamp: expect.any(String),
92-
service: "simple-logger",
93-
sampling_rate: 0,
94-
},
95-
{
96-
level: "INFO",
97-
message:
98-
"This logger does not implement configureDurableLoggingContext",
99-
timestamp: expect.any(String),
100-
service: "simple-logger",
101-
sampling_rate: 0,
102-
},
103-
{
104-
level: "DEBUG",
105-
message:
106-
"Debug message: Raw logger without DurableLogger wrapper",
107-
timestamp: expect.any(String),
108-
service: "simple-logger",
109-
sampling_rate: 0,
110-
},
111-
{
112-
level: "INFO",
113-
message: "Info message: Using powertools logger directly",
114-
timestamp: expect.any(String),
115-
service: "simple-logger",
116-
sampling_rate: 0,
117-
},
118-
{
119-
level: "INFO",
120-
message: "Before wait operation",
121-
timestamp: expect.any(String),
122-
service: "simple-logger",
123-
sampling_rate: 0,
124-
},
87+
// Replay - language SDK prevents duplicate logs for custom loggers
12588
{
12689
level: "INFO",
12790
message: "After wait operation - logger still works",
@@ -167,7 +130,6 @@ createTests({
167130
];
168131

169132
// Expected stderr logs (WARN and ERROR levels)
170-
// Note: Logs also appear twice for the same reason as stdout logs
171133
const expectedStderrLogs = [
172134
// First execution
173135
{
@@ -178,13 +140,6 @@ createTests({
178140
sampling_rate: 0,
179141
},
180142
// Replay
181-
{
182-
level: "WARN",
183-
message: "Warning message: No durable logging context available",
184-
timestamp: expect.any(String),
185-
service: "simple-logger",
186-
sampling_rate: 0,
187-
},
188143
{
189144
level: "WARN",
190145
message: "Warning log from step context",
@@ -248,16 +203,6 @@ createTests({
248203
stderrSpy.mockRestore();
249204
}
250205
});
251-
252-
it("should execute correct number of operations", async () => {
253-
const execution = await runner.run();
254-
255-
// Should have:
256-
// 1. wait operation
257-
// 2. step operation
258-
// 3. runInChildContext operation
259-
expect(execution.getOperations()).toHaveLength(3);
260-
});
261206
}
262207
},
263208
});

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ export class FileLogger implements DurableLogger {
1818
message?: unknown,
1919
...optionalParams: unknown[]
2020
): void {
21-
if (this.durableLoggingContext && !this.durableLoggingContext.shouldLog()) {
21+
if (!this.durableLoggingContext) {
2222
return;
2323
}
2424

25-
const logData = this.durableLoggingContext?.getDurableLogData();
25+
const logData = this.durableLoggingContext.getDurableLogData();
2626
const params =
2727
message !== undefined ? [message, ...optionalParams] : optionalParams;
2828

packages/aws-durable-execution-sdk-js/src/context/durable-context/durable-context.integration.test.ts

Lines changed: 0 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -63,103 +63,6 @@ describe("DurableContext Integration Tests", () => {
6363
});
6464

6565
describe("getDurableLoggingContext", () => {
66-
describe("shouldLog function", () => {
67-
it("should return true in ExecutionMode when inside an active context", () => {
68-
const { context } = createTestDurableContext({
69-
durableExecutionMode: DurableExecutionMode.ExecutionMode,
70-
});
71-
72-
const loggingContext = context.getDurableLoggingContext();
73-
74-
runWithContext(
75-
"root",
76-
undefined,
77-
() => {
78-
expect(loggingContext.shouldLog()).toBe(true);
79-
},
80-
undefined,
81-
DurableExecutionMode.ExecutionMode,
82-
);
83-
});
84-
85-
it("should return false in ReplayMode when inside an active context", () => {
86-
const { context } = createTestDurableContext({
87-
durableExecutionMode: DurableExecutionMode.ReplayMode,
88-
});
89-
90-
const loggingContext = context.getDurableLoggingContext();
91-
92-
runWithContext(
93-
"root",
94-
undefined,
95-
() => {
96-
expect(loggingContext.shouldLog()).toBe(false);
97-
},
98-
undefined,
99-
DurableExecutionMode.ReplayMode,
100-
);
101-
});
102-
103-
it("should return false in ReplaySucceededContext when inside an active context", () => {
104-
const { context } = createTestDurableContext({
105-
durableExecutionMode: DurableExecutionMode.ReplaySucceededContext,
106-
});
107-
108-
const loggingContext = context.getDurableLoggingContext();
109-
110-
runWithContext(
111-
"root",
112-
undefined,
113-
() => {
114-
expect(loggingContext.shouldLog()).toBe(false);
115-
},
116-
undefined,
117-
DurableExecutionMode.ReplaySucceededContext,
118-
);
119-
});
120-
121-
it("should return true when no active context exists", () => {
122-
const { context } = createTestDurableContext({
123-
durableExecutionMode: DurableExecutionMode.ReplayMode,
124-
});
125-
126-
const loggingContext = context.getDurableLoggingContext();
127-
128-
// Call without runWithContext - no active context
129-
expect(loggingContext.shouldLog()).toBe(true);
130-
});
131-
132-
it("should use active context mode for child contexts (non-root)", () => {
133-
const { context } = createTestDurableContext({
134-
durableExecutionMode: DurableExecutionMode.ReplayMode,
135-
});
136-
137-
const loggingContext = context.getDurableLoggingContext();
138-
139-
// Test with child context in ExecutionMode - should log
140-
runWithContext(
141-
"1", // Non-root context (child)
142-
undefined,
143-
() => {
144-
expect(loggingContext.shouldLog()).toBe(true);
145-
},
146-
undefined,
147-
DurableExecutionMode.ExecutionMode, // Child context in ExecutionMode
148-
);
149-
150-
// Test with child context in ReplayMode - should NOT log
151-
runWithContext(
152-
"2", // Non-root context (child)
153-
undefined,
154-
() => {
155-
expect(loggingContext.shouldLog()).toBe(false);
156-
},
157-
undefined,
158-
DurableExecutionMode.ReplayMode, // Child context in ReplayMode
159-
);
160-
});
161-
});
162-
16366
describe("getDurableLogData function", () => {
16467
it("should return basic execution data in root context", () => {
16568
const { context, executionContext } = createTestDurableContext({

0 commit comments

Comments
 (0)