Skip to content

Commit e0304dd

Browse files
committed
fix: align log metadata with other SDKs
1 parent da837f8 commit e0304dd

18 files changed

Lines changed: 389 additions & 211 deletions

File tree

examples/src/main/java/software/amazon/lambda/durable/examples/general/LoggingExample.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// SPDX-License-Identifier: Apache-2.0
33
package software.amazon.lambda.durable.examples.general;
44

5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
57
import software.amazon.lambda.durable.DurableContext;
68
import software.amazon.lambda.durable.DurableHandler;
79
import software.amazon.lambda.durable.examples.types.GreetingRequest;
@@ -13,15 +15,16 @@
1315
* in log entries via MDC. By default, logs are suppressed during replay to avoid duplicates.
1416
*/
1517
public class LoggingExample extends DurableHandler<GreetingRequest, String> {
18+
Logger logger = LoggerFactory.getLogger(LoggingExample.class);
1619

1720
@Override
1821
public String handleRequest(GreetingRequest input, DurableContext context) {
1922
// Log at execution level (outside any step)
20-
context.getLogger().info("Processing greeting for: {}", input.getName());
23+
context.getLogger(logger).info("Processing greeting for: {}", input.getName());
2124

2225
// Step 1: Create greeting - logs inside step include operation context
2326
var greeting = context.step("create-greeting", String.class, ctx -> {
24-
ctx.getLogger().info("Creating greeting message");
27+
ctx.getLogger(logger).info("Creating greeting message");
2528
return "Hello, " + input.getName();
2629
});
2730

examples/src/test/java/software/amazon/lambda/durable/examples/CloudBasedIntegrationTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,14 @@ void testWaitAtLeastInProcessExample() {
253253
assertTrue(asyncOp.getStepResult(String.class).contains("Processed: TestUser"));
254254
}
255255

256+
@Test
257+
void testLoggingExample() {
258+
var runner = CloudDurableTestRunner.create(
259+
arn("logging-example"), GreetingRequest.class, String.class, lambdaClient);
260+
var result = runner.run(new GreetingRequest("TestUser"));
261+
assertEquals(ExecutionStatus.SUCCEEDED, result.getStatus());
262+
}
263+
256264
@Test
257265
void testGenericTypesExample() {
258266
var runner = CloudDurableTestRunner.create(

sdk/src/main/java/software/amazon/lambda/durable/DurableContext.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
import software.amazon.lambda.durable.model.WaitForConditionResult;
2323

2424
public interface DurableContext extends BaseContext {
25+
static DurableContext getContext() {
26+
return (DurableContext) BaseContext.getCurrentContext();
27+
}
28+
2529
/**
2630
* Executes a durable step with the given name and blocks until it completes.
2731
*

sdk/src/main/java/software/amazon/lambda/durable/ParallelDurableFuture.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
import java.util.function.Function;
66
import software.amazon.lambda.durable.config.ParallelBranchConfig;
77
import software.amazon.lambda.durable.model.ParallelResult;
8+
import software.amazon.lambda.durable.model.SafeCloseable;
89

910
/** User-facing context for managing parallel branch execution within a durable function. */
10-
public interface ParallelDurableFuture extends AutoCloseable, DurableFuture<ParallelResult> {
11+
public interface ParallelDurableFuture extends SafeCloseable, DurableFuture<ParallelResult> {
1112

1213
/**
1314
* Registers and immediately starts a branch (respects maxConcurrency).
@@ -68,7 +69,4 @@ default <T> DurableFuture<T> branch(
6869
*/
6970
<T> DurableFuture<T> branch(
7071
String name, TypeToken<T> resultType, Function<DurableContext, T> func, ParallelBranchConfig config);
71-
72-
/** Calls {@link #get()} if not already called. Guarantees that the context is closed. */
73-
void close();
7472
}

sdk/src/main/java/software/amazon/lambda/durable/context/BaseContext.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,36 @@
33
package software.amazon.lambda.durable.context;
44

55
import com.amazonaws.services.lambda.runtime.Context;
6+
import org.slf4j.Logger;
67
import software.amazon.lambda.durable.DurableConfig;
78
import software.amazon.lambda.durable.logging.DurableLogger;
89

9-
public interface BaseContext extends AutoCloseable {
10+
public interface BaseContext {
11+
ThreadLocal<BaseContext> CONTEXT = new ThreadLocal<>();
12+
13+
/**
14+
* Gets the current context (DurableContext or StepContext) for this thread.
15+
*
16+
* @return the current context or null if not set
17+
*/
18+
static BaseContext getCurrentContext() {
19+
return CONTEXT.get();
20+
}
1021
/**
1122
* Gets a logger with additional information of the current execution context.
1223
*
1324
* @return a DurableLogger instance
1425
*/
1526
DurableLogger getLogger();
1627

28+
/**
29+
* Gets a logger with additional information of the current execution context.
30+
*
31+
* @param delegate the logger to wrap
32+
* @return a DurableLogger instance
33+
*/
34+
DurableLogger getLogger(Logger delegate);
35+
1736
/**
1837
* Returns the AWS Lambda runtime context.
1938
*
@@ -46,7 +65,4 @@ public interface BaseContext extends AutoCloseable {
4665

4766
/** Returns whether this context is currently in replay mode. */
4867
boolean isReplaying();
49-
50-
/** Closes this context. */
51-
void close();
5268
}

sdk/src/main/java/software/amazon/lambda/durable/context/BaseContextImpl.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
package software.amazon.lambda.durable.context;
44

55
import com.amazonaws.services.lambda.runtime.Context;
6+
import org.slf4j.Logger;
67
import software.amazon.lambda.durable.DurableConfig;
78
import software.amazon.lambda.durable.execution.ExecutionManager;
89
import software.amazon.lambda.durable.execution.ThreadType;
10+
import software.amazon.lambda.durable.logging.DurableLogger;
911

10-
public abstract class BaseContextImpl implements AutoCloseable, BaseContext {
12+
public abstract class BaseContextImpl implements BaseContext {
1113
private final ExecutionManager executionManager;
1214
private final DurableConfig durableConfig;
1315
private final Context lambdaContext;
@@ -109,4 +111,18 @@ public boolean isReplaying() {
109111
public void setExecutionMode() {
110112
this.isReplaying = false;
111113
}
114+
115+
/** Returns a durable logger for this context. */
116+
public DurableLogger getLogger() {
117+
return DurableLogger.INSTANCE;
118+
}
119+
120+
/** Returns a durable logger for this context. */
121+
public DurableLogger getLogger(Logger delegate) {
122+
return new DurableLogger(delegate);
123+
}
124+
125+
public static void setCurrentContext(BaseContext context) {
126+
CONTEXT.set(context);
127+
}
112128
}

sdk/src/main/java/software/amazon/lambda/durable/context/DurableContextImpl.java

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import java.util.function.BiConsumer;
1111
import java.util.function.BiFunction;
1212
import java.util.function.Function;
13-
import org.slf4j.LoggerFactory;
1413
import software.amazon.lambda.durable.DurableCallbackFuture;
1514
import software.amazon.lambda.durable.DurableConfig;
1615
import software.amazon.lambda.durable.DurableContext;
@@ -32,7 +31,6 @@
3231
import software.amazon.lambda.durable.execution.OperationIdGenerator;
3332
import software.amazon.lambda.durable.execution.SuspendExecutionException;
3433
import software.amazon.lambda.durable.execution.ThreadType;
35-
import software.amazon.lambda.durable.logging.DurableLogger;
3634
import software.amazon.lambda.durable.model.MapResult;
3735
import software.amazon.lambda.durable.model.OperationIdentifier;
3836
import software.amazon.lambda.durable.model.OperationSubType;
@@ -62,7 +60,6 @@ public class DurableContextImpl extends BaseContextImpl implements DurableContex
6260
private final OperationIdGenerator operationIdGenerator;
6361
private final DurableContextImpl parentContext;
6462
private final boolean isVirtual;
65-
private volatile DurableLogger logger;
6663

6764
/** Shared initialization — sets all fields. */
6865
private DurableContextImpl(
@@ -430,30 +427,6 @@ private static <T> T executeRetryLoop(
430427
}
431428

432429
// =============== accessors ================
433-
@Override
434-
public DurableLogger getLogger() {
435-
// lazy initialize logger
436-
if (logger == null) {
437-
synchronized (this) {
438-
if (logger == null) {
439-
logger = new DurableLogger(LoggerFactory.getLogger(DurableContext.class), this);
440-
}
441-
}
442-
}
443-
return logger;
444-
}
445-
446-
/**
447-
* Clears the logger's thread properties. Called during context destruction to prevent memory leaks and ensure clean
448-
* state for subsequent executions.
449-
*/
450-
@Override
451-
public void close() {
452-
if (logger != null) {
453-
logger.close();
454-
}
455-
}
456-
457430
/**
458431
* Get the next operationId. Returns a globally unique operation ID by hashing a sequential operation counter. For
459432
* root contexts, the counter value is hashed directly (e.g. "1", "2", "3"). For child contexts, the values are

sdk/src/main/java/software/amazon/lambda/durable/context/StepContextImpl.java

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@
33
package software.amazon.lambda.durable.context;
44

55
import com.amazonaws.services.lambda.runtime.Context;
6-
import org.slf4j.LoggerFactory;
76
import software.amazon.lambda.durable.DurableConfig;
87
import software.amazon.lambda.durable.StepContext;
98
import software.amazon.lambda.durable.execution.ExecutionManager;
109
import software.amazon.lambda.durable.execution.ThreadType;
11-
import software.amazon.lambda.durable.logging.DurableLogger;
1210

1311
/**
1412
* Context available inside a step operation's user function.
@@ -17,7 +15,6 @@
1715
* {@link BaseContext} for thread lifecycle management.
1816
*/
1917
public class StepContextImpl extends BaseContextImpl implements StepContext {
20-
private volatile DurableLogger logger;
2118
private final int attempt;
2219

2320
/**
@@ -46,25 +43,4 @@ protected StepContextImpl(
4643
public int getAttempt() {
4744
return attempt;
4845
}
49-
50-
@Override
51-
public DurableLogger getLogger() {
52-
// lazy initialize logger
53-
if (logger == null) {
54-
synchronized (this) {
55-
if (logger == null) {
56-
logger = new DurableLogger(LoggerFactory.getLogger(StepContext.class), this);
57-
}
58-
}
59-
}
60-
return logger;
61-
}
62-
63-
/** Closes the logger for this context. */
64-
@Override
65-
public void close() {
66-
if (logger != null) {
67-
logger.close();
68-
}
69-
}
7046
}

sdk/src/main/java/software/amazon/lambda/durable/execution/DurableExecutor.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import software.amazon.lambda.durable.exception.DurableOperationException;
2323
import software.amazon.lambda.durable.exception.IllegalDurableOperationException;
2424
import software.amazon.lambda.durable.exception.UnrecoverableDurableExecutionException;
25+
import software.amazon.lambda.durable.logging.DurableLogger;
2526
import software.amazon.lambda.durable.model.DurableExecutionInput;
2627
import software.amazon.lambda.durable.model.DurableExecutionOutput;
2728
import software.amazon.lambda.durable.plugin.InvocationEndInfo;
@@ -69,9 +70,10 @@ public static <I, O> DurableExecutionOutput execute(
6970

7071
var userInput = extractUserInput(
7172
executionManager.getExecutionOperation(), config.getSerDes(), inputType);
72-
// use try-with-resources to clear logger properties
73-
try (var context =
74-
DurableContextImpl.createRootContext(executionManager, config, lambdaContext)) {
73+
var context = DurableContextImpl.createRootContext(executionManager, config, lambdaContext);
74+
DurableContextImpl.setCurrentContext(context);
75+
// use a try-with-resources to clear logger properties
76+
try (var ignored = DurableLogger.attachContext()) {
7577
return handler.apply(userInput, context);
7678
}
7779
},

sdk/src/main/java/software/amazon/lambda/durable/execution/ExecutionManager.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import software.amazon.lambda.durable.DurableConfig;
2525
import software.amazon.lambda.durable.exception.UnrecoverableDurableExecutionException;
2626
import software.amazon.lambda.durable.model.DurableExecutionInput;
27+
import software.amazon.lambda.durable.model.SafeCloseable;
2728
import software.amazon.lambda.durable.operation.BaseDurableOperation;
2829

2930
/**
@@ -47,7 +48,7 @@
4748
*
4849
* @see InternalExecutor
4950
*/
50-
public class ExecutionManager implements AutoCloseable {
51+
public class ExecutionManager implements SafeCloseable {
5152

5253
private static final Logger logger = LoggerFactory.getLogger(ExecutionManager.class);
5354

0 commit comments

Comments
 (0)