Skip to content

Commit 0ee6e66

Browse files
Jeel Mehtayiyuan-he
authored andcommitted
Contribute OTLP UDP Exporter
1 parent d8ce8c0 commit 0ee6e66

File tree

11 files changed

+791
-0
lines changed

11 files changed

+791
-0
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
name: Release ADOT OTLP UDP Exporter
2+
3+
on:
4+
push:
5+
branches:
6+
- "yiyuanh-udp-*"
7+
workflow_dispatch:
8+
inputs:
9+
version:
10+
description: 'Version number for deployment e.g. 0.0.1'
11+
required: true
12+
type: string
13+
14+
jobs:
15+
build-test-publish:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v3
19+
20+
- name: Set up Java
21+
uses: actions/setup-java@v3
22+
with:
23+
java-version: '17' # or your preferred version
24+
distribution: 'temurin'
25+
cache: 'gradle' # Add gradle caching
26+
27+
- name: Configure Git
28+
run: |
29+
git config --global user.email "you@example.com"
30+
git config --global user.name "Your Name"
31+
32+
- name: Run Local Patch
33+
run: |
34+
chmod +x ./scripts/local_patch.sh
35+
./scripts/local_patch.sh
36+
37+
- name: Grant execute permission for gradlew
38+
run: chmod +x gradlew
39+
40+
- name: Build UDP exporter
41+
run: |
42+
./gradlew -p ./exporters/aws-otel-otlp-udp-exporter -x test
43+
44+
- name: Download and run X-Ray Daemon
45+
run: |
46+
mkdir xray-daemon
47+
cd xray-daemon
48+
wget https://s3.us-west-2.amazonaws.com/aws-xray-assets.us-west-2/xray-daemon/aws-xray-daemon-linux-3.x.zip
49+
unzip aws-xray-daemon-linux-3.x.zip
50+
./xray -o -n us-west-2 -f ./daemon-logs.log --log-level debug &
51+
52+
- name: Install UDP Exporter
53+
run: |
54+
./gradlew -p ./exporters/aws-otel-otlp-udp-exporter publishMavenJavaPublicationToMavenLocal -Psigning.skip=true -x sign
55+
56+
- name: Ensure Unit Tests are passing
57+
working-directory: exporters/aws-otel-otlp-udp-exporter
58+
run: |
59+
../../gradlew test
60+
61+
- name: Run Sample App in Background
62+
working-directory: sample-apps/integ-test-app
63+
run: |
64+
../../gradlew :sample-apps:integ-test-app:build
65+
java -jar build/libs/integ-test-app.jar &
66+
sleep 5
67+
68+
- name: Call Sample App Endpoint
69+
run: |
70+
echo "traceId=$(curl localhost:8080/test)" >> $GITHUB_OUTPUT
71+
72+
- name: Verify X-Ray daemon received traces
73+
run: |
74+
sleep 10
75+
echo "X-Ray daemon logs:"
76+
cat xray-daemon/daemon-logs.log
77+
78+
# Check if the daemon received and processed some data
79+
if grep -q "sending.*batch" xray-daemon/daemon-logs.log; then
80+
echo "✅ X-Ray daemon processed trace data (AWS upload errors are expected)"
81+
exit 0
82+
elif grep -q "processor:.*segment" xray-daemon/daemon-logs.log; then
83+
echo "✅ X-Ray daemon processed segment data (AWS upload errors are expected)"
84+
exit 0
85+
else
86+
echo "❌ No evidence of traces being received by X-Ray daemon"
87+
exit 1
88+
fi
89+
90+
# TODO: Steps to publish to Maven Central
91+
# - name: Publish to Maven Central
92+
# env:
93+
# MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}
94+
# MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }}
95+
# working-directory: exporters/aws-otel-otlp-udp-exporter
96+
# run: ../../gradlew publish
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
plugins {
17+
id("java")
18+
id("java-library")
19+
id("maven-publish")
20+
}
21+
22+
group = "software.amazon.opentelemetry.exporters.otlp.udp.trace"
23+
version = "0.0.1"
24+
25+
dependencies {
26+
implementation(platform("io.opentelemetry:opentelemetry-bom:1.33.0"))
27+
implementation("io.opentelemetry:opentelemetry-api")
28+
implementation("io.opentelemetry:opentelemetry-sdk")
29+
implementation("io.opentelemetry:opentelemetry-exporter-otlp-common")
30+
implementation("io.opentelemetry.proto:opentelemetry-proto:1.0.0-alpha")
31+
compileOnly("com.google.code.findbugs:jsr305:3.0.2")
32+
testImplementation(platform("org.junit:junit-bom:5.9.2"))
33+
testImplementation("org.junit.jupiter:junit-jupiter-api")
34+
testImplementation("org.junit.jupiter:junit-jupiter-engine")
35+
testImplementation("org.mockito:mockito-core:5.3.1")
36+
testImplementation("org.assertj:assertj-core:3.24.2")
37+
testImplementation("org.mockito:mockito-junit-jupiter:5.3.1")
38+
}
39+
40+
java {
41+
withSourcesJar()
42+
withJavadocJar()
43+
}
44+
45+
tasks.javadoc {
46+
options {
47+
(this as CoreJavadocOptions).addStringOption("Xdoclint:none", "-quiet")
48+
}
49+
isFailOnError = false
50+
}
51+
52+
sourceSets {
53+
main {
54+
java {
55+
srcDirs("src/main/java")
56+
}
57+
}
58+
test {
59+
java {
60+
srcDirs("src/test/java")
61+
}
62+
}
63+
}
64+
65+
tasks.test {
66+
useJUnitPlatform()
67+
testLogging {
68+
events("passed", "skipped", "failed")
69+
}
70+
}
71+
72+
tasks.jar {
73+
manifest {
74+
attributes(
75+
"Implementation-Title" to project.name,
76+
"Implementation-Version" to project.version,
77+
)
78+
}
79+
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
80+
}
81+
82+
tasks.named<Jar>("javadocJar") {
83+
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
84+
}
85+
86+
tasks.named<Jar>("sourcesJar") {
87+
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
88+
}
89+
90+
publishing {
91+
publications {
92+
create<MavenPublication>("mavenJava") {
93+
from(components["java"])
94+
groupId = project.group.toString()
95+
artifactId = "aws-otel-otlp-udp-exporter"
96+
version = project.version.toString()
97+
}
98+
}
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.opentelemetry.exporters.otlp.udp.trace;
17+
18+
import io.opentelemetry.exporter.internal.otlp.traces.TraceRequestMarshaler;
19+
import io.opentelemetry.sdk.common.CompletableResultCode;
20+
import io.opentelemetry.sdk.trace.data.SpanData;
21+
import io.opentelemetry.sdk.trace.export.SpanExporter;
22+
import java.io.ByteArrayOutputStream;
23+
import java.nio.charset.StandardCharsets;
24+
import java.util.Base64;
25+
import java.util.Collection;
26+
import java.util.concurrent.atomic.AtomicBoolean;
27+
import java.util.logging.Level;
28+
import java.util.logging.Logger;
29+
import javax.annotation.concurrent.Immutable;
30+
31+
/**
32+
* Exports spans via UDP, using OpenTelemetry's protobuf model. The protobuf modelled spans are
33+
* Base64 encoded and prefixed with AWS X-Ray specific information before being sent over to {@link
34+
* UdpSender}.
35+
*
36+
* <p>This exporter is NOT meant for generic use since the payload is prefixed with AWS X-Ray
37+
* specific information.
38+
*/
39+
@Immutable
40+
public class OtlpUdpSpanExporter implements SpanExporter {
41+
42+
private static final Logger logger = Logger.getLogger(OtlpUdpSpanExporter.class.getName());
43+
44+
private final AtomicBoolean isShutdown = new AtomicBoolean();
45+
46+
private final UdpSender sender;
47+
private final String payloadPrefix;
48+
49+
OtlpUdpSpanExporter(UdpSender sender, String payloadPrefix) {
50+
this.sender = sender;
51+
this.payloadPrefix = payloadPrefix;
52+
}
53+
54+
@Override
55+
public CompletableResultCode export(Collection<SpanData> spans) {
56+
if (isShutdown.get()) {
57+
return CompletableResultCode.ofFailure();
58+
}
59+
60+
TraceRequestMarshaler exportRequest = TraceRequestMarshaler.create(spans);
61+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
62+
try {
63+
exportRequest.writeBinaryTo(baos);
64+
String payload = payloadPrefix + Base64.getEncoder().encodeToString(baos.toByteArray());
65+
sender.send(payload.getBytes(StandardCharsets.UTF_8));
66+
return CompletableResultCode.ofSuccess();
67+
} catch (Exception e) {
68+
logger.log(Level.SEVERE, "Failed to export spans. Error: " + e.getMessage(), e);
69+
return CompletableResultCode.ofFailure();
70+
}
71+
}
72+
73+
@Override
74+
public CompletableResultCode flush() {
75+
// TODO: implement
76+
return CompletableResultCode.ofSuccess();
77+
}
78+
79+
@Override
80+
public CompletableResultCode shutdown() {
81+
if (!isShutdown.compareAndSet(false, true)) {
82+
logger.log(Level.INFO, "Calling shutdown() multiple times.");
83+
return CompletableResultCode.ofSuccess();
84+
}
85+
return sender.shutdown();
86+
}
87+
88+
// Visible for testing
89+
UdpSender getSender() {
90+
return sender;
91+
}
92+
93+
// Visible for testing
94+
String getPayloadPrefix() {
95+
return payloadPrefix;
96+
}
97+
}

0 commit comments

Comments
 (0)