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

Fix http attributes of AWS SDK V2 instrumentation #8931

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ dependencies {
testLibrary("software.amazon.awssdk:s3:2.2.0")
testLibrary("software.amazon.awssdk:sqs:2.2.0")
testLibrary("software.amazon.awssdk:sns:2.2.0")
testLibrary("software.amazon.awssdk:ses:2.2.0")
}

tasks {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

import io.opentelemetry.instrumentation.awssdk.v2_2.AbstractQueryProtocolModelTest;
import io.opentelemetry.instrumentation.awssdk.v2_2.AwsSdkTelemetry;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;

public class QueryProtocolModelTest extends AbstractQueryProtocolModelTest {
rapphil marked this conversation as resolved.
Show resolved Hide resolved
private final AgentInstrumentationExtension extension = AgentInstrumentationExtension.create();
trask marked this conversation as resolved.
Show resolved Hide resolved
trask marked this conversation as resolved.
Show resolved Hide resolved

@Override
protected ClientOverrideConfiguration.Builder createClientOverrideConfigurationBuilder() {
return ClientOverrideConfiguration.builder()
.addExecutionInterceptor(
trask marked this conversation as resolved.
Show resolved Hide resolved
AwsSdkTelemetry.builder(extension.getOpenTelemetry())
.build()
.newExecutionInterceptor());
}

@Override
protected InstrumentationExtension getInstrumentationExtension() {
trask marked this conversation as resolved.
Show resolved Hide resolved
return extension;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dependencies {
testLibrary("software.amazon.awssdk:kinesis:2.2.0")
testLibrary("software.amazon.awssdk:rds:2.2.0")
testLibrary("software.amazon.awssdk:s3:2.2.0")
testLibrary("software.amazon.awssdk:ses:2.2.0")
}

testing {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,13 @@ public SdkRequest modifyRequest(
}

@Override
public void afterMarshalling(
Context.AfterMarshalling context, ExecutionAttributes executionAttributes) {

public void beforeTransmission(
Context.BeforeTransmission context, ExecutionAttributes executionAttributes) {
// In beforeTransmission we get access to the finalized http request, including modifications
// performed by other interceptors and the message signature.
// It is unlikely that further modifications are performed by the http client performing the
// request given that this would require the signature to be regenerated.
//
// Since we merge the HTTP attributes into an already started span instead of creating a
// full child span, we have to do some dirty work here.
//
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.awssdk.v2_2;

import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;

public class QueryProtocolModelTest extends AbstractQueryProtocolModelTest {
private final LibraryInstrumentationExtension extension =
LibraryInstrumentationExtension.create();

@Override
protected ClientOverrideConfiguration.Builder createClientOverrideConfigurationBuilder() {
return ClientOverrideConfiguration.builder()
.addExecutionInterceptor(
AwsSdkTelemetry.builder(extension.getOpenTelemetry())
.build()
.newExecutionInterceptor());
}

@Override
protected InstrumentationExtension getInstrumentationExtension() {
return extension;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dependencies {
compileOnly("software.amazon.awssdk:s3:2.2.0")
compileOnly("software.amazon.awssdk:sqs:2.2.0")
compileOnly("software.amazon.awssdk:sns:2.2.0")
compileOnly("software.amazon.awssdk:ses:2.2.0")

// needed for SQS - using emq directly as localstack references emq v0.15.7 ie WITHOUT AWS trace header propagation
implementation("org.elasticmq:elasticmq-rest-sqs_2.12:1.0.0")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ abstract class AbstractAws2ClientTest extends AbstractAws2ClientCoreTest {
"$SemanticAttributes.RPC_SYSTEM" "aws-api"
"$SemanticAttributes.RPC_SERVICE" "S3"
"$SemanticAttributes.RPC_METHOD" "GetObject"
"$SemanticAttributes.USER_AGENT_ORIGINAL" String
"aws.agent" "java-aws-sdk"
"aws.bucket.name" "somebucket"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ abstract class AbstractAws2SqsTracingTest extends InstrumentationSpecification {
"http.method" "POST"
"http.status_code" 200
"http.url" { it.startsWith("http://localhost:$sqsPort") }
"$SemanticAttributes.USER_AGENT_ORIGINAL" String
"net.peer.name" "localhost"
"net.peer.port" sqsPort
"$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long }
Expand Down Expand Up @@ -310,6 +311,7 @@ abstract class AbstractAws2SqsTracingTest extends InstrumentationSpecification {
"http.method" "POST"
"http.status_code" 200
"http.url" { it.startsWith("http://localhost:$sqsPort") }
"$SemanticAttributes.USER_AGENT_ORIGINAL" String
"net.peer.name" "localhost"
"net.peer.port" sqsPort
"$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long }
Expand Down Expand Up @@ -364,6 +366,7 @@ abstract class AbstractAws2SqsTracingTest extends InstrumentationSpecification {
"http.status_code" 200
"http.url" { it.startsWith("http://localhost:$sqsPort") }
"net.peer.name" "localhost"
"$SemanticAttributes.USER_AGENT_ORIGINAL" String
"net.peer.port" sqsPort
"$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long }
"$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.awssdk.v2_2;

import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_URL;

import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.testing.internal.armeria.common.HttpResponse;
import io.opentelemetry.testing.internal.armeria.common.HttpStatus;
import io.opentelemetry.testing.internal.armeria.common.MediaType;
import io.opentelemetry.testing.internal.armeria.testing.junit5.server.mock.MockWebServerExtension;
import java.net.URI;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ses.SesClient;
import software.amazon.awssdk.services.ses.model.Body;
import software.amazon.awssdk.services.ses.model.Content;
import software.amazon.awssdk.services.ses.model.Destination;
import software.amazon.awssdk.services.ses.model.Message;
import software.amazon.awssdk.services.ses.model.SendEmailRequest;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public abstract class AbstractQueryProtocolModelTest {
private final MockWebServerExtension server = new MockWebServerExtension();

@BeforeAll
public void setup() {
server.start();
}

@AfterAll
public void end() {
server.stop();
}

@BeforeEach
public void setupEach() {
server.beforeTestExecution(null);
}

protected abstract ClientOverrideConfiguration.Builder createClientOverrideConfigurationBuilder();

protected abstract InstrumentationExtension getInstrumentationExtension();

@Test
public void testClientWithQueryProtocolModel() {
rapphil marked this conversation as resolved.
Show resolved Hide resolved
server.enqueue(
HttpResponse.of(
HttpStatus.OK,
MediaType.PLAIN_TEXT_UTF_8,
"<SendEmailResponse><MessageId>12345</MessageId></SendEmailResponse>"));
SesClient ses =
SesClient.builder()
.endpointOverride(server.httpUri())
.credentialsProvider(
StaticCredentialsProvider.create(AwsBasicCredentials.create("foo", "bar")))
.overrideConfiguration(createClientOverrideConfigurationBuilder().build())
.region(Region.US_WEST_2)
.build();

Destination destination = Destination.builder().toAddresses("dest@test.com").build();
Content content = Content.builder().data("content").build();
Content sub = Content.builder().data("subject").build();
Body body = Body.builder().html(content).build();
Message msg = Message.builder().subject(sub).body(body).build();
SendEmailRequest emailRequest =
SendEmailRequest.builder()
.destination(destination)
.message(msg)
.source("source@test.com")
.build();

ses.sendEmail(emailRequest);

getInstrumentationExtension()
.waitAndAssertTraces(
trace -> {
trace.hasSpansSatisfyingExactly(
span -> {
span.hasKind(SpanKind.CLIENT);
span.hasAttributesSatisfying(
attributes -> {
assertThat(attributes)
.hasEntrySatisfying(
HTTP_URL,
entry -> {
assertThat(entry)
.satisfies(
value -> {
URI uri = URI.create(value);
assertThat(uri.getQuery()).isNull();
});
});
});
});
});
}
}
Loading