Skip to content

Commit bed02d5

Browse files
jhallidayjack-berg
andauthored
Add gRPC export for profiles signal type. (#7301)
Co-authored-by: Jack Berg <[email protected]>
1 parent 394cd35 commit bed02d5

File tree

16 files changed

+878
-4
lines changed

16 files changed

+878
-4
lines changed

exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterUtil.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,19 @@ public final class GrpcExporterUtil {
2828
public static final int GRPC_STATUS_DATA_LOSS = 15;
2929

3030
static void logUnimplemented(Logger logger, String type, @Nullable String fullErrorMessage) {
31+
32+
// hopefully temporary special handling for profile signal as it evolves towards stability.
33+
if ("profile".equals(type)) {
34+
logger.log(
35+
Level.SEVERE,
36+
"Failed to export profile. The profile signal type is still under development "
37+
+ "and the endpoint you are connecting to may not support it yet, "
38+
+ "or may support a different version. "
39+
+ "Full error message: "
40+
+ fullErrorMessage);
41+
return;
42+
}
43+
3144
String envVar;
3245
switch (type) {
3346
case "span":

exporters/otlp/profiles/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,16 @@ dependencies {
1515
api(project(":exporters:common"))
1616
implementation(project(":exporters:otlp:common"))
1717

18+
implementation(project(":exporters:otlp:all"))
19+
compileOnly("io.grpc:grpc-stub")
20+
1821
annotationProcessor("com.google.auto.value:auto-value")
1922

2023
testCompileOnly("com.google.guava:guava")
2124
testImplementation("com.fasterxml.jackson.core:jackson-databind")
2225
testImplementation("com.google.protobuf:protobuf-java-util")
2326
testImplementation("io.opentelemetry.proto:opentelemetry-proto")
27+
testImplementation(project(":exporters:otlp:testing-internal"))
28+
testImplementation(project(":exporters:sender:okhttp"))
29+
testImplementation("io.grpc:grpc-stub")
2430
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.exporter.otlp.profiles;
7+
8+
// A Java object to correspond to the gRPC response for the ProfilesService.Export method. If fields
9+
// are added to the type in the future, this can be converted to an actual class.
10+
//
11+
// It may seem like Void could be used instead but gRPC does not allow response values to be
12+
// null.
13+
enum ExportProfilesServiceResponse {
14+
INSTANCE;
15+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.exporter.otlp.profiles;
7+
8+
import static io.grpc.MethodDescriptor.generateFullMethodName;
9+
10+
import com.google.common.util.concurrent.ListenableFuture;
11+
import io.grpc.CallOptions;
12+
import io.grpc.Channel;
13+
import io.grpc.MethodDescriptor;
14+
import io.grpc.stub.ClientCalls;
15+
import io.opentelemetry.exporter.internal.grpc.MarshalerInputStream;
16+
import io.opentelemetry.exporter.internal.grpc.MarshalerServiceStub;
17+
import io.opentelemetry.exporter.internal.marshal.Marshaler;
18+
import java.io.InputStream;
19+
import javax.annotation.Nullable;
20+
21+
// Adapted from the protoc generated code for ProfilesServiceGrpc.
22+
final class MarshalerProfilesServiceGrpc {
23+
24+
private static final String SERVICE_NAME =
25+
"opentelemetry.proto.collector.profiles.v1development.ProfilesService";
26+
27+
private static final MethodDescriptor.Marshaller<Marshaler> REQUEST_MARSHALLER =
28+
new MethodDescriptor.Marshaller<Marshaler>() {
29+
@Override
30+
public InputStream stream(Marshaler value) {
31+
return new MarshalerInputStream(value);
32+
}
33+
34+
@Override
35+
public Marshaler parse(InputStream stream) {
36+
throw new UnsupportedOperationException("Only for serializing");
37+
}
38+
};
39+
40+
private static final MethodDescriptor.Marshaller<ExportProfilesServiceResponse>
41+
RESPONSE_MARSHALER =
42+
new MethodDescriptor.Marshaller<ExportProfilesServiceResponse>() {
43+
@Override
44+
public InputStream stream(ExportProfilesServiceResponse value) {
45+
throw new UnsupportedOperationException("Only for parsing");
46+
}
47+
48+
@Override
49+
public ExportProfilesServiceResponse parse(InputStream stream) {
50+
return ExportProfilesServiceResponse.INSTANCE;
51+
}
52+
};
53+
54+
private static final MethodDescriptor<Marshaler, ExportProfilesServiceResponse> getExportMethod =
55+
MethodDescriptor.<Marshaler, ExportProfilesServiceResponse>newBuilder()
56+
.setType(MethodDescriptor.MethodType.UNARY)
57+
.setFullMethodName(generateFullMethodName(SERVICE_NAME, "Export"))
58+
.setRequestMarshaller(REQUEST_MARSHALLER)
59+
.setResponseMarshaller(RESPONSE_MARSHALER)
60+
.build();
61+
62+
static ProfilesServiceFutureStub newFutureStub(
63+
Channel channel, @Nullable String authorityOverride) {
64+
return ProfilesServiceFutureStub.newStub(
65+
(c, options) -> new ProfilesServiceFutureStub(c, options.withAuthority(authorityOverride)),
66+
channel);
67+
}
68+
69+
static final class ProfilesServiceFutureStub
70+
extends MarshalerServiceStub<
71+
Marshaler, ExportProfilesServiceResponse, ProfilesServiceFutureStub> {
72+
private ProfilesServiceFutureStub(Channel channel, CallOptions callOptions) {
73+
super(channel, callOptions);
74+
}
75+
76+
@Override
77+
protected MarshalerProfilesServiceGrpc.ProfilesServiceFutureStub build(
78+
Channel channel, CallOptions callOptions) {
79+
return new MarshalerProfilesServiceGrpc.ProfilesServiceFutureStub(channel, callOptions);
80+
}
81+
82+
@Override
83+
public ListenableFuture<ExportProfilesServiceResponse> export(Marshaler request) {
84+
return ClientCalls.futureUnaryCall(
85+
getChannel().newCall(getExportMethod, getCallOptions()), request);
86+
}
87+
}
88+
89+
private MarshalerProfilesServiceGrpc() {}
90+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.exporter.otlp.profiles;
7+
8+
import io.opentelemetry.sdk.common.CompletableResultCode;
9+
import java.util.Collection;
10+
11+
final class NoopProfileExporter implements ProfileExporter {
12+
13+
private static final ProfileExporter INSTANCE = new NoopProfileExporter();
14+
15+
static ProfileExporter getInstance() {
16+
return INSTANCE;
17+
}
18+
19+
@Override
20+
public CompletableResultCode export(Collection<ProfileData> profiles) {
21+
return CompletableResultCode.ofSuccess();
22+
}
23+
24+
@Override
25+
public CompletableResultCode flush() {
26+
return CompletableResultCode.ofSuccess();
27+
}
28+
29+
@Override
30+
public CompletableResultCode shutdown() {
31+
return CompletableResultCode.ofSuccess();
32+
}
33+
34+
@Override
35+
public String toString() {
36+
return "NoopProfilesExporter{}";
37+
}
38+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.exporter.otlp.profiles;
7+
8+
import io.opentelemetry.exporter.internal.grpc.GrpcExporter;
9+
import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder;
10+
import io.opentelemetry.exporter.internal.marshal.Marshaler;
11+
import io.opentelemetry.sdk.common.CompletableResultCode;
12+
import java.util.Collection;
13+
import java.util.StringJoiner;
14+
import javax.annotation.concurrent.ThreadSafe;
15+
16+
/** Exports profiles using OTLP via gRPC, using OpenTelemetry's protobuf model. */
17+
@ThreadSafe
18+
public class OtlpGrpcProfileExporter implements ProfileExporter {
19+
20+
private final GrpcExporterBuilder<Marshaler> builder;
21+
private final GrpcExporter<Marshaler> delegate;
22+
23+
/**
24+
* Returns a new {@link OtlpGrpcProfileExporter} using the default values.
25+
*
26+
* <p>To load configuration values from environment variables and system properties, use <a
27+
* href="https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions/autoconfigure">opentelemetry-sdk-extension-autoconfigure</a>.
28+
*
29+
* @return a new {@link OtlpGrpcProfileExporter} instance.
30+
*/
31+
public static OtlpGrpcProfileExporter getDefault() {
32+
return builder().build();
33+
}
34+
35+
/**
36+
* Returns a new builder instance for this exporter.
37+
*
38+
* @return a new builder instance for this exporter.
39+
*/
40+
public static OtlpGrpcProfilesExporterBuilder builder() {
41+
return new OtlpGrpcProfilesExporterBuilder();
42+
}
43+
44+
OtlpGrpcProfileExporter(
45+
GrpcExporterBuilder<Marshaler> builder, GrpcExporter<Marshaler> delegate) {
46+
this.builder = builder;
47+
this.delegate = delegate;
48+
}
49+
50+
/**
51+
* Returns a builder with configuration values equal to those for this exporter.
52+
*
53+
* <p>IMPORTANT: Be sure to {@link #shutdown()} this instance if it will no longer be used.
54+
*/
55+
public OtlpGrpcProfilesExporterBuilder toBuilder() {
56+
return new OtlpGrpcProfilesExporterBuilder(builder.copy());
57+
}
58+
59+
/**
60+
* Submits all the given profiles in a single batch to the OpenTelemetry collector.
61+
*
62+
* @param profiles the list of sampled profiles to be exported.
63+
* @return the result of the operation
64+
*/
65+
@Override
66+
public CompletableResultCode export(Collection<ProfileData> profiles) {
67+
ProfilesRequestMarshaler request = ProfilesRequestMarshaler.create(profiles);
68+
return delegate.export(request, profiles.size());
69+
}
70+
71+
/**
72+
* The OTLP exporter does not batch items, so this method will immediately return with success.
73+
*
74+
* @return always Success
75+
*/
76+
@Override
77+
public CompletableResultCode flush() {
78+
return CompletableResultCode.ofSuccess();
79+
}
80+
81+
/**
82+
* Initiates an orderly shutdown in which preexisting calls continue but new calls are immediately
83+
* cancelled.
84+
*/
85+
@Override
86+
public CompletableResultCode shutdown() {
87+
return delegate.shutdown();
88+
}
89+
90+
@Override
91+
public String toString() {
92+
StringJoiner joiner = new StringJoiner(", ", "OtlpGrpcProfilesExporter{", "}");
93+
joiner.add(builder.toString(false));
94+
return joiner.toString();
95+
}
96+
}

0 commit comments

Comments
 (0)