-
Notifications
You must be signed in to change notification settings - Fork 1k
OpenSearch Transport v3.0 Implementation #14823
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
ab8ca71
1ec0416
6451368
bde4f45
8ea5cb4
4ed0def
a7663d1
e66488d
e4d95fa
e90f611
4443006
99b511b
c310326
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
plugins { | ||
id("otel.javaagent-instrumentation") | ||
} | ||
|
||
muzzle { | ||
pass { | ||
group.set("org.opensearch.client") | ||
module.set("opensearch-java") | ||
versions.set("[3.0,)") | ||
} | ||
} | ||
|
||
otelJava { | ||
minJavaVersionSupported.set(JavaVersion.VERSION_11) | ||
} | ||
|
||
dependencies { | ||
library("org.opensearch.client:opensearch-java:3.0.0") | ||
compileOnly("com.google.auto.value:auto-value-annotations") | ||
annotationProcessor("com.google.auto.value:auto-value") | ||
|
||
testImplementation("org.opensearch.client:opensearch-rest-client:3.0.0") | ||
testImplementation(project(":instrumentation:opensearch:opensearch-rest-common:testing")) | ||
testInstrumentation(project(":instrumentation:apache-httpclient:apache-httpclient-5.0:javaagent")) | ||
|
||
// For testing AwsSdk2Transport | ||
testInstrumentation(project(":instrumentation:apache-httpclient:apache-httpclient-4.0:javaagent")) | ||
testInstrumentation(project(":instrumentation:netty:netty-4.1:javaagent")) | ||
testImplementation("software.amazon.awssdk:auth:2.22.0") | ||
testImplementation("software.amazon.awssdk:identity-spi:2.22.0") | ||
testImplementation("software.amazon.awssdk:apache-client:2.22.0") | ||
testImplementation("software.amazon.awssdk:netty-nio-client:2.22.0") | ||
testImplementation("software.amazon.awssdk:regions:2.22.0") | ||
} | ||
|
||
tasks { | ||
test { | ||
usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) | ||
} | ||
|
||
val testStableSemconv by registering(Test::class) { | ||
testClassesDirs = sourceSets.test.get().output.classesDirs | ||
classpath = sourceSets.test.get().runtimeClasspath | ||
jvmArgs("-Dotel.semconv-stability.opt-in=database") | ||
} | ||
|
||
check { | ||
dependsOn(testStableSemconv) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.opensearch.java.v3_0; | ||
|
||
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter; | ||
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; | ||
import javax.annotation.Nullable; | ||
|
||
final class OpenSearchJavaAttributesGetter | ||
implements DbClientAttributesGetter<OpenSearchJavaRequest, Void> { | ||
|
||
@SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues | ||
@Override | ||
public String getDbSystem(OpenSearchJavaRequest request) { | ||
return DbIncubatingAttributes.DbSystemIncubatingValues.OPENSEARCH; | ||
} | ||
|
||
@Deprecated | ||
@Override | ||
@Nullable | ||
public String getUser(OpenSearchJavaRequest request) { | ||
return null; | ||
} | ||
laurit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
@Override | ||
@Nullable | ||
public String getDbNamespace(OpenSearchJavaRequest request) { | ||
return null; | ||
} | ||
|
||
@Deprecated | ||
@Override | ||
@Nullable | ||
public String getConnectionString(OpenSearchJavaRequest request) { | ||
return null; | ||
} | ||
laurit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
@Override | ||
@Nullable | ||
public String getDbQueryText(OpenSearchJavaRequest request) { | ||
return request.getMethod() + " " + request.getOperation(); | ||
|
||
} | ||
|
||
@Override | ||
@Nullable | ||
public String getDbOperationName(OpenSearchJavaRequest request) { | ||
return request.getMethod(); | ||
} | ||
|
||
@Nullable | ||
@Override | ||
public String getResponseStatus(@Nullable Void response, @Nullable Throwable error) { | ||
return null; // Response status is handled by HTTP instrumentation | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.opensearch.java.v3_0; | ||
|
||
import static java.util.Collections.singletonList; | ||
|
||
import com.google.auto.service.AutoService; | ||
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; | ||
import java.util.List; | ||
|
||
@AutoService(InstrumentationModule.class) | ||
public class OpenSearchJavaInstrumentationModule extends InstrumentationModule { | ||
public OpenSearchJavaInstrumentationModule() { | ||
super("opensearch-java", "opensearch-java-3.0", "opensearch"); | ||
} | ||
|
||
@Override | ||
public List<TypeInstrumentation> typeInstrumentations() { | ||
return singletonList(new OpenSearchTransportInstrumentation()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.opensearch.java.v3_0; | ||
|
||
import io.opentelemetry.api.GlobalOpenTelemetry; | ||
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesExtractor; | ||
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientMetrics; | ||
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientSpanNameExtractor; | ||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; | ||
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; | ||
|
||
public final class OpenSearchJavaInstrumenterFactory { | ||
laurit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
public static Instrumenter<OpenSearchJavaRequest, Void> create(String instrumentationName) { | ||
OpenSearchJavaAttributesGetter dbClientAttributesGetter = new OpenSearchJavaAttributesGetter(); | ||
|
||
return Instrumenter.<OpenSearchJavaRequest, Void>builder( | ||
GlobalOpenTelemetry.get(), | ||
instrumentationName, | ||
DbClientSpanNameExtractor.create(dbClientAttributesGetter)) | ||
.addAttributesExtractor(DbClientAttributesExtractor.create(dbClientAttributesGetter)) | ||
.addOperationMetrics(DbClientMetrics.get()) | ||
.buildInstrumenter(SpanKindExtractor.alwaysClient()); | ||
} | ||
|
||
private OpenSearchJavaInstrumenterFactory() {} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.opensearch.java.v3_0; | ||
|
||
import com.google.auto.value.AutoValue; | ||
|
||
@AutoValue | ||
public abstract class OpenSearchJavaRequest { | ||
laurit marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
public static OpenSearchJavaRequest create(String method, String endpoint) { | ||
return new AutoValue_OpenSearchJavaRequest(method, endpoint); | ||
} | ||
|
||
public abstract String getMethod(); | ||
|
||
public abstract String getOperation(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.opensearch.java.v3_0; | ||
|
||
import io.opentelemetry.context.Context; | ||
import java.util.function.BiConsumer; | ||
|
||
public final class OpenSearchJavaResponseHandler implements BiConsumer<Object, Throwable> { | ||
private final Context context; | ||
private final OpenSearchJavaRequest otelRequest; | ||
|
||
public OpenSearchJavaResponseHandler(Context context, OpenSearchJavaRequest otelRequest) { | ||
this.context = context; | ||
this.otelRequest = otelRequest; | ||
} | ||
|
||
@Override | ||
public void accept(Object response, Throwable error) { | ||
// OpenSearch responses don't provide response information, so the span is closed with null. | ||
OpenSearchJavaSingletons.instrumenter().end(context, otelRequest, null, error); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.opensearch.java.v3_0; | ||
|
||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; | ||
|
||
public final class OpenSearchJavaSingletons { | ||
private static final Instrumenter<OpenSearchJavaRequest, Void> INSTRUMENTER = | ||
OpenSearchJavaInstrumenterFactory.create("io.opentelemetry.opensearch-java-3.0"); | ||
|
||
public static Instrumenter<OpenSearchJavaRequest, Void> instrumenter() { | ||
return INSTRUMENTER; | ||
} | ||
|
||
private OpenSearchJavaSingletons() {} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.opensearch.java.v3_0; | ||
|
||
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; | ||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; | ||
import static io.opentelemetry.javaagent.instrumentation.opensearch.java.v3_0.OpenSearchJavaSingletons.instrumenter; | ||
import static net.bytebuddy.matcher.ElementMatchers.isMethod; | ||
import static net.bytebuddy.matcher.ElementMatchers.isPublic; | ||
import static net.bytebuddy.matcher.ElementMatchers.named; | ||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; | ||
|
||
import io.opentelemetry.context.Context; | ||
import io.opentelemetry.context.Scope; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; | ||
import java.util.concurrent.CompletableFuture; | ||
import net.bytebuddy.asm.Advice; | ||
import net.bytebuddy.description.type.TypeDescription; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
import org.opensearch.client.transport.Endpoint; | ||
|
||
public class OpenSearchTransportInstrumentation implements TypeInstrumentation { | ||
@Override | ||
public ElementMatcher<TypeDescription> typeMatcher() { | ||
return implementsInterface(named("org.opensearch.client.transport.OpenSearchTransport")); | ||
} | ||
|
||
@Override | ||
public void transform(TypeTransformer transformer) { | ||
transformer.applyAdviceToMethod( | ||
isMethod() | ||
.and(isPublic()) | ||
.and(named("performRequest")) | ||
.and(takesArgument(0, Object.class)) | ||
.and(takesArgument(1, named("org.opensearch.client.transport.Endpoint"))), | ||
this.getClass().getName() + "$PerformRequestAdvice"); | ||
|
||
transformer.applyAdviceToMethod( | ||
isMethod() | ||
.and(isPublic()) | ||
.and(named("performRequestAsync")) | ||
.and(takesArgument(0, Object.class)) | ||
.and(takesArgument(1, named("org.opensearch.client.transport.Endpoint"))), | ||
this.getClass().getName() + "$PerformRequestAsyncAdvice"); | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
public static class PerformRequestAdvice { | ||
|
||
@Advice.OnMethodEnter(suppress = Throwable.class) | ||
public static void onEnter( | ||
@Advice.Argument(0) Object request, | ||
@Advice.Argument(1) Endpoint<Object, Object, Object> endpoint, | ||
@Advice.Local("otelRequest") OpenSearchJavaRequest otelRequest, | ||
@Advice.Local("otelContext") Context context, | ||
@Advice.Local("otelScope") Scope scope) { | ||
|
||
Context parentContext = currentContext(); | ||
otelRequest = | ||
OpenSearchJavaRequest.create(endpoint.method(request), endpoint.requestUrl(request)); | ||
if (!instrumenter().shouldStart(parentContext, otelRequest)) { | ||
return; | ||
} | ||
|
||
context = instrumenter().start(parentContext, otelRequest); | ||
scope = context.makeCurrent(); | ||
} | ||
|
||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) | ||
public static void stopSpan( | ||
@Advice.Thrown Throwable throwable, | ||
@Advice.Local("otelRequest") OpenSearchJavaRequest otelRequest, | ||
@Advice.Local("otelContext") Context context, | ||
@Advice.Local("otelScope") Scope scope) { | ||
|
||
if (scope == null) { | ||
return; | ||
} | ||
scope.close(); | ||
|
||
instrumenter().end(context, otelRequest, null, throwable); | ||
} | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
public static class PerformRequestAsyncAdvice { | ||
|
||
@Advice.OnMethodEnter(suppress = Throwable.class) | ||
public static void onEnter( | ||
@Advice.Argument(0) Object request, | ||
@Advice.Argument(value = 1, readOnly = false) Endpoint<Object, Object, Object> endpoint, | ||
@Advice.Local("otelRequest") OpenSearchJavaRequest otelRequest, | ||
@Advice.Local("otelContext") Context context, | ||
@Advice.Local("otelScope") Scope scope) { | ||
|
||
Context parentContext = currentContext(); | ||
otelRequest = | ||
OpenSearchJavaRequest.create(endpoint.method(request), endpoint.requestUrl(request)); | ||
if (!instrumenter().shouldStart(parentContext, otelRequest)) { | ||
return; | ||
} | ||
|
||
context = instrumenter().start(parentContext, otelRequest); | ||
scope = context.makeCurrent(); | ||
} | ||
|
||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) | ||
public static void stopSpan( | ||
@Advice.Thrown Throwable throwable, | ||
@Advice.Return(readOnly = false) CompletableFuture<Object> future, | ||
@Advice.Local("otelRequest") OpenSearchJavaRequest otelRequest, | ||
@Advice.Local("otelContext") Context context, | ||
@Advice.Local("otelScope") Scope scope) { | ||
|
||
if (scope == null) { | ||
return; | ||
} | ||
scope.close(); | ||
|
||
if (throwable != null) { | ||
instrumenter().end(context, otelRequest, null, throwable); | ||
} | ||
|
||
future.whenComplete(new OpenSearchJavaResponseHandler(context, otelRequest)); | ||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.