Skip to content

Commit b3e2c27

Browse files
authored
AWS SDK v2 interceptor (#112)
* Skeleton * Losing the application span scope somehow... * Still not working * Small bug * Fix reference problem * Put in a builder for client options * Start adding tests * Fix one more test * Add license headers before creating PR * Don't need retro-lambda because this is a Java 8 artifact * Fixing a test and simplifying logic * I'm surprised that worked at all on the first version * Fixing more tests, need to backport some of this * Better * Changing the model a little bit * Remove unneeded dep, use async * Review comments and effective java changes * Change comment to better reflect current impl * Add scoping when necessary * Add readme * Rename type to be more obvious * I like the method to build the interceptor instead of the configuration. Updated docs to reflect this change * One more readme update
1 parent 03b2980 commit b3e2c27

File tree

9 files changed

+799
-1
lines changed

9 files changed

+799
-1
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# AWS SDK V2 Instrumentation
2+
3+
This module contains instrumentation for [AWS](https://github.com/aws/aws-sdk-java-v2) clients that
4+
extend `SdkClient`.
5+
6+
The `TracingClientOverrideConfiguration` class finalizes your `ClientOverrideConfiguration`
7+
instances by adding a `ExecutionInterceptor` instance to your client configuration.
8+
9+
## Span Model
10+
11+
Traces AWS Java SDK calls. Adds on the standard zipkin/brave http tags, as well as tags that align
12+
with the XRay data model.
13+
14+
This implementation creates 2 types of spans to allow for better error visibility.
15+
16+
The outer span, "Application Span", wraps the whole SDK operation. This span uses `aws-sdk` as it's
17+
name and will NOT have a remoteService configuration, making it a local span. If the entire
18+
operation results in an error then this span will have an error tag with the cause.
19+
20+
The inner span, "Client Span", is created for each outgoing HTTP request. This span will be of type
21+
CLIENT. The remoteService will be the name of the AWS service, and the span name will be the name of
22+
the operation being done. If the request results in an error then the span will be tagged with the
23+
error. The AWS request ID is added when available.
24+
25+
## Wiring it up
26+
27+
```java
28+
// Set up brave
29+
Tracing tracing = Tracing.currentTracer();
30+
HttpTracing httpTracing = HttpTracing.create(tracing);
31+
AwsSdkTracing awsSdkTracing = AwsSdkTracing.create(httpTracing);
32+
33+
// Create your client
34+
ClientOverrideConfiguration configuration = ClientOverrideConfiguration.builder()
35+
// Any other options you'd like to set
36+
.addExecutionInterceptor(awsSdkTracing.executionInterceptor())
37+
.build();
38+
DynamoDbAsyncClient client = DynamoDbAsyncClient.builder()
39+
.overrideConfiguration(configuration)
40+
.build();
41+
42+
// Now use you client like usual
43+
```
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Copyright 2016-2018 The OpenZipkin Authors
5+
6+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7+
in compliance with the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software distributed under the License
12+
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13+
or implied. See the License for the specific language governing permissions and limitations under
14+
the License.
15+
16+
-->
17+
<project xmlns="http://maven.apache.org/POM/4.0.0"
18+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
19+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
20+
<modelVersion>4.0.0</modelVersion>
21+
22+
<parent>
23+
<artifactId>brave-instrumentation-parent</artifactId>
24+
<groupId>io.zipkin.aws</groupId>
25+
<version>0.14.1-SNAPSHOT</version>
26+
</parent>
27+
28+
<artifactId>brave-instrumentation-aws-java-sdk-v2-core</artifactId>
29+
30+
<properties>
31+
<!-- SDK V2 requires Java 8 -->
32+
<main.java.version>1.8</main.java.version>
33+
<main.signature.artifact>java18</main.signature.artifact>
34+
35+
<main.basedir>${project.basedir}/../..</main.basedir>
36+
</properties>
37+
38+
<dependencies>
39+
<dependency>
40+
<groupId>io.zipkin.brave</groupId>
41+
<artifactId>brave-instrumentation-http</artifactId>
42+
</dependency>
43+
<dependency>
44+
<groupId>software.amazon.awssdk</groupId>
45+
<artifactId>sdk-core</artifactId>
46+
<version>${aws-java-sdk-v2.version}</version>
47+
<scope>provided</scope>
48+
</dependency>
49+
50+
<dependency>
51+
<groupId>io.zipkin.brave</groupId>
52+
<artifactId>brave-instrumentation-http-tests</artifactId>
53+
<scope>test</scope>
54+
</dependency>
55+
<dependency>
56+
<groupId>com.squareup.okhttp3</groupId>
57+
<artifactId>mockwebserver</artifactId>
58+
<scope>test</scope>
59+
</dependency>
60+
<dependency>
61+
<groupId>io.zipkin.brave</groupId>
62+
<artifactId>brave-context-log4j2</artifactId>
63+
<scope>test</scope>
64+
</dependency>
65+
<dependency>
66+
<groupId>org.apache.logging.log4j</groupId>
67+
<artifactId>log4j-core</artifactId>
68+
<version>${log4j.version}</version>
69+
<scope>test</scope>
70+
</dependency>
71+
<dependency>
72+
<groupId>software.amazon.awssdk</groupId>
73+
<artifactId>dynamodb</artifactId>
74+
<version>${aws-java-sdk-v2.version}</version>
75+
<scope>test</scope>
76+
</dependency>
77+
<dependency>
78+
<groupId>software.amazon.awssdk</groupId>
79+
<artifactId>netty-nio-client</artifactId>
80+
<version>${aws-java-sdk-v2.version}</version>
81+
<scope>test</scope>
82+
</dependency>
83+
</dependencies>
84+
85+
<build>
86+
<plugins>
87+
<plugin>
88+
<groupId>net.orfjackal.retrolambda</groupId>
89+
<artifactId>retrolambda-maven-plugin</artifactId>
90+
<version>2.5.5</version>
91+
<executions>
92+
<execution>
93+
<phase>none</phase>
94+
</execution>
95+
</executions>
96+
</plugin>
97+
</plugins>
98+
</build>
99+
</project>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2016-2018 The OpenZipkin Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package brave.instrumentation.awsv2;
15+
16+
import brave.http.HttpTracing;
17+
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
18+
19+
public final class AwsSdkTracing {
20+
public static AwsSdkTracing create(HttpTracing httpTracing) {
21+
return new AwsSdkTracing(httpTracing);
22+
}
23+
24+
final HttpTracing httpTracing;
25+
26+
AwsSdkTracing(HttpTracing httpTracing) {
27+
if (httpTracing == null) throw new NullPointerException("httpTracing == null");
28+
this.httpTracing = httpTracing;
29+
}
30+
31+
public ExecutionInterceptor executionInterceptor() {
32+
return new TracingExecutionInterceptor(httpTracing);
33+
}
34+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright 2016-2018 The OpenZipkin Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package brave.instrumentation.awsv2;
15+
16+
import brave.http.HttpClientAdapter;
17+
import java.util.Iterator;
18+
import java.util.List;
19+
import java.util.Map;
20+
import software.amazon.awssdk.http.SdkHttpRequest;
21+
import software.amazon.awssdk.http.SdkHttpResponse;
22+
23+
class HttpAdapter extends HttpClientAdapter<SdkHttpRequest.Builder, SdkHttpResponse> {
24+
@Override public String method(SdkHttpRequest.Builder builder) {
25+
return builder.method().name();
26+
}
27+
28+
@Override public String path(SdkHttpRequest.Builder request) {
29+
return request.encodedPath();
30+
}
31+
32+
@Override public String url(SdkHttpRequest.Builder request) {
33+
StringBuilder url = new StringBuilder(request.protocol())
34+
.append("://")
35+
.append(request.host())
36+
.append(":")
37+
.append(request.port());
38+
if (request.encodedPath() != null) url.append(request.encodedPath());
39+
if (request.rawQueryParameters().isEmpty()) return url.toString();
40+
url.append('?');
41+
Iterator<Map.Entry<String, List<String>>> entries = request.rawQueryParameters().entrySet().iterator();
42+
while (entries.hasNext()) {
43+
Map.Entry<String, List<String>> entry = entries.next();
44+
url.append(entry.getKey());
45+
if (entry.getKey().isEmpty()) continue;
46+
url.append('=').append(entry.getValue().get(0)); // skip the others.
47+
if (entries.hasNext()) url.append('&');
48+
}
49+
return url.toString();
50+
}
51+
52+
@Override public String requestHeader(SdkHttpRequest.Builder builder, String s) {
53+
return builder.headers().get(s).get(0);
54+
}
55+
56+
@Override public Integer statusCode(SdkHttpResponse sdkHttpResponse) {
57+
return sdkHttpResponse.statusCode();
58+
}
59+
}

0 commit comments

Comments
 (0)