Skip to content

Commit d924163

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

15 files changed

Lines changed: 337 additions & 209 deletions

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: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import software.amazon.lambda.durable.DurableConfig;
77
import software.amazon.lambda.durable.logging.DurableLogger;
88

9-
public interface BaseContext extends AutoCloseable {
9+
public interface BaseContext {
1010
/**
1111
* Gets a logger with additional information of the current execution context.
1212
*
@@ -46,7 +46,4 @@ public interface BaseContext extends AutoCloseable {
4646

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

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
import software.amazon.lambda.durable.DurableConfig;
77
import software.amazon.lambda.durable.execution.ExecutionManager;
88
import software.amazon.lambda.durable.execution.ThreadType;
9+
import software.amazon.lambda.durable.logging.DurableLogger;
910

10-
public abstract class BaseContextImpl implements AutoCloseable, BaseContext {
11+
public abstract class BaseContextImpl implements BaseContext {
1112
private final ExecutionManager executionManager;
1213
private final DurableConfig durableConfig;
1314
private final Context lambdaContext;
@@ -109,4 +110,8 @@ public boolean isReplaying() {
109110
public void setExecutionMode() {
110111
this.isReplaying = false;
111112
}
113+
114+
public DurableLogger getLogger() {
115+
return DurableLogger.INSTANCE;
116+
}
112117
}

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: 4 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,9 @@ 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+
// use a try-with-resources to clear logger properties
75+
try (var ignored = DurableLogger.attachContext(context)) {
7576
return handler.apply(userInput, context);
7677
}
7778
},

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

sdk/src/main/java/software/amazon/lambda/durable/logging/DurableLogger.java

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,66 @@
33
package software.amazon.lambda.durable.logging;
44

55
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
67
import org.slf4j.MDC;
78
import software.amazon.lambda.durable.DurableContext;
89
import software.amazon.lambda.durable.StepContext;
9-
import software.amazon.lambda.durable.context.BaseContextImpl;
10+
import software.amazon.lambda.durable.context.BaseContext;
11+
import software.amazon.lambda.durable.model.SafeCloseable;
1012

1113
/**
1214
* Logger wrapper that adds durable execution context to log entries via MDC and optionally suppresses logs during
1315
* replay.
1416
*/
1517
public class DurableLogger {
16-
static final String MDC_EXECUTION_ARN = "durableExecutionArn";
18+
static final String MDC_DURABLE_EXECUTION_ARN = "durableExecutionArn";
19+
static final String MDC_EXECUTION_ARN = "executionArn";
1720
static final String MDC_REQUEST_ID = "requestId";
1821
static final String MDC_OPERATION_ID = "operationId";
1922
static final String MDC_CONTEXT_ID = "contextId";
2023
static final String MDC_OPERATION_NAME = "operationName";
2124
static final String MDC_CONTEXT_NAME = "contextName";
2225
static final String MDC_ATTEMPT = "attempt";
2326

27+
public static final DurableLogger INSTANCE = new DurableLogger(LoggerFactory.getLogger(DurableLogger.class));
28+
private static final SafeCloseable AUTO_CLOSER = DurableLogger::detachContext;
29+
private static final ThreadLocal<BaseContext> CONTEXT = new ThreadLocal<>();
30+
2431
private final Logger delegate;
25-
private final BaseContextImpl context;
2632

2733
/**
2834
* Creates a DurableLogger wrapping the given SLF4J logger with execution context MDC entries.
2935
*
3036
* @param delegate the SLF4J logger to wrap
31-
* @param context the durable execution context providing MDC values
3237
*/
33-
public DurableLogger(Logger delegate, BaseContextImpl context) {
38+
DurableLogger(Logger delegate) {
3439
this.delegate = delegate;
35-
this.context = context;
40+
}
41+
42+
public static SafeCloseable attachContext(BaseContext context) {
43+
CONTEXT.set(context);
44+
if (context != null) {
45+
injectMdcProperties(context);
46+
}
47+
return AUTO_CLOSER;
48+
}
49+
50+
public static void detachContext() {
51+
if (CONTEXT.get() != null) {
52+
CONTEXT.remove();
53+
MDC.clear();
54+
}
55+
}
56+
57+
private static void injectMdcProperties(BaseContext context) {
58+
var config = context.getDurableConfig().getLoggerConfig();
3659

3760
// execution arn
38-
MDC.put(MDC_EXECUTION_ARN, context.getExecutionArn());
61+
if (config.oldKeyNames()) {
62+
MDC.put(MDC_DURABLE_EXECUTION_ARN, context.getExecutionArn());
63+
} else {
64+
MDC.put(MDC_EXECUTION_ARN, context.getExecutionArn());
65+
}
3966

4067
// lambda request id
4168
var requestId =
@@ -47,10 +74,18 @@ public DurableLogger(Logger delegate, BaseContextImpl context) {
4774
if (context instanceof DurableContext) {
4875
// context thread - context id and name
4976
if (context.getContextId() != null) {
50-
MDC.put(MDC_CONTEXT_ID, context.getContextId());
77+
if (config.oldKeyNames()) {
78+
MDC.put(MDC_CONTEXT_ID, context.getContextId());
79+
} else {
80+
MDC.put(MDC_OPERATION_ID, context.getContextId());
81+
}
5182
}
5283
if (context.getContextName() != null) {
53-
MDC.put(MDC_CONTEXT_NAME, context.getContextName());
84+
if (config.oldKeyNames()) {
85+
MDC.put(MDC_CONTEXT_NAME, context.getContextName());
86+
} else {
87+
MDC.put(MDC_OPERATION_NAME, context.getContextName());
88+
}
5489
}
5590
} else if (context instanceof StepContext stepContext) {
5691
// In step context, context id is the operation id, context name is the operation name
@@ -63,11 +98,6 @@ public DurableLogger(Logger delegate, BaseContextImpl context) {
6398
}
6499
}
65100

66-
/** Clears all MDC entries. User set MDC entries will also be removed as the thread will not be used anymore. */
67-
public void close() {
68-
MDC.clear();
69-
}
70-
71101
public void trace(String format, Object... args) {
72102
log(() -> delegate.trace(format, args));
73103
}
@@ -92,13 +122,13 @@ public void error(String message, Throwable t) {
92122
log(() -> delegate.error(message, t));
93123
}
94124

95-
private boolean shouldSuppress() {
96-
return context.getDurableConfig().getLoggerConfig().suppressReplayLogs()
97-
&& context.getExecutionManager().isReplaying();
125+
private boolean shouldSuppress(BaseContext context) {
126+
return context.getDurableConfig().getLoggerConfig().suppressReplayLogs() && context.isReplaying();
98127
}
99128

100129
private void log(Runnable logAction) {
101-
if (!shouldSuppress()) {
130+
var threadLocalContext = CONTEXT.get();
131+
if (threadLocalContext == null || !shouldSuppress(threadLocalContext)) {
102132
logAction.run();
103133
}
104134
}

sdk/src/main/java/software/amazon/lambda/durable/logging/LoggerConfig.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
package software.amazon.lambda.durable.logging;
44

55
/** Configuration for DurableLogger behavior. */
6-
public record LoggerConfig(boolean suppressReplayLogs) {
6+
public record LoggerConfig(boolean suppressReplayLogs, boolean oldKeyNames) {
77

88
/** Default configuration: suppress logs during replay. */
99
public static LoggerConfig defaults() {
10-
return new LoggerConfig(true);
10+
return new LoggerConfig(true, false);
1111
}
1212

1313
/** Configuration that allows logs during replay. */
1414
public static LoggerConfig withReplayLogging() {
15-
return new LoggerConfig(false);
15+
return new LoggerConfig(false, false);
1616
}
1717
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
package software.amazon.lambda.durable.model;
4+
5+
/** A resource that can be safely closed, similar to {@link java.io.Closeable} but without throwing IOException. */
6+
public interface SafeCloseable extends AutoCloseable {
7+
void close();
8+
}

0 commit comments

Comments
 (0)