Skip to content

Commit 892cdbd

Browse files
committed
feat: Emit worker pool size as a DEBUG log event during init on Lambda Managed Instances
1 parent 99e0349 commit 892cdbd

8 files changed

Lines changed: 76 additions & 4 deletions

File tree

aws-lambda-java-runtime-interface-client/RELEASE.CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
### September 2, 2026
2+
`2.12.1`
3+
- Emit a structured `runtime_worker_pool_initializing` DEBUG log event once during INIT in multi-concurrent (Lambda Managed Instances) mode, reporting the worker pool size (`workerCount`) and the maximum concurrency the execution environment supports (`executionEnvironmentMaxConcurrency`). Only visible when the function log level is DEBUG or lower; not emitted for standard on-demand functions.
4+
15
### July 17, 2026
26
`2.12.0`
37
- Add `Lambda-Runtime-Invocation-Id` header support for cross-wiring protection. The RIC now echoes the invocation ID received from RAPID on `/next` back on `/response` and `/error`, enabling RAPID to detect and reject stale responses from timed-out invocations.

aws-lambda-java-runtime-interface-client/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<modelVersion>4.0.0</modelVersion>
55
<groupId>com.amazonaws</groupId>
66
<artifactId>aws-lambda-java-runtime-interface-client</artifactId>
7-
<version>2.12.0-SNAPSHOT</version>
7+
<version>2.12.1-SNAPSHOT</version>
88
<packaging>jar</packaging>
99

1010
<name>AWS Lambda Java Runtime Interface Client</name>

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambda.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,11 @@ protected static void startRuntimeLoops(LambdaRequestHandler lambdaRequestHandle
251251
if (concurrencyConfig.isMultiConcurrent()) {
252252
lambdaLogger.log(concurrencyConfig.getConcurrencyConfigMessage(), lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.INFO : LogLevel.UNDEFINED);
253253
ExecutorService platformThreadExecutor = Executors.newFixedThreadPool(concurrencyConfig.getNumberOfPlatformThreads());
254+
lambdaLogger.logStructuredEvent(
255+
new WorkerPoolInitializedEvent(
256+
concurrencyConfig.getNumberOfPlatformThreads(),
257+
concurrencyConfig.getNumberOfPlatformThreads()),
258+
LogLevel.DEBUG);
254259
try {
255260
for (int i = 0; i < concurrencyConfig.getNumberOfPlatformThreads(); i++) {
256261
startRuntimeLoopWithExecutor(lambdaRequestHandler, lambdaLogger, platformThreadExecutor, runtimeClient);
@@ -373,4 +378,15 @@ private static void logExceptionCloudWatch(LambdaContextLogger lambdaLogger, Exc
373378
protected static URLClassLoader getCustomerClassLoader() {
374379
return customerClassLoader;
375380
}
381+
382+
static class WorkerPoolInitializedEvent {
383+
final String event = "runtime_worker_pool_initializing";
384+
final int workerCount;
385+
final int executionEnvironmentMaxConcurrency;
386+
387+
WorkerPoolInitializedEvent(int workerCount, int executionEnvironmentMaxConcurrency) {
388+
this.workerCount = workerCount;
389+
this.executionEnvironmentMaxConcurrency = executionEnvironmentMaxConcurrency;
390+
}
391+
}
376392
}

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/AbstractLambdaLogger.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ public void log(byte[] message) {
6464
this.log(message, LogLevel.UNDEFINED);
6565
}
6666

67+
public void logStructuredEvent(Object event, LogLevel logLevel) {
68+
if (logFiltering.isEnabled(logLevel)) {
69+
this.logMessage(logFormatter.format(event, logLevel), logLevel);
70+
}
71+
}
72+
6773
public void setLambdaContext(LambdaContext lambdaContext) {
6874
this.logFormatter.setLambdaContext(lambdaContext);
6975
}

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/JsonLogFormatter.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,22 @@ public class JsonLogFormatter implements LogFormatter {
2626

2727
@Override
2828
public String format(String message, LogLevel logLevel) {
29+
return serialize(createLogMessage(message, logLevel));
30+
}
31+
32+
@Override
33+
public String format(Object message, LogLevel logLevel) {
34+
return serialize(createLogMessage(message, logLevel));
35+
}
36+
37+
private String serialize(StructuredLogMessage msg) {
2938
ByteArrayOutputStream stream = new ByteArrayOutputStream();
30-
StructuredLogMessage msg = createLogMessage(message, logLevel);
3139
serializer.toJson(msg, stream);
3240
stream.write('\n');
3341
return new String(stream.toByteArray(), StandardCharsets.UTF_8);
3442
}
3543

36-
private StructuredLogMessage createLogMessage(String message, LogLevel logLevel) {
44+
private StructuredLogMessage createLogMessage(Object message, LogLevel logLevel) {
3745
StructuredLogMessage msg = new StructuredLogMessage();
3846
msg.timestamp = dateFormatter.format(LocalDateTime.now());
3947
msg.message = message;

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LogFormatter.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
public interface LogFormatter {
1212
String format(String message, LogLevel logLevel);
1313

14+
default String format(Object message, LogLevel logLevel) {
15+
return format(String.valueOf(message), logLevel);
16+
}
17+
1418
default void setLambdaContext(LambdaContext context) {
1519
}
1620
}

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/StructuredLogMessage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
class StructuredLogMessage {
1111
public String timestamp;
12-
public String message;
12+
public Object message;
1313
public LogLevel level;
1414
public String AWSRequestId;
1515
public String tenantId;

aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambdaTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,40 @@ void testSequentialWithVirtualMachineErrorStopsLoop() throws Throwable {
576576
assertEquals(2 * SampleHandler.nOfIterations, SampleHandler.globalCounter.get());
577577
}
578578

579+
@Test
580+
@Timeout(value = 1, unit = TimeUnit.MINUTES)
581+
void testWorkerPoolInitializedEventEmittedOnceInMultiConcurrentMode() throws Throwable {
582+
when(concurrencyConfig.isMultiConcurrent()).thenReturn(true);
583+
when(concurrencyConfig.getNumberOfPlatformThreads()).thenReturn(4);
584+
585+
when(runtimeClient.nextInvocationWithExponentialBackoff(lambdaLogger))
586+
.thenThrow(fakelambdaRuntimeClientMaxRetriesExceededException);
587+
588+
AWSLambda.startRuntimeLoops(lambdaRequestHandler, lambdaLogger, concurrencyConfig, runtimeClient);
589+
590+
org.mockito.ArgumentCaptor<Object> eventCaptor = org.mockito.ArgumentCaptor.forClass(Object.class);
591+
verify(lambdaLogger, times(1)).logStructuredEvent(eventCaptor.capture(), eq(LogLevel.DEBUG));
592+
593+
AWSLambda.WorkerPoolInitializedEvent event = (AWSLambda.WorkerPoolInitializedEvent) eventCaptor.getValue();
594+
assertEquals("runtime_worker_pool_initializing", event.event);
595+
assertEquals(4, event.workerCount);
596+
assertEquals(4, event.executionEnvironmentMaxConcurrency);
597+
}
598+
599+
@Test
600+
@Timeout(value = 1, unit = TimeUnit.MINUTES)
601+
void testWorkerPoolInitializedEventNotEmittedInSequentialMode() throws Throwable {
602+
when(concurrencyConfig.isMultiConcurrent()).thenReturn(false);
603+
604+
InvocationRequest fatalRequest = mock(InvocationRequest.class);
605+
when(fatalRequest.getId()).thenThrow(UserFault.makeUserFault(new IOError(new Throwable()), true)).thenReturn("fatal");
606+
when(runtimeClient.nextInvocation()).thenReturn(fatalRequest);
607+
608+
AWSLambda.startRuntimeLoops(lambdaRequestHandler, lambdaLogger, concurrencyConfig, runtimeClient);
609+
610+
verify(lambdaLogger, never()).logStructuredEvent(any(), any());
611+
}
612+
579613
@Test
580614
@Timeout(value = 1, unit = TimeUnit.MINUTES)
581615
void testInvocationIdIsPassedToReportSuccess() throws Throwable {

0 commit comments

Comments
 (0)