Skip to content

Commit 5255564

Browse files
authored
Added patch for the X-Ray Remote Sampler (#1089)
### Description of changes: Since upstream has updated the HTTP semantic conventions, the X-Ray Remote Sampler has been outdated as it's been looking at the older attributes that are no longer emitted. I've created a patch to temporarily fix this until the change is merged upstream. To create the patch file: 1. Cloned the [opentelemetry-java-contrib](https://github.com/open-telemetry/opentelemetry-java-contrib/) repo 2. Ran `git checkout v1.39.0` to checkout to the same version used by the latest release (2.11.0) 3. Made the changes in the `SamplingRuleApplier.java`, `SamplingRuleApplierTest.java` and the `version.gradle.kts` files 4. Generated the patch file by running `git diff > opentelemetry-java-contrib.patch`. This should be a temporary patch. I'm creating another CR in upstream to fix this issue. Once it's fixed, we will need to update the [io.opentelemetry.contrib:opentelemetry-aws-xray](https://github.com/aws-observability/aws-otel-java-instrumentation/blob/284eed6a7e50237b2a677258bef58e8bf01c71d9/dependencyManagement/build.gradle.kts#L76) version to the latest one. ### Testing: **Testing Setup:** 1. Created a local build of the ADOT Distro by running the following: ``` ./gradlew clean ./scripts/local_patch.sh //applies the patches including the new contrib patch ./gradlew build //this builds everything under the adot repo including the sample apps. ``` 2. Used the [SpringBoot](https://github.com/aws-observability/aws-otel-java-instrumentation/tree/284eed6a7e50237b2a677258bef58e8bf01c71d9/sample-apps/springboot) sample app which includes both the `outgoing-http-call` and the `/aws-sdk-call` APIs 3. Started the AWS Adot Collector in a docker container to retrieve Sampling Rules and to export traces to X-Ray 4. Started the Sample App using the following: ``` JAVA_TOOL_OPTIONS=" -javaagent:/Volumes/workplace/otel/aws-otel-java-instrumentation/otelagent/build/libs/aws-opentelemetry-agent-2.11.0-SNAPSHOT.jar" \ OTEL_METRICS_EXPORTER=none \ OTEL_LOGS_EXPORTER=none \ OTEL_AWS_APPLICATION_SIGNALS_ENABLED=true \ OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT=http://localhost:4316/v1/metrics \ OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \ OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4316/v1/traces \ OTEL_RESOURCE_ATTRIBUTES="service.name=$YOUR_SVC_NAME" \ java -jar /Volumes/workplace/otel/aws-otel-java-instrumentation/sample-apps/springboot/build/libs/springboot-2.11.0-SNAPSHOT.jar ``` **Manual Testing:** For the manual testing. I created three sampling rules 1. Default one with zero sampling 6. Second one with path = `/aws-sdk-call` with 100% sampling 7. Third one with path = `/outgoing-http-call` with 100% sampling Before the change, calling any of the above APIs wasn't being matched when it should. After the change, the rules were being matched and each rule showed the correct stats. **Before the change:** ![Screenshot 2025-06-01 at 11 34 56 PM](https://github.com/user-attachments/assets/6cc69450-8c8f-47e5-b428-65b2a6a0ef0c) **After the change:** ![Screenshot 2025-06-01 at 11 29 54 PM](https://github.com/user-attachments/assets/dd10ba67-5e6a-475a-81f2-d8b6481fa71b) By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 2dad73c commit 5255564

File tree

4 files changed

+255
-1
lines changed

4 files changed

+255
-1
lines changed
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
diff --git a/aws-xray/src/main/java/io/opentelemetry/contrib/awsxray/SamplingRuleApplier.java b/aws-xray/src/main/java/io/opentelemetry/contrib/awsxray/SamplingRuleApplier.java
2+
index 1ef8abf..ef84f35 100644
3+
--- a/aws-xray/src/main/java/io/opentelemetry/contrib/awsxray/SamplingRuleApplier.java
4+
+++ b/aws-xray/src/main/java/io/opentelemetry/contrib/awsxray/SamplingRuleApplier.java
5+
@@ -35,6 +35,11 @@ final class SamplingRuleApplier {
6+
7+
private static final Map<String, String> XRAY_CLOUD_PLATFORM;
8+
9+
+ private static final AttributeKey<String> URL_PATH = AttributeKey.stringKey("url.path");
10+
+ private static final AttributeKey<String> URL_FULL = AttributeKey.stringKey("url.full");
11+
+ private static final AttributeKey<String> HTTP_REQUEST_METHOD =
12+
+ AttributeKey.stringKey("http.request.method");
13+
+
14+
static {
15+
Map<String, String> xrayCloudPlatform = new HashMap<>();
16+
xrayCloudPlatform.put(ResourceAttributes.CloudPlatformValues.AWS_EC2, "AWS::EC2::Instance");
17+
@@ -162,11 +167,14 @@ final class SamplingRuleApplier {
18+
String host = null;
19+
20+
for (Map.Entry<AttributeKey<?>, Object> entry : attributes.asMap().entrySet()) {
21+
- if (entry.getKey().equals(SemanticAttributes.HTTP_TARGET)) {
22+
+ if (entry.getKey().equals(SemanticAttributes.HTTP_TARGET)
23+
+ || entry.getKey().equals(URL_PATH)) {
24+
httpTarget = (String) entry.getValue();
25+
- } else if (entry.getKey().equals(SemanticAttributes.HTTP_URL)) {
26+
+ } else if (entry.getKey().equals(SemanticAttributes.HTTP_URL)
27+
+ || entry.getKey().equals(URL_FULL)) {
28+
httpUrl = (String) entry.getValue();
29+
- } else if (entry.getKey().equals(SemanticAttributes.HTTP_METHOD)) {
30+
+ } else if (entry.getKey().equals(SemanticAttributes.HTTP_METHOD)
31+
+ || entry.getKey().equals(HTTP_REQUEST_METHOD)) {
32+
httpMethod = (String) entry.getValue();
33+
} else if (entry.getKey().equals(SemanticAttributes.NET_HOST_NAME)) {
34+
host = (String) entry.getValue();
35+
diff --git a/aws-xray/src/test/java/io/opentelemetry/contrib/awsxray/SamplingRuleApplierTest.java b/aws-xray/src/test/java/io/opentelemetry/contrib/awsxray/SamplingRuleApplierTest.java
36+
index 6bb6e82..55dabbd 100644
37+
--- a/aws-xray/src/test/java/io/opentelemetry/contrib/awsxray/SamplingRuleApplierTest.java
38+
+++ b/aws-xray/src/test/java/io/opentelemetry/contrib/awsxray/SamplingRuleApplierTest.java
39+
@@ -42,6 +42,11 @@ class SamplingRuleApplierTest {
40+
41+
private static final String CLIENT_ID = "test-client-id";
42+
43+
+ private static final AttributeKey<String> URL_PATH = AttributeKey.stringKey("url.path");
44+
+ private static final AttributeKey<String> URL_FULL = AttributeKey.stringKey("url.full");
45+
+ private static final AttributeKey<String> HTTP_REQUEST_METHOD =
46+
+ AttributeKey.stringKey("http.request.method");
47+
+
48+
@Nested
49+
@SuppressWarnings("ClassCanBeStatic")
50+
class ExactMatch {
51+
@@ -68,6 +73,15 @@ class SamplingRuleApplierTest {
52+
.put(AttributeKey.longKey("speed"), 10)
53+
.build();
54+
55+
+ private final Attributes newSemCovAttributes =
56+
+ Attributes.builder()
57+
+ .put(HTTP_REQUEST_METHOD, "GET")
58+
+ .put(SemanticAttributes.NET_HOST_NAME, "opentelemetry.io")
59+
+ .put(URL_PATH, "/instrument-me")
60+
+ .put(AttributeKey.stringKey("animal"), "cat")
61+
+ .put(AttributeKey.longKey("speed"), 10)
62+
+ .build();
63+
+
64+
// FixedRate set to 1.0 in rule and no reservoir
65+
@Test
66+
void fixedRateAlwaysSample() {
67+
@@ -116,6 +130,21 @@ class SamplingRuleApplierTest {
68+
.isTrue();
69+
}
70+
71+
+ @Test
72+
+ void matchesURLFullNewSemCov() {
73+
+ assertThat(applier.matches(newSemCovAttributes, resource)).isTrue();
74+
+
75+
+ // http.url works too
76+
+ assertThat(
77+
+ applier.matches(
78+
+ attributes.toBuilder()
79+
+ .remove(URL_FULL)
80+
+ .put(URL_FULL, "scheme://host:port/instrument-me")
81+
+ .build(),
82+
+ resource))
83+
+ .isTrue();
84+
+ }
85+
+
86+
@Test
87+
void serviceNameNotMatch() {
88+
assertThat(
89+
@@ -137,6 +166,13 @@ class SamplingRuleApplierTest {
90+
assertThat(applier.matches(attributes, resource)).isFalse();
91+
}
92+
93+
+ @Test
94+
+ void methodNewSemCovNotMatch() {
95+
+ Attributes attributes =
96+
+ this.newSemCovAttributes.toBuilder().put(HTTP_REQUEST_METHOD, "POST").build();
97+
+ assertThat(applier.matches(attributes, resource)).isFalse();
98+
+ }
99+
+
100+
@Test
101+
void hostNotMatch() {
102+
// Replacing dot with character makes sure we're not accidentally treating dot as regex
103+
@@ -178,6 +214,34 @@ class SamplingRuleApplierTest {
104+
assertThat(applier.matches(attributes, resource)).isFalse();
105+
}
106+
107+
+ @Test
108+
+ void pathNewSemCovNotMatch() {
109+
+ Attributes attributes =
110+
+ this.newSemCovAttributes.toBuilder().put(URL_PATH, "/instrument-you").build();
111+
+ assertThat(applier.matches(attributes, resource)).isFalse();
112+
+ attributes =
113+
+ this.newSemCovAttributes.toBuilder()
114+
+ .remove(URL_PATH)
115+
+ .put(URL_FULL, "scheme://host:port/instrument-you")
116+
+ .build();
117+
+ assertThat(applier.matches(attributes, resource)).isFalse();
118+
+ attributes =
119+
+ this.newSemCovAttributes.toBuilder()
120+
+ .remove(URL_PATH)
121+
+ .put(URL_FULL, "scheme://host:port")
122+
+ .build();
123+
+ assertThat(applier.matches(attributes, resource)).isFalse();
124+
+
125+
+ // Correct path, but we ignore anyways since the URL is malformed per spec, scheme is always
126+
+ // present.
127+
+ attributes =
128+
+ this.newSemCovAttributes.toBuilder()
129+
+ .remove(URL_PATH)
130+
+ .put(URL_FULL, "host:port/instrument-me")
131+
+ .build();
132+
+ assertThat(applier.matches(attributes, resource)).isFalse();
133+
+ }
134+
+
135+
@Test
136+
void attributeNotMatch() {
137+
Attributes attributes =
138+
@@ -243,6 +307,15 @@ class SamplingRuleApplierTest {
139+
.put(AttributeKey.longKey("speed"), 10)
140+
.build();
141+
142+
+ private final Attributes newSemCovAttributes =
143+
+ Attributes.builder()
144+
+ .put(HTTP_REQUEST_METHOD, "GET")
145+
+ .put(SemanticAttributes.NET_HOST_NAME, "opentelemetry.io")
146+
+ .put(URL_PATH, "/instrument-me?foo=bar&cat=meow")
147+
+ .put(AttributeKey.stringKey("animal"), "cat")
148+
+ .put(AttributeKey.longKey("speed"), 10)
149+
+ .build();
150+
+
151+
// FixedRate set to 0.0 in rule and no reservoir
152+
@Test
153+
void fixedRateNeverSample() {
154+
@@ -329,6 +402,26 @@ class SamplingRuleApplierTest {
155+
assertThat(applier.matches(attributes, resource)).isFalse();
156+
}
157+
158+
+ @Test
159+
+ void newSemCovMethodMatches() {
160+
+ Attributes attributes =
161+
+ this.newSemCovAttributes.toBuilder().put(HTTP_REQUEST_METHOD, "BADGETGOOD").build();
162+
+ assertThat(applier.matches(attributes, resource)).isTrue();
163+
+ attributes = newSemCovAttributes.toBuilder().put(HTTP_REQUEST_METHOD, "BADGET").build();
164+
+ assertThat(applier.matches(attributes, resource)).isTrue();
165+
+ attributes = newSemCovAttributes.toBuilder().put(HTTP_REQUEST_METHOD, "GETGET").build();
166+
+ assertThat(applier.matches(attributes, resource)).isTrue();
167+
+ }
168+
+
169+
+ @Test
170+
+ void newSemCovMethodNotMatch() {
171+
+ Attributes attributes =
172+
+ newSemCovAttributes.toBuilder().put(HTTP_REQUEST_METHOD, "POST").build();
173+
+ assertThat(applier.matches(attributes, resource)).isFalse();
174+
+ attributes = removeAttribute(newSemCovAttributes, HTTP_REQUEST_METHOD);
175+
+ assertThat(applier.matches(attributes, resource)).isFalse();
176+
+ }
177+
+
178+
@Test
179+
void hostMatches() {
180+
Attributes attributes =
181+
@@ -410,6 +503,29 @@ class SamplingRuleApplierTest {
182+
assertThat(applier.matches(attributes, resource)).isFalse();
183+
}
184+
185+
+ @Test
186+
+ void pathNewSemCovMatches() {
187+
+ Attributes attributes =
188+
+ newSemCovAttributes.toBuilder().put(URL_PATH, "/instrument-me?foo=bar&cat=").build();
189+
+ assertThat(applier.matches(attributes, resource)).isTrue();
190+
+ // Deceptive question mark, it's actually a wildcard :-)
191+
+ attributes =
192+
+ newSemCovAttributes.toBuilder().put(URL_PATH, "/instrument-meafoo=bar&cat=").build();
193+
+ assertThat(applier.matches(attributes, resource)).isTrue();
194+
+ }
195+
+
196+
+ @Test
197+
+ void pathNewSemCovNotMatch() {
198+
+ Attributes attributes =
199+
+ newSemCovAttributes.toBuilder().put(URL_PATH, "/instrument-mea?foo=bar&cat=").build();
200+
+ assertThat(applier.matches(attributes, resource)).isFalse();
201+
+ attributes =
202+
+ newSemCovAttributes.toBuilder().put(URL_PATH, "foo/instrument-meafoo=bar&cat=").build();
203+
+ assertThat(applier.matches(attributes, resource)).isFalse();
204+
+ attributes = removeAttribute(newSemCovAttributes, URL_PATH);
205+
+ assertThat(applier.matches(attributes, resource)).isFalse();
206+
+ }
207+
+
208+
@Test
209+
void attributeMatches() {
210+
Attributes attributes =
211+
diff --git a/disk-buffering/build.gradle.kts b/disk-buffering/build.gradle.kts
212+
index 041d2e9..e3d60f4 100644
213+
--- a/disk-buffering/build.gradle.kts
214+
+++ b/disk-buffering/build.gradle.kts
215+
@@ -70,6 +70,10 @@ tasks.named<ShadowJar>("shadowJar") {
216+
mustRunAfter("jar")
217+
}
218+
219+
+tasks.withType<Test>().configureEach {
220+
+ dependsOn("shadowJar")
221+
+}
222+
+
223+
// The javadoc from wire's generated classes has errors that make the task that generates the "javadoc" artifact to fail. This
224+
// makes the javadoc task to ignore those generated classes.
225+
tasks.withType(Javadoc::class.java) {
226+
diff --git a/version.gradle.kts b/version.gradle.kts
227+
index acefcee..329b524 100644
228+
--- a/version.gradle.kts
229+
+++ b/version.gradle.kts
230+
@@ -1,5 +1,5 @@
231+
-val stableVersion = "1.39.0"
232+
-val alphaVersion = "1.39.0-alpha"
233+
+val stableVersion = "1.39.0-adot1"
234+
+val alphaVersion = "1.39.0-alpha-adot1"
235+
236+
allprojects {
237+
if (findProperty("otel.stable") != "true") {

.github/patches/versions

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
OTEL_JAVA_INSTRUMENTATION_VERSION=v2.11.0
2+
OTEL_JAVA_CONTRIB_VERSION=v1.39.0

dependencyManagement/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ val dependencyLists = listOf(
7373
"commons-logging:commons-logging:1.2",
7474
"com.sparkjava:spark-core:2.9.4",
7575
"com.squareup.okhttp3:okhttp:4.12.0",
76-
"io.opentelemetry.contrib:opentelemetry-aws-xray:1.39.0",
76+
"io.opentelemetry.contrib:opentelemetry-aws-xray:1.39.0-adot1",
7777
"io.opentelemetry.contrib:opentelemetry-aws-resources:1.39.0-alpha",
7878
"io.opentelemetry.proto:opentelemetry-proto:1.0.0-alpha",
7979
"io.opentelemetry.javaagent:opentelemetry-javaagent:$otelJavaAgentVersion",

lambda-layer/build-layer.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,22 @@ patch -p1 < "$SOURCEDIR"/patches/StreamHandlerInstrumentation.patch
3434
popd
3535
rm -rf opentelemetry-java-instrumentation
3636

37+
contrib_version=$(awk -F'=v' '/OTEL_JAVA_CONTRIB_VERSION/ {print $2}' "$file")
38+
if [[ -n "$contrib_version" ]]; then
39+
echo "Found OTEL Contrib Version: ${contrib_version}"
40+
## Clone and Patch the OpenTelemetry Java contrib Repository
41+
echo "Info: Cloning and Patching OpenTelemetry Java contrib Repository"
42+
git clone https://github.com/open-telemetry/opentelemetry-java-contrib.git
43+
pushd opentelemetry-java-contrib
44+
git checkout v${contrib_version} -b tag-v${contrib_version}
45+
46+
# There is another patch in the .github/patches directory for other changes. We should apply them too for consistency.
47+
patch -p1 < "$SOURCEDIR"/../.github/patches/opentelemetry-java-contrib.patch
48+
49+
./gradlew publishToMavenLocal
50+
popd
51+
rm -rf opentelemetry-java-contrib
52+
fi
3753

3854
## Build the ADOT Java from current source
3955
echo "Info: Building ADOT Java from current source"

0 commit comments

Comments
 (0)