-
Notifications
You must be signed in to change notification settings - Fork 860
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for OpenSearch 1.x and 2.x auto-instrumentation (#6998)
This PR adds support for OpenSearch 1.x and 2.x Java clients auto-instrumentation. This is made possible by OpenTelemetry specification v1.14.0 and OpenTelemetry Java SDK v1.19.0. Testing is being done using org.opensearch:opensearch-testcontainers:2.0.0 (https://github.com/opensearch-project/opensearch-testcontainers) Resolves #7007 Signed-off-by: Cédric Pelvet <cedric.pelvet@gmail.com> Signed-off-by: Cédric Pelvet <cedric.pelvet@gmail.com> Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
- Loading branch information
1 parent
b9b3fea
commit 31f4d12
Showing
15 changed files
with
658 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Settings for the OpenSearch instrumentation | ||
|
||
| System property | Type | Default | Description | | ||
|----------------------------------------------------------------|-----------|---------|-----------------------------------------------------| | ||
| `otel.instrumentation.opensearch.experimental-span-attributes` | `Boolean` | `false` | Enable the capture of experimental span attributes. | |
43 changes: 43 additions & 0 deletions
43
instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
plugins { | ||
id("otel.javaagent-instrumentation") | ||
} | ||
|
||
muzzle { | ||
pass { | ||
group.set("org.opensearch.client") | ||
module.set("opensearch-rest-client") | ||
versions.set("[1.0,)") | ||
assertInverse.set(true) | ||
} | ||
|
||
fail { | ||
group.set("org.opensearch.client") | ||
module.set("rest") | ||
versions.set("(,)") | ||
} | ||
} | ||
|
||
otelJava { | ||
minJavaVersionSupported.set(JavaVersion.VERSION_11) | ||
} | ||
|
||
dependencies { | ||
library("org.opensearch.client:opensearch-rest-client:1.0.0") | ||
|
||
implementation(project(":instrumentation:opensearch:opensearch-rest-common:javaagent")) | ||
|
||
testInstrumentation(project(":instrumentation:apache-httpclient:apache-httpclient-4.0:javaagent")) | ||
testInstrumentation(project(":instrumentation:apache-httpasyncclient-4.1:javaagent")) | ||
|
||
testImplementation("org.apache.logging.log4j:log4j-core:2.18.0") | ||
testImplementation("org.apache.logging.log4j:log4j-api:2.18.0") | ||
testImplementation("org.apache.commons:commons-lang3:3.12.0") | ||
testImplementation("commons-io:commons-io:2.11.0") | ||
testImplementation("org.opensearch:opensearch-testcontainers:2.0.0") | ||
} | ||
|
||
tasks { | ||
test { | ||
usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
...y/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRestInstrumentationModule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.opensearch.rest.v1_0; | ||
|
||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; | ||
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; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
|
||
@AutoService(InstrumentationModule.class) | ||
public class OpenSearchRestInstrumentationModule extends InstrumentationModule { | ||
public OpenSearchRestInstrumentationModule() { | ||
super("opensearch-rest", "opensearch-rest-1.0", "opensearch"); | ||
} | ||
|
||
@Override | ||
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() { | ||
// class introduced in 1.0.0 | ||
return hasClassesNamed("org.opensearch.client.RestClient$InternalRequest"); | ||
} | ||
|
||
@Override | ||
public List<TypeInstrumentation> typeInstrumentations() { | ||
return singletonList(new RestClientInstrumentation()); | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
...pentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRestSingletons.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.opensearch.rest.v1_0; | ||
|
||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; | ||
import io.opentelemetry.javaagent.instrumentation.opensearch.rest.OpenSearchRestInstrumenterFactory; | ||
import io.opentelemetry.javaagent.instrumentation.opensearch.rest.OpenSearchRestRequest; | ||
import org.opensearch.client.Response; | ||
|
||
public final class OpenSearchRestSingletons { | ||
|
||
private static final Instrumenter<OpenSearchRestRequest, Response> INSTRUMENTER = | ||
OpenSearchRestInstrumenterFactory.create("io.opentelemetry.opensearch-rest-1.0"); | ||
|
||
public static Instrumenter<OpenSearchRestRequest, Response> instrumenter() { | ||
return INSTRUMENTER; | ||
} | ||
|
||
private OpenSearchRestSingletons() {} | ||
} |
134 changes: 134 additions & 0 deletions
134
...entelemetry/javaagent/instrumentation/opensearch/rest/v1_0/RestClientInstrumentation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.opensearch.rest.v1_0; | ||
|
||
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; | ||
import static net.bytebuddy.matcher.ElementMatchers.isMethod; | ||
import static net.bytebuddy.matcher.ElementMatchers.named; | ||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; | ||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; | ||
|
||
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 io.opentelemetry.javaagent.instrumentation.opensearch.rest.OpenSearchRestRequest; | ||
import io.opentelemetry.javaagent.instrumentation.opensearch.rest.RestResponseListener; | ||
import net.bytebuddy.asm.Advice; | ||
import net.bytebuddy.description.type.TypeDescription; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
import org.opensearch.client.Request; | ||
import org.opensearch.client.Response; | ||
import org.opensearch.client.ResponseListener; | ||
|
||
public class RestClientInstrumentation implements TypeInstrumentation { | ||
@Override | ||
public ElementMatcher<TypeDescription> typeMatcher() { | ||
return named("org.opensearch.client.RestClient"); | ||
} | ||
|
||
@Override | ||
public void transform(TypeTransformer transformer) { | ||
transformer.applyAdviceToMethod( | ||
isMethod() | ||
.and(named("performRequest")) | ||
.and(takesArguments(1)) | ||
.and(takesArgument(0, named("org.opensearch.client.Request"))), | ||
this.getClass().getName() + "$PerformRequestAdvice"); | ||
transformer.applyAdviceToMethod( | ||
isMethod() | ||
.and(named("performRequestAsync")) | ||
.and(takesArguments(2)) | ||
.and(takesArgument(0, named("org.opensearch.client.Request"))) | ||
.and(takesArgument(1, named("org.opensearch.client.ResponseListener"))), | ||
this.getClass().getName() + "$PerformRequestAsyncAdvice"); | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
public static class PerformRequestAdvice { | ||
|
||
@Advice.OnMethodEnter(suppress = Throwable.class) | ||
public static void onEnter( | ||
@Advice.Argument(0) Request request, | ||
@Advice.Local("otelRequest") OpenSearchRestRequest otelRequest, | ||
@Advice.Local("otelContext") Context context, | ||
@Advice.Local("otelScope") Scope scope) { | ||
|
||
Context parentContext = currentContext(); | ||
otelRequest = OpenSearchRestRequest.create(request.getMethod(), request.getEndpoint()); | ||
if (!OpenSearchRestSingletons.instrumenter().shouldStart(parentContext, otelRequest)) { | ||
return; | ||
} | ||
|
||
context = OpenSearchRestSingletons.instrumenter().start(parentContext, otelRequest); | ||
scope = context.makeCurrent(); | ||
} | ||
|
||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) | ||
public static void stopSpan( | ||
@Advice.Return Response response, | ||
@Advice.Thrown Throwable throwable, | ||
@Advice.Local("otelRequest") OpenSearchRestRequest otelRequest, | ||
@Advice.Local("otelContext") Context context, | ||
@Advice.Local("otelScope") Scope scope) { | ||
|
||
if (scope == null) { | ||
return; | ||
} | ||
scope.close(); | ||
|
||
OpenSearchRestSingletons.instrumenter().end(context, otelRequest, response, throwable); | ||
} | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
public static class PerformRequestAsyncAdvice { | ||
|
||
@Advice.OnMethodEnter(suppress = Throwable.class) | ||
public static void onEnter( | ||
@Advice.Argument(0) Request request, | ||
@Advice.Argument(value = 1, readOnly = false) ResponseListener responseListener, | ||
@Advice.Local("otelRequest") OpenSearchRestRequest otelRequest, | ||
@Advice.Local("otelContext") Context context, | ||
@Advice.Local("otelScope") Scope scope) { | ||
|
||
Context parentContext = currentContext(); | ||
otelRequest = OpenSearchRestRequest.create(request.getMethod(), request.getEndpoint()); | ||
if (!OpenSearchRestSingletons.instrumenter().shouldStart(parentContext, otelRequest)) { | ||
return; | ||
} | ||
|
||
context = OpenSearchRestSingletons.instrumenter().start(parentContext, otelRequest); | ||
scope = context.makeCurrent(); | ||
|
||
responseListener = | ||
new RestResponseListener( | ||
responseListener, | ||
parentContext, | ||
OpenSearchRestSingletons.instrumenter(), | ||
context, | ||
otelRequest); | ||
} | ||
|
||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) | ||
public static void stopSpan( | ||
@Advice.Thrown Throwable throwable, | ||
@Advice.Local("otelRequest") OpenSearchRestRequest otelRequest, | ||
@Advice.Local("otelContext") Context context, | ||
@Advice.Local("otelScope") Scope scope) { | ||
|
||
if (scope == null) { | ||
return; | ||
} | ||
scope.close(); | ||
|
||
if (throwable != null) { | ||
OpenSearchRestSingletons.instrumenter().end(context, otelRequest, null, throwable); | ||
} | ||
// span ended in RestResponseListener | ||
} | ||
} | ||
} |
Oops, something went wrong.