From f589b183e36a0768ae84c51cc0b5b051f95c2fdd Mon Sep 17 00:00:00 2001 From: Maxime David Date: Wed, 31 Jul 2024 16:37:51 +0100 Subject: [PATCH] Use Lambda-Runtime-Function-Error-Type for reporting errors in format "Runtime." (#501) * Introduce Runtime. error codes * fix: apply linting * Remove unused imports --------- Co-authored-by: Alexander Smirnov --- .../lambda/runtime/api/client/AWSLambda.java | 31 ++++++++++++------- .../api/client/runtimeapi/DtoSerializers.java | 6 ++-- .../api/client/runtimeapi/LambdaError.java | 27 ++++++++++++++++ .../runtimeapi/LambdaRuntimeApiClient.java | 10 ------ .../LambdaRuntimeApiClientImpl.java | 28 ++++++----------- .../api/client/runtimeapi/RapidErrorType.java | 16 ++++++++++ .../converters/LambdaErrorConverter.java | 15 ++++----- .../{LambdaError.java => ErrorRequest.java} | 6 ++-- 8 files changed, 86 insertions(+), 53 deletions(-) create mode 100644 aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaError.java create mode 100644 aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/RapidErrorType.java rename aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/{LambdaError.java => ErrorRequest.java} (75%) diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambda.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambda.java index 75513d36..986f8b7b 100644 --- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambda.java +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambda.java @@ -11,13 +11,13 @@ import com.amazonaws.services.lambda.runtime.api.client.logging.LambdaContextLogger; import com.amazonaws.services.lambda.runtime.api.client.logging.LogSink; import com.amazonaws.services.lambda.runtime.api.client.logging.StdOutLogSink; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaError; import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeApiClient; import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeApiClientImpl; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.RapidErrorType; import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.converters.LambdaErrorConverter; import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.converters.XRayErrorCauseConverter; import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest; -import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.LambdaError; -import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.XRayErrorCause; import com.amazonaws.services.lambda.runtime.api.client.util.LambdaOutputStream; import com.amazonaws.services.lambda.runtime.api.client.util.UnsafeUtil; import com.amazonaws.services.lambda.runtime.logging.LogFormat; @@ -215,7 +215,9 @@ private static void startRuntime(String handler, LambdaContextLogger lambdaLogge requestHandler = findRequestHandler(handler, customerClassLoader); } catch (UserFault userFault) { lambdaLogger.log(userFault.reportableError(), lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.ERROR : LogLevel.UNDEFINED); - LambdaError error = LambdaErrorConverter.fromUserFault(userFault); + LambdaError error = new LambdaError( + LambdaErrorConverter.fromUserFault(userFault), + RapidErrorType.BadFunctionCode); runtimeClient.reportInitError(error); System.exit(1); return; @@ -243,17 +245,20 @@ private static void startRuntime(String handler, LambdaContextLogger lambdaLogge shouldExit = f.fatal; userFault = f; UserFault.filterStackTrace(f); - - LambdaError error = LambdaErrorConverter.fromUserFault(f); + LambdaError error = new LambdaError( + LambdaErrorConverter.fromUserFault(f), + RapidErrorType.BadFunctionCode); runtimeClient.reportInvocationError(request.getId(), error); } catch (Throwable t) { shouldExit = t instanceof VirtualMachineError || t instanceof IOError; UserFault.filterStackTrace(t); userFault = UserFault.makeUserFault(t); - LambdaError error = LambdaErrorConverter.fromThrowable(t); - XRayErrorCause xRayErrorCause = XRayErrorCauseConverter.fromThrowable(t); - runtimeClient.reportInvocationError(request.getId(), error, xRayErrorCause); + LambdaError error = new LambdaError( + LambdaErrorConverter.fromThrowable(t), + XRayErrorCauseConverter.fromThrowable(t), + RapidErrorType.UserException); + runtimeClient.reportInvocationError(request.getId(), error); } finally { if (userFault != null) { lambdaLogger.log(userFault.reportableError(), lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.ERROR : LogLevel.UNDEFINED); @@ -268,16 +273,18 @@ static void onInitComplete(final LambdaContextLogger lambdaLogger) throws IOExce runtimeClient.restoreNext(); } catch (Exception e1) { logExceptionCloudWatch(lambdaLogger, e1); - LambdaError error = LambdaErrorConverter.fromThrowable(e1); - runtimeClient.reportInitError(error); + runtimeClient.reportInitError(new LambdaError( + LambdaErrorConverter.fromThrowable(e1), + RapidErrorType.BeforeCheckpointError)); System.exit(64); } try { Core.getGlobalContext().afterRestore(null); } catch (Exception restoreExc) { logExceptionCloudWatch(lambdaLogger, restoreExc); - LambdaError error = LambdaErrorConverter.fromThrowable(restoreExc); - runtimeClient.reportRestoreError(error); + runtimeClient.reportRestoreError(new LambdaError( + LambdaErrorConverter.fromThrowable(restoreExc), + RapidErrorType.AfterRestoreError)); System.exit(64); } } diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/DtoSerializers.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/DtoSerializers.java index cda811d7..9f0045e0 100644 --- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/DtoSerializers.java +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/DtoSerializers.java @@ -4,7 +4,7 @@ */ package com.amazonaws.services.lambda.runtime.api.client.runtimeapi; -import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.LambdaError; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.ErrorRequest; import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.XRayErrorCause; import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; import com.amazonaws.services.lambda.runtime.serialization.factories.GsonFactory; @@ -13,7 +13,7 @@ public class DtoSerializers { - public static byte[] serialize(LambdaError error) { + public static byte[] serialize(ErrorRequest error) { return serialize(error, SingletonHelper.LAMBDA_ERROR_SERIALIZER); } @@ -36,7 +36,7 @@ private static byte[] serialize(T pojo, PojoSerializer serializer) { * This way the serializers will be loaded lazily */ private static class SingletonHelper { - private static final PojoSerializer LAMBDA_ERROR_SERIALIZER = GsonFactory.getInstance().getSerializer(LambdaError.class); + private static final PojoSerializer LAMBDA_ERROR_SERIALIZER = GsonFactory.getInstance().getSerializer(ErrorRequest.class); private static final PojoSerializer X_RAY_ERROR_CAUSE_SERIALIZER = GsonFactory.getInstance().getSerializer(XRayErrorCause.class); } } diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaError.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaError.java new file mode 100644 index 00000000..cb59a8c0 --- /dev/null +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaError.java @@ -0,0 +1,27 @@ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ +package com.amazonaws.services.lambda.runtime.api.client.runtimeapi; + +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.ErrorRequest; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.XRayErrorCause; + +public class LambdaError { + + public final ErrorRequest errorRequest; + + public final XRayErrorCause xRayErrorCause; + + public final RapidErrorType errorType; + + public LambdaError(ErrorRequest errorRequest, XRayErrorCause xRayErrorCause, RapidErrorType errorType) { + this.errorRequest = errorRequest; + this.xRayErrorCause = xRayErrorCause; + this.errorType = errorType; + } + + public LambdaError(ErrorRequest errorRequest, RapidErrorType errorType) { + this(errorRequest, null, errorType); + } +} diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClient.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClient.java index 7715b994..e2ae0969 100644 --- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClient.java +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClient.java @@ -5,8 +5,6 @@ package com.amazonaws.services.lambda.runtime.api.client.runtimeapi; import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest; -import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.LambdaError; -import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.XRayErrorCause; import java.io.IOException; /** @@ -40,14 +38,6 @@ public interface LambdaRuntimeApiClient { */ void reportInvocationError(String requestId, LambdaError error) throws IOException; - /** - * Report invocation error - * @param requestId request id - * @param error error to report - * @param xRayErrorCause X-Ray error cause - */ - void reportInvocationError(String requestId, LambdaError error, XRayErrorCause xRayErrorCause) throws IOException; - /** * SnapStart endpoint to report that beforeCheckoint hooks were executed */ diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImpl.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImpl.java index 21691cf4..65024b98 100644 --- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImpl.java +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImpl.java @@ -5,8 +5,6 @@ package com.amazonaws.services.lambda.runtime.api.client.runtimeapi; import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest; -import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.LambdaError; -import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.XRayErrorCause; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -46,7 +44,7 @@ public LambdaRuntimeApiClientImpl(String hostnameAndPort) { @Override public void reportInitError(LambdaError error) throws IOException { String endpoint = this.baseUrl + "/2018-06-01/runtime/init/error"; - reportLambdaError(endpoint, error, null); + reportLambdaError(endpoint, error, XRAY_ERROR_CAUSE_MAX_HEADER_SIZE); } @Override @@ -61,13 +59,8 @@ public void reportInvocationSuccess(String requestId, byte[] response) { @Override public void reportInvocationError(String requestId, LambdaError error) throws IOException { - reportInvocationError(requestId, error, null); - } - - @Override - public void reportInvocationError(String requestId, LambdaError error, XRayErrorCause xRayErrorCause) throws IOException { String endpoint = invocationEndpoint + requestId + "/error"; - reportLambdaError(endpoint, error, xRayErrorCause); + reportLambdaError(endpoint, error, XRAY_ERROR_CAUSE_MAX_HEADER_SIZE); } @Override @@ -82,22 +75,21 @@ public void restoreNext() throws IOException { @Override public void reportRestoreError(LambdaError error) throws IOException { String endpoint = this.baseUrl + "/2018-06-01/runtime/restore/error"; - reportLambdaError(endpoint, error, null); + reportLambdaError(endpoint, error, XRAY_ERROR_CAUSE_MAX_HEADER_SIZE); } - void reportLambdaError(String endpoint, LambdaError error, XRayErrorCause xRayErrorCause) throws IOException { + void reportLambdaError(String endpoint, LambdaError error, int maxXrayHeaderSize) throws IOException { Map headers = new HashMap<>(); - if (error.errorType != null && !error.errorType.isEmpty()) { - headers.put(ERROR_TYPE_HEADER, error.errorType); - } - if (xRayErrorCause != null) { - byte[] xRayErrorCauseJson = DtoSerializers.serialize(xRayErrorCause); - if (xRayErrorCauseJson != null && xRayErrorCauseJson.length < XRAY_ERROR_CAUSE_MAX_HEADER_SIZE) { + headers.put(ERROR_TYPE_HEADER, error.errorType.getRapidError()); + + if (error.xRayErrorCause != null) { + byte[] xRayErrorCauseJson = DtoSerializers.serialize(error.xRayErrorCause); + if (xRayErrorCauseJson != null && xRayErrorCauseJson.length < maxXrayHeaderSize) { headers.put(XRAY_ERROR_CAUSE_HEADER, new String(xRayErrorCauseJson)); } } - byte[] payload = DtoSerializers.serialize(error); + byte[] payload = DtoSerializers.serialize(error.errorRequest); int responseCode = doPost(endpoint, headers, payload); if (responseCode != HTTP_ACCEPTED) { throw new LambdaRuntimeClientException(endpoint, responseCode); diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/RapidErrorType.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/RapidErrorType.java new file mode 100644 index 00000000..b471ce3f --- /dev/null +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/RapidErrorType.java @@ -0,0 +1,16 @@ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ +package com.amazonaws.services.lambda.runtime.api.client.runtimeapi; + +public enum RapidErrorType { + BadFunctionCode, + UserException, + BeforeCheckpointError, + AfterRestoreError; + + public String getRapidError() { + return "Runtime." + this; + } +} diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/converters/LambdaErrorConverter.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/converters/LambdaErrorConverter.java index decef446..a2520bf7 100644 --- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/converters/LambdaErrorConverter.java +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/converters/LambdaErrorConverter.java @@ -5,20 +5,21 @@ package com.amazonaws.services.lambda.runtime.api.client.runtimeapi.converters; import com.amazonaws.services.lambda.runtime.api.client.UserFault; -import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.LambdaError; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.ErrorRequest; public class LambdaErrorConverter { private LambdaErrorConverter() { } - public static LambdaError fromUserFault(UserFault userFault) { + public static ErrorRequest fromUserFault(UserFault userFault) { // Not setting stacktrace for compatibility with legacy/native runtime - return new LambdaError(userFault.msg, userFault.exception, null); + return new ErrorRequest(userFault.msg, userFault.exception, null); } - public static LambdaError fromThrowable(Throwable throwable) { - String errorMessage = throwable.getLocalizedMessage() == null - ? throwable.getClass().getName() : throwable.getLocalizedMessage(); + public static ErrorRequest fromThrowable(Throwable throwable) { + String errorMessage = throwable.getLocalizedMessage() == null + ? throwable.getClass().getName() + : throwable.getLocalizedMessage(); String errorType = throwable.getClass().getName(); StackTraceElement[] trace = throwable.getStackTrace(); @@ -26,6 +27,6 @@ public static LambdaError fromThrowable(Throwable throwable) { for (int i = 0; i < trace.length; i++) { stackTrace[i] = trace[i].toString(); } - return new LambdaError(errorMessage, errorType, stackTrace); + return new ErrorRequest(errorMessage, errorType, stackTrace); } } diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/LambdaError.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/ErrorRequest.java similarity index 75% rename from aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/LambdaError.java rename to aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/ErrorRequest.java index 7b5abc10..d5886a67 100644 --- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/LambdaError.java +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/ErrorRequest.java @@ -4,16 +4,16 @@ */ package com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto; -public class LambdaError { +public class ErrorRequest { public String errorMessage; public String errorType; public String[] stackTrace; @SuppressWarnings("unused") - public LambdaError() { + public ErrorRequest() { } - public LambdaError(String errorMessage, String errorType, String[] stackTrace) { + public ErrorRequest(String errorMessage, String errorType, String[] stackTrace) { this.errorMessage = errorMessage; this.errorType = errorType; this.stackTrace = stackTrace;