Skip to content

Commit 2ed720d

Browse files
committed
reverted
1 parent 936f23b commit 2ed720d

File tree

5 files changed

+401
-0
lines changed

5 files changed

+401
-0
lines changed

awsagentprovider/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ dependencies {
4545
// For Udp emitter
4646
compileOnly("io.opentelemetry:opentelemetry-exporter-otlp-common")
4747

48+
// For OtlpAwsSpanExporter SigV4 Authentication
49+
implementation("software.amazon.awssdk:auth")
50+
implementation("software.amazon.awssdk:http-auth-aws")
51+
4852
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
4953
testImplementation("io.opentelemetry:opentelemetry-sdk-testing")
5054
testImplementation("io.opentelemetry:opentelemetry-extension-aws")

awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsApplicationSignalsCustomizerProvider.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import java.util.Set;
5252
import java.util.logging.Level;
5353
import java.util.logging.Logger;
54+
import java.util.regex.Pattern;
5455

5556
/**
5657
* This customizer performs the following customizations:
@@ -70,6 +71,8 @@
7071
public class AwsApplicationSignalsCustomizerProvider
7172
implements AutoConfigurationCustomizerProvider {
7273
static final String AWS_LAMBDA_FUNCTION_NAME_CONFIG = "AWS_LAMBDA_FUNCTION_NAME";
74+
private static final String XRAY_OTLP_ENDPOINT_PATTERN =
75+
"^https://xray\\.([a-z0-9-]+)\\.amazonaws\\.com/v1/traces$";
7376

7477
private static final Duration DEFAULT_METRIC_EXPORT_INTERVAL = Duration.ofMinutes(1);
7578
private static final Logger logger =
@@ -95,6 +98,9 @@ public class AwsApplicationSignalsCustomizerProvider
9598
private static final String OTEL_JMX_TARGET_SYSTEM_CONFIG = "otel.jmx.target.system";
9699
private static final String OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_CONFIG =
97100
"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT";
101+
private static final String OTEL_EXPORTER_HTTP_PROTOBUF_PROTOCOL = "http/protobuf";
102+
private static final String OTEL_EXPORTER_OTLP_TRACES_PROTOCOL_CONFIG =
103+
"OTEL_EXPORTER_OTLP_TRACES_PROTOCOL";
98104
private static final String AWS_XRAY_DAEMON_ADDRESS_CONFIG = "AWS_XRAY_DAEMON_ADDRESS";
99105
private static final String DEFAULT_UDP_ENDPOINT = "127.0.0.1:2000";
100106
private static final String OTEL_DISABLED_RESOURCE_PROVIDERS_CONFIG =
@@ -106,6 +112,8 @@ public class AwsApplicationSignalsCustomizerProvider
106112
// This is a bit of a magic number, as there is no simple way to tell how many spans can make a
107113
// 64KB batch since spans can vary in size.
108114
private static final int LAMBDA_SPAN_EXPORT_BATCH_SIZE = 10;
115+
private static final boolean isSigV4Enabled =
116+
AwsApplicationSignalsCustomizerProvider.isSigV4Enabled();
109117

110118
public void customize(AutoConfigurationCustomizer autoConfiguration) {
111119
autoConfiguration.addPropertiesCustomizer(this::customizeProperties);
@@ -121,6 +129,37 @@ static boolean isLambdaEnvironment() {
121129
return System.getenv(AWS_LAMBDA_FUNCTION_NAME_CONFIG) != null;
122130
}
123131

132+
static boolean isSigV4Enabled() {
133+
String otlpEndpoint = System.getenv(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_CONFIG);
134+
boolean isXrayOtlpEndpoint =
135+
otlpEndpoint != null
136+
&& Pattern.compile(XRAY_OTLP_ENDPOINT_PATTERN)
137+
.matcher(otlpEndpoint.toLowerCase())
138+
.matches();
139+
140+
if (isXrayOtlpEndpoint) {
141+
logger.log(Level.INFO, "Detected using AWS OTLP XRay Endpoint.");
142+
143+
String otlpTracesProtocol = System.getenv(OTEL_EXPORTER_OTLP_TRACES_PROTOCOL_CONFIG);
144+
145+
if (otlpTracesProtocol == null
146+
|| !otlpTracesProtocol.equals(OTEL_EXPORTER_HTTP_PROTOBUF_PROTOCOL)) {
147+
logger.info(
148+
String.format(
149+
"Improper configuration: Please configure your environment variables and export/set %s=%s",
150+
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL_CONFIG, OTEL_EXPORTER_HTTP_PROTOBUF_PROTOCOL));
151+
return false;
152+
}
153+
154+
logger.info(
155+
String.format(
156+
"Proper configuration detected: Now exporting trace span data to %s", otlpEndpoint));
157+
return true;
158+
}
159+
160+
return false;
161+
}
162+
124163
private boolean isApplicationSignalsEnabled(ConfigProperties configProps) {
125164
return configProps.getBoolean(
126165
APPLICATION_SIGNALS_ENABLED_CONFIG,
@@ -221,6 +260,10 @@ private SdkTracerProviderBuilder customizeTracerProviderBuilder(
221260
return tracerProviderBuilder;
222261
}
223262

263+
if (isSigV4Enabled) {
264+
return tracerProviderBuilder;
265+
}
266+
224267
// Construct meterProvider
225268
MetricExporter metricsExporter =
226269
ApplicationSignalsExporterProvider.INSTANCE.createExporter(configProps);
@@ -287,6 +330,15 @@ private SpanExporter customizeSpanExporter(
287330
}
288331
}
289332

333+
// When running OTLP endpoint for X-Ray backend, use custom exporter for SigV4 authentication.
334+
if (isSigV4Enabled) {
335+
return new OtlpAwsSpanExporter(
336+
(OtlpHttpSpanExporter)
337+
spanExporter, // can cast here since we've checked that the environment variable is
338+
// set to http/protobuf
339+
System.getenv(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_CONFIG));
340+
}
341+
290342
if (isApplicationSignalsEnabled(configProps)) {
291343
return AwsMetricAttributesSpanExporterBuilder.create(
292344
spanExporter, ResourceHolder.getResource())
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
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.javaagent.providers;
17+
18+
import io.opentelemetry.exporter.internal.otlp.traces.TraceRequestMarshaler;
19+
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
20+
import io.opentelemetry.sdk.common.CompletableResultCode;
21+
import io.opentelemetry.sdk.common.export.MemoryMode;
22+
import io.opentelemetry.sdk.trace.data.SpanData;
23+
import io.opentelemetry.sdk.trace.export.SpanExporter;
24+
import java.io.ByteArrayInputStream;
25+
import java.io.ByteArrayOutputStream;
26+
import java.net.URI;
27+
import java.util.ArrayList;
28+
import java.util.Collection;
29+
import java.util.HashMap;
30+
import java.util.List;
31+
import java.util.Map;
32+
import java.util.function.Supplier;
33+
import javax.annotation.concurrent.Immutable;
34+
import org.slf4j.Logger;
35+
import org.slf4j.LoggerFactory;
36+
import software.amazon.awssdk.auth.credentials.AwsCredentials;
37+
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
38+
import software.amazon.awssdk.http.SdkHttpFullRequest;
39+
import software.amazon.awssdk.http.SdkHttpMethod;
40+
import software.amazon.awssdk.http.SdkHttpRequest;
41+
import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner;
42+
import software.amazon.awssdk.http.auth.spi.signer.SignedRequest;
43+
44+
/**
45+
* This exporter extends the functionality of the OtlpHttpSpanExporter to allow spans to be exported
46+
* to the XRay OTLP endpoint https://xray.[AWSRegion].amazonaws.com/v1/traces. Utilizes the AWSSDK
47+
* library to sign and directly inject SigV4 Authentication to the exported request's headers. <a
48+
* href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-OTLPEndpoint.html">...</a>
49+
*/
50+
@Immutable
51+
public class OtlpAwsSpanExporter implements SpanExporter {
52+
private static final String SERVICE_NAME = "xray";
53+
private static final Logger logger = LoggerFactory.getLogger(OtlpAwsSpanExporter.class);
54+
55+
private final SpanExporter parentExporter;
56+
private final String awsRegion;
57+
private final String endpoint;
58+
private Collection<SpanData> spanData;
59+
60+
public OtlpAwsSpanExporter(OtlpHttpSpanExporter parentExporter, String endpoint) {
61+
this.parentExporter =
62+
parentExporter.toBuilder()
63+
.setMemoryMode(MemoryMode.IMMUTABLE_DATA)
64+
.setEndpoint(endpoint)
65+
.setHeaders(new SigV4AuthHeaderSupplier())
66+
.build();
67+
68+
this.awsRegion = endpoint.split("\\.")[1];
69+
this.endpoint = endpoint;
70+
this.spanData = new ArrayList<>();
71+
}
72+
73+
/**
74+
* Overrides the upstream implementation of export. All behaviors are the same except if the
75+
* endpoint is an XRay OTLP endpoint, we will sign the request with SigV4 in headers before
76+
* sending it to the endpoint. Otherwise, we will skip signing.
77+
*/
78+
@Override
79+
public CompletableResultCode export(Collection<SpanData> spans) {
80+
this.spanData = spans;
81+
return this.parentExporter.export(spans);
82+
}
83+
84+
@Override
85+
public CompletableResultCode flush() {
86+
return this.parentExporter.flush();
87+
}
88+
89+
@Override
90+
public CompletableResultCode shutdown() {
91+
return this.parentExporter.shutdown();
92+
}
93+
94+
@Override
95+
public String toString() {
96+
return this.parentExporter.toString();
97+
}
98+
99+
private final class SigV4AuthHeaderSupplier implements Supplier<Map<String, String>> {
100+
101+
@Override
102+
public Map<String, String> get() {
103+
try {
104+
ByteArrayOutputStream encodedSpans = new ByteArrayOutputStream();
105+
TraceRequestMarshaler.create(OtlpAwsSpanExporter.this.spanData).writeBinaryTo(encodedSpans);
106+
107+
SdkHttpRequest httpRequest =
108+
SdkHttpFullRequest.builder()
109+
.uri(URI.create(OtlpAwsSpanExporter.this.endpoint))
110+
.method(SdkHttpMethod.POST)
111+
.putHeader("Content-Type", "application/x-protobuf")
112+
.contentStreamProvider(() -> new ByteArrayInputStream(encodedSpans.toByteArray()))
113+
.build();
114+
115+
AwsCredentials credentials = DefaultCredentialsProvider.create().resolveCredentials();
116+
117+
SignedRequest signedRequest =
118+
AwsV4HttpSigner.create()
119+
.sign(
120+
b ->
121+
b.identity(credentials)
122+
.request(httpRequest)
123+
.putProperty(AwsV4HttpSigner.SERVICE_SIGNING_NAME, SERVICE_NAME)
124+
.putProperty(
125+
AwsV4HttpSigner.REGION_NAME, OtlpAwsSpanExporter.this.awsRegion)
126+
.payload(() -> new ByteArrayInputStream(encodedSpans.toByteArray())));
127+
128+
Map<String, String> result = new HashMap<>();
129+
130+
Map<String, List<String>> headers = signedRequest.request().headers();
131+
headers.forEach(
132+
(key, values) -> {
133+
if (!values.isEmpty()) {
134+
result.put(key, values.get(0));
135+
}
136+
});
137+
138+
return result;
139+
140+
} catch (Exception e) {
141+
logger.error(
142+
"Failed to sign/authenticate the given exported Span request to OTLP CloudWatch endpoint with error: {}",
143+
e.getMessage());
144+
145+
return new HashMap<>();
146+
}
147+
}
148+
}
149+
}

awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/OtlpAwsSpanExporter/test.java

Whitespace-only changes.

0 commit comments

Comments
 (0)