Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding attempt count to error message #5834

Merged
merged 17 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/next-release/feature-AWSSDKforJavav2-6db3e90.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "feature",
"category": "AWS SDK for Java v2",
"contributor": "",
"description": "Add retry attempt count to exception messages to improve debugging visibility."
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ public static List<MethodSpec> builderInterfaceMethods(ClassName className) {
builderMethod(className, "requestId", String.class),
builderMethod(className, "statusCode", int.class),
builderMethod(className, "cause", Throwable.class),
builderMethod(className, "writableStackTrace", Boolean.class));
builderMethod(className, "writableStackTrace", Boolean.class),
builderMethod(className, "numAttempts", Integer.class));

}

public static List<MethodSpec> builderImplMethods(ClassName className) {
Expand All @@ -44,7 +46,8 @@ public static List<MethodSpec> builderImplMethods(ClassName className) {
builderImplMethods(className, "requestId", String.class),
builderImplMethods(className, "statusCode", int.class),
builderImplMethods(className, "cause", Throwable.class),
builderImplMethods(className, "writableStackTrace", Boolean.class));
builderImplMethods(className, "writableStackTrace", Boolean.class),
builderImplMethods(className, "numAttempts", Integer.class));
}

private static MethodSpec builderMethod(ClassName className, String name, Class clazz) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ public class SourceException extends SdkException {

@Override
SourceException build();

@Override
Builder numAttempts(Integer numAttempts);
}

public static class BuilderImpl extends SdkException.BuilderImpl implements Builder {
Expand All @@ -48,5 +51,11 @@ public class SourceException extends SdkException {
public SourceException build() {
return new SourceException(this);
}

@Override
public Builder numAttempts(Integer numAttempts) {
super.numAttempts(numAttempts);
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ public interface Builder extends AwsServiceException.Builder {

@Override
Builder writableStackTrace(Boolean writableStackTrace);

@Override
Builder numAttempts(Integer numAttempts);
}

protected static class BuilderImpl extends AwsServiceException.BuilderImpl implements Builder {
Expand Down Expand Up @@ -86,6 +89,12 @@ public BuilderImpl writableStackTrace(Boolean writableStackTrace) {
return this;
}

@Override
public BuilderImpl numAttempts(Integer numAttempts) {
this.numAttempts = numAttempts;
return this;
}

@Override
public JsonProtocolTestsException build() {
return new JsonProtocolTestsException(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ public interface Builder extends SdkPojo, CopyableBuilder<Builder, EmptyModeledE

@Override
Builder writableStackTrace(Boolean writableStackTrace);

@Override
Builder numAttempts(Integer numAttempts);
}

static final class BuilderImpl extends JsonProtocolTestsException.BuilderImpl implements Builder {
Expand Down Expand Up @@ -116,6 +119,12 @@ public BuilderImpl writableStackTrace(Boolean writableStackTrace) {
return this;
}

@Override
public BuilderImpl numAttempts(Integer numAttempts) {
this.numAttempts = numAttempts;
return this;
}

@Override
public EmptyModeledException build() {
return new EmptyModeledException(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ public interface Builder extends SdkPojo, CopyableBuilder<Builder, JsonServiceIn

@Override
Builder writableStackTrace(Boolean writableStackTrace);

@Override
Builder numAttempts(Integer numAttempts);
}

static final class BuilderImpl extends JsonException.BuilderImpl implements Builder {
Expand Down Expand Up @@ -125,6 +128,12 @@ public BuilderImpl writableStackTrace(Boolean writableStackTrace) {
return this;
}

@Override
public BuilderImpl numAttempts(Integer numAttempts) {
this.numAttempts = numAttempts;
return this;
}

@Override
public JsonServiceInternalServerErrorException build() {
return new JsonServiceInternalServerErrorException(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ public interface Builder extends SdkPojo, CopyableBuilder<Builder, JsonServiceIn

@Override
Builder writableStackTrace(Boolean writableStackTrace);

@Override
Builder numAttempts(Integer numAttempts);
}

static final class BuilderImpl extends JsonException.BuilderImpl implements Builder {
Expand Down Expand Up @@ -119,6 +122,12 @@ public BuilderImpl writableStackTrace(Boolean writableStackTrace) {
return this;
}

@Override
public BuilderImpl numAttempts(Integer numAttempts) {
this.numAttempts = numAttempts;
return this;
}

@Override
public JsonServiceInvalidInputException build() {
return new JsonServiceInvalidInputException(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ public interface Builder extends SdkPojo, CopyableBuilder<Builder, JsonServiceTh

@Override
Builder writableStackTrace(Boolean writableStackTrace);

@Override
Builder numAttempts(Integer numAttempts);
}

static final class BuilderImpl extends JsonException.BuilderImpl implements Builder {
Expand Down Expand Up @@ -129,6 +132,12 @@ public BuilderImpl writableStackTrace(Boolean writableStackTrace) {
return this;
}

@Override
public BuilderImpl numAttempts(Integer numAttempts) {
this.numAttempts = numAttempts;
return this;
}

@Override
public JsonServiceThrottlingException build() {
return new JsonServiceThrottlingException(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.awscore.internal.AwsErrorCode;
import software.amazon.awssdk.awscore.internal.AwsStatusCode;
import software.amazon.awssdk.core.exception.SdkDiagnostics;
import software.amazon.awssdk.core.exception.SdkServiceException;
import software.amazon.awssdk.core.retry.ClockSkew;
import software.amazon.awssdk.http.SdkHttpResponse;
Expand Down Expand Up @@ -60,25 +61,36 @@ public AwsErrorDetails awsErrorDetails() {

@Override
public String getMessage() {
StringJoiner joiner = new StringJoiner(" ");
String primaryMessage = rawMessage();
if (primaryMessage == null && awsErrorDetails != null) {
primaryMessage = awsErrorDetails.errorMessage();
}
if (primaryMessage != null) {
joiner.add(primaryMessage);
}

if (awsErrorDetails != null) {
StringJoiner details = new StringJoiner(", ", "(", ")");
details.add("Service: " + awsErrorDetails().serviceName());
details.add("Status Code: " + statusCode());
details.add("Request ID: " + requestId());
if (extendedRequestId() != null) {
details.add("Extended Request ID: " + extendedRequestId());
}
String message = super.getMessage();
if (message == null) {
message = awsErrorDetails().errorMessage();
}
if (message == null) {
return details.toString();
}
return message + " " + details;
joiner.add(serviceDiagnostics());
}

return super.getMessage();
if (numAttempts() != null) {
SdkDiagnostics diagnostics = SdkDiagnostics.builder().numAttempts(numAttempts()).build();
joiner.add(diagnostics.toString());
}

return joiner.toString();
}

private String serviceDiagnostics() {
StringJoiner details = new StringJoiner(", ", "(", ")");
details.add("Service: " + awsErrorDetails().serviceName());
details.add("Status Code: " + statusCode());
details.add("Request ID: " + requestId());
if (extendedRequestId() != null) {
details.add("Extended Request ID: " + extendedRequestId());
}
return details.toString();
}

@Override
Expand Down Expand Up @@ -116,9 +128,9 @@ public boolean isClockSkewException() {
@Override
public boolean isThrottlingException() {
return super.isThrottlingException() ||
Optional.ofNullable(awsErrorDetails)
.map(a -> AwsErrorCode.isThrottlingErrorCode(a.errorCode()))
.orElse(false);
Optional.ofNullable(awsErrorDetails)
.map(a -> AwsErrorCode.isThrottlingErrorCode(a.errorCode()))
.orElse(false);
}

/**
Expand Down Expand Up @@ -173,6 +185,9 @@ public interface Builder extends SdkServiceException.Builder {
@Override
Builder message(String message);

@Override
Builder numAttempts(Integer numAttempts);

@Override
Builder cause(Throwable t);

Expand Down Expand Up @@ -238,6 +253,12 @@ public Builder message(String message) {
return this;
}

@Override
public Builder numAttempts(Integer numAttempts) {
this.numAttempts = numAttempts;
return this;
}

@Override
public Builder cause(Throwable cause) {
this.cause = cause;
Expand Down Expand Up @@ -273,4 +294,4 @@ public AwsServiceException build() {
return new AwsServiceException(this);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,48 +31,32 @@
public class AwsServiceExceptionSerializationTest {

@Test
public void serializeServiceException() throws Exception {
public void serializeBasicServiceException() throws Exception {
AwsServiceException expectedException = createException();

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(expectedException);
objectOutputStream.flush();
objectOutputStream.close();

ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(outputStream.toByteArray()));
AwsServiceException resultException = (AwsServiceException) ois.readObject();

AwsServiceException resultException = serializeServiceException(expectedException);
assertSameValues(resultException, expectedException);
}

private void assertSameValues(AwsServiceException resultException, AwsServiceException expectedException) {
assertThat(resultException.getMessage()).isEqualTo(expectedException.getMessage());
assertThat(resultException.requestId()).isEqualTo(expectedException.requestId());
assertThat(resultException.extendedRequestId()).isEqualTo(expectedException.extendedRequestId());
assertThat(resultException.toBuilder().clockSkew()).isEqualTo(expectedException.toBuilder().clockSkew());
assertThat(resultException.toBuilder().cause().getMessage()).isEqualTo(expectedException.toBuilder().cause().getMessage());
assertThat(resultException.awsErrorDetails()).isEqualTo(expectedException.awsErrorDetails());
@Test
public void serializeRetryableServiceException() throws Exception {
AwsServiceException expectedException = createRetryableServiceException();
AwsServiceException resultException = serializeServiceException(expectedException);
assertSameValues(resultException, expectedException);
}

private void assertSameValues(AwsServiceException result, AwsServiceException expected) {
assertThat(result.getMessage()).isEqualTo(expected.getMessage());
assertThat(result.requestId()).isEqualTo(expected.requestId());
assertThat(result.extendedRequestId()).isEqualTo(expected.extendedRequestId());
assertThat(result.toBuilder().clockSkew()).isEqualTo(expected.toBuilder().clockSkew());
assertThat(result.toBuilder().cause().getMessage()).isEqualTo(expected.toBuilder().cause().getMessage());
assertThat(result.awsErrorDetails()).isEqualTo(expected.awsErrorDetails());
assertThat(result.numAttempts()).isEqualTo(expected.numAttempts());
}

private AwsServiceException createException() {
AbortableInputStream contentStream = AbortableInputStream.create(new StringInputStream("some content"));
SdkHttpResponse httpResponse = SdkHttpFullResponse.builder()
.statusCode(403)
.statusText("SomeText")
.content(contentStream)
.build();

AwsErrorDetails errorDetails = AwsErrorDetails.builder()
.errorCode("someCode")
.errorMessage("message")
.serviceName("someService")
.sdkHttpResponse(httpResponse)
.build();

return AwsServiceException.builder()
.awsErrorDetails(errorDetails)
.awsErrorDetails(createErrorDetails(403, "SomeText"))
.statusCode(403)
.cause(new RuntimeException("someThrowable"))
.clockSkew(Duration.ofSeconds(2))
Expand All @@ -81,4 +65,44 @@ private AwsServiceException createException() {
.message("message")
.build();
}

private AwsServiceException createRetryableServiceException() {
return AwsServiceException.builder()
.awsErrorDetails(createErrorDetails(429, "Throttling"))
.statusCode(429)
.cause(new RuntimeException("someThrowable"))
.clockSkew(Duration.ofSeconds(2))
.requestId("requestId")
.extendedRequestId("extendedRequestId")
.message("message")
.numAttempts(3)
.build();
}

private AwsErrorDetails createErrorDetails(int statusCode, String statusText) {
AbortableInputStream contentStream = AbortableInputStream.create(new StringInputStream("some content"));
SdkHttpResponse httpResponse = SdkHttpFullResponse.builder()
.statusCode(statusCode)
.statusText(statusText)
.content(contentStream)
.build();

return AwsErrorDetails.builder()
.errorCode("someCode")
.errorMessage("message")
.serviceName("someService")
.sdkHttpResponse(httpResponse)
.build();
}

private AwsServiceException serializeServiceException(AwsServiceException exception) throws Exception {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(exception);
objectOutputStream.flush();
objectOutputStream.close();

ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(outputStream.toByteArray()));
return (AwsServiceException) ois.readObject();
}
}
Loading
Loading