From bae02748e8dfb6b0fbe13bd2b83ac23f8c6648dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Pelvet?= Date: Mon, 8 Aug 2022 18:25:47 +0200 Subject: [PATCH 01/11] Add support for OpenSearch 1.x and 2.x auto-instrumentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Pelvet --- .../decorators/DbSpanDecorator.java | 2 + .../decorators/DecoratorRegistry.java | 1 + instrumentation/opensearch/README.md | 5 + .../javaagent/build.gradle.kts | 42 ++++ .../OpenSearchRest1InstrumentationModule.java | 33 ++++ .../rest/v1_0/OpenSearchRest1Singletons.java | 23 +++ .../rest/v1_0/RestClientInstrumentation.java | 134 +++++++++++++ .../src/test/groovy/OpenSearchRestTest.groovy | 186 ++++++++++++++++++ .../javaagent/build.gradle.kts | 42 ++++ .../OpenSearchRest2InstrumentationModule.java | 33 ++++ .../rest/v2_0/OpenSearchRest2Singletons.java | 23 +++ .../rest/v2_0/RestClientInstrumentation.java | 134 +++++++++++++ .../src/test/groovy/OpenSearchRestTest.groovy | 186 ++++++++++++++++++ .../javaagent/build.gradle.kts | 10 + .../rest/OpenSearchRestAttributesGetter.java | 49 +++++ .../OpenSearchRestInstrumenterFactory.java | 38 ++++ ...SearchRestNetResponseAttributesGetter.java | 52 +++++ .../rest/OpenSearchRestRequest.java | 20 ++ .../opensearch/rest/RestResponseListener.java | 50 +++++ settings.gradle.kts | 3 + 20 files changed, 1066 insertions(+) create mode 100644 instrumentation/opensearch/README.md create mode 100644 instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts create mode 100644 instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRest1InstrumentationModule.java create mode 100644 instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRest1Singletons.java create mode 100644 instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/RestClientInstrumentation.java create mode 100644 instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/test/groovy/OpenSearchRestTest.groovy create mode 100644 instrumentation/opensearch/opensearch-rest-2.0/javaagent/build.gradle.kts create mode 100644 instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/OpenSearchRest2InstrumentationModule.java create mode 100644 instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/OpenSearchRest2Singletons.java create mode 100644 instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/RestClientInstrumentation.java create mode 100644 instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/test/groovy/OpenSearchRestTest.groovy create mode 100644 instrumentation/opensearch/opensearch-rest-common/javaagent/build.gradle.kts create mode 100644 instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestAttributesGetter.java create mode 100644 instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestInstrumenterFactory.java create mode 100644 instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestNetResponseAttributesGetter.java create mode 100644 instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestRequest.java create mode 100644 instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/RestResponseListener.java diff --git a/instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DbSpanDecorator.java b/instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DbSpanDecorator.java index f831cf8b61c3..990e6ade635a 100644 --- a/instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DbSpanDecorator.java +++ b/instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DbSpanDecorator.java @@ -53,6 +53,7 @@ public String getOperationName( switch (component) { case "mongodb": case "elasticsearch": + case "opensearch": Map queryParameters = toQueryParameters(endpoint.getEndpointUri()); if (queryParameters.containsKey("operation")) { return queryParameters.get("operation"); @@ -102,6 +103,7 @@ private String getDbName(Endpoint endpoint) { } return null; case "elasticsearch": + case "opensearch": Map elasticsearchParameters = toQueryParameters(endpoint.getEndpointUri()); if (elasticsearchParameters.containsKey("indexName")) { return elasticsearchParameters.get("indexName"); diff --git a/instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DecoratorRegistry.java b/instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DecoratorRegistry.java index c8f2ec465ea2..1b052c9b9e97 100644 --- a/instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DecoratorRegistry.java +++ b/instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DecoratorRegistry.java @@ -31,6 +31,7 @@ private static Map loadDecorators() { result.put("disruptor", new InternalSpanDecorator()); result.put("disruptor-vm", new InternalSpanDecorator()); result.put("elasticsearch", new DbSpanDecorator("elasticsearch", "elasticsearch")); + result.put("opensearch", new DbSpanDecorator("opensearch", "opensearch")); result.put("http4", new Http4SpanDecorator()); result.put("https4", new Https4SpanDecorator()); result.put("http", new HttpSpanDecorator()); diff --git a/instrumentation/opensearch/README.md b/instrumentation/opensearch/README.md new file mode 100644 index 000000000000..a23faee716b8 --- /dev/null +++ b/instrumentation/opensearch/README.md @@ -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. | \ No newline at end of file diff --git a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts new file mode 100644 index 000000000000..d8334967181f --- /dev/null +++ b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts @@ -0,0 +1,42 @@ +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("(,)") + } +} + +dependencies { + library("org.opensearch.client:opensearch-rest-client:1.3.6") + + 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") + + testCompileOnly("org.projectlombok:lombok:1.18.24") + testAnnotationProcessor("org.projectlombok:lombok:1.18.24") +} + +tasks { + test { + usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) + } +} diff --git a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRest1InstrumentationModule.java b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRest1InstrumentationModule.java new file mode 100644 index 000000000000..73eb8246d56d --- /dev/null +++ b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRest1InstrumentationModule.java @@ -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 OpenSearchRest1InstrumentationModule extends InstrumentationModule { + public OpenSearchRest1InstrumentationModule() { + super("opensearch-rest", "opensearch-rest-1.0", "opensearch"); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + // class introduced in 1.0.0 + return hasClassesNamed("org.opensearch.client.RestClient$InternalRequest"); + } + + @Override + public List typeInstrumentations() { + return singletonList(new RestClientInstrumentation()); + } +} diff --git a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRest1Singletons.java b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRest1Singletons.java new file mode 100644 index 000000000000..8ea9ac82e454 --- /dev/null +++ b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRest1Singletons.java @@ -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 OpenSearchRest1Singletons { + + private static final Instrumenter INSTRUMENTER = + OpenSearchRestInstrumenterFactory.create("io.opentelemetry.opensearch-rest-1.0"); + + public static Instrumenter instrumenter() { + return INSTRUMENTER; + } + + private OpenSearchRest1Singletons() {} +} diff --git a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/RestClientInstrumentation.java b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/RestClientInstrumentation.java new file mode 100644 index 000000000000..9342af899894 --- /dev/null +++ b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/RestClientInstrumentation.java @@ -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 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 (!OpenSearchRest1Singletons.instrumenter().shouldStart(parentContext, otelRequest)) { + return; + } + + context = OpenSearchRest1Singletons.instrumenter().start(parentContext, otelRequest); + scope = context.makeCurrent(); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void stopSpan( + @Advice.Return(readOnly = false) 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(); + + OpenSearchRest1Singletons.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 (!OpenSearchRest1Singletons.instrumenter().shouldStart(parentContext, otelRequest)) { + return; + } + + context = OpenSearchRest1Singletons.instrumenter().start(parentContext, otelRequest); + scope = context.makeCurrent(); + + responseListener = + new RestResponseListener( + responseListener, + parentContext, + OpenSearchRest1Singletons.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) { + OpenSearchRest1Singletons.instrumenter().end(context, otelRequest, null, throwable); + } + // span ended in RestResponseListener + } + } +} diff --git a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/test/groovy/OpenSearchRestTest.groovy b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/test/groovy/OpenSearchRestTest.groovy new file mode 100644 index 000000000000..8f77969b0058 --- /dev/null +++ b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/test/groovy/OpenSearchRestTest.groovy @@ -0,0 +1,186 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import groovy.json.JsonSlurper +import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes +import org.apache.http.HttpHost +import org.apache.http.auth.AuthScope +import org.apache.http.auth.UsernamePasswordCredentials +import org.apache.http.client.CredentialsProvider +import org.apache.http.conn.ssl.TrustAllStrategy +import org.apache.http.impl.client.BasicCredentialsProvider +import org.apache.http.ssl.SSLContextBuilder +import org.apache.http.util.EntityUtils +import org.opensearch.client.Request +import org.opensearch.client.Response +import org.opensearch.client.ResponseListener +import org.opensearch.client.RestClient +import org.opensearch.testcontainers.OpensearchContainer +import org.testcontainers.utility.DockerImageName +import spock.lang.Shared + +import javax.net.ssl.SSLContext +import java.util.concurrent.CountDownLatch + +import static io.opentelemetry.api.trace.SpanKind.CLIENT +import static io.opentelemetry.api.trace.SpanKind.INTERNAL + +class OpenSearchRestTest extends AgentInstrumentationSpecification { + @Shared + OpensearchContainer opensearch + + @Shared + HttpHost httpHost + + @Shared + RestClient client + + def setupSpec() { + opensearch = new OpensearchContainer(DockerImageName.parse("opensearchproject/opensearch:1.3.6")).withSecurityEnabled() + // limit memory usage + opensearch.withEnv("OPENSEARCH_JAVA_OPTS", "-Xmx256m -Xms256m") + opensearch.start() + + final CredentialsProvider credentialsProvider = new BasicCredentialsProvider() + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(opensearch.getUsername(), opensearch.getPassword())) + + final SSLContext sslContext = SSLContextBuilder.create() + .loadTrustMaterial(null, new TrustAllStrategy()) + .build() + + httpHost = HttpHost.create(opensearch.getHttpHostAddress()) + client = RestClient.builder(httpHost) + .setHttpClientConfigCallback(httpClientBuilder -> { + return httpClientBuilder + .setSSLContext(sslContext) + .setDefaultCredentialsProvider(credentialsProvider) + }) + .build() + } + + def cleanupSpec() { + opensearch.stop() + } + + def "test opensearch status"() { + setup: + Response response = client.performRequest(new Request("GET", "_cluster/health")) + + Map result = new JsonSlurper().parseText(EntityUtils.toString(response.entity)) + + expect: + result.status == "green" + + assertTraces(1) { + trace(0, 2) { + span(0) { + name "GET" + kind CLIENT + hasNoParent() + attributes { + "$SemanticAttributes.DB_SYSTEM" "opensearch" + "$SemanticAttributes.DB_OPERATION" "GET" + "$SemanticAttributes.DB_STATEMENT" "GET _cluster/health" + "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP + } + } + span(1) { + name "HTTP GET" + kind CLIENT + childOf span(0) + attributes { + "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP + "$SemanticAttributes.NET_PEER_NAME" httpHost.hostName + "$SemanticAttributes.NET_PEER_PORT" httpHost.port + "$SemanticAttributes.HTTP_METHOD" "GET" + "$SemanticAttributes.HTTP_FLAVOR" SemanticAttributes.HttpFlavorValues.HTTP_1_1 + "$SemanticAttributes.HTTP_URL" "${httpHost.toURI()}/_cluster/health" + "$SemanticAttributes.HTTP_STATUS_CODE" 200 + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long + } + } + } + } + } + + def "test opensearch status async"() { + setup: + Response requestResponse = null + Exception exception = null + CountDownLatch countDownLatch = new CountDownLatch(1) + ResponseListener responseListener = new ResponseListener() { + @Override + void onSuccess(Response response) { + runWithSpan("callback") { + requestResponse = response + countDownLatch.countDown() + } + } + + @Override + void onFailure(Exception e) { + runWithSpan("callback") { + exception = e + countDownLatch.countDown() + } + } + } + runWithSpan("parent") { + client.performRequestAsync(new Request("GET", "_cluster/health"), responseListener) + } + countDownLatch.await() + + if (exception != null) { + throw exception + } + Map result = new JsonSlurper().parseText(EntityUtils.toString(requestResponse.entity)) as Map + + expect: + result.status == "green" + + assertTraces(1) { + trace(0, 4) { + span(0) { + name "parent" + kind INTERNAL + hasNoParent() + } + span(1) { + name "GET" + kind CLIENT + childOf(span(0)) + attributes { + "$SemanticAttributes.DB_SYSTEM" "opensearch" + "$SemanticAttributes.DB_OPERATION" "GET" + "$SemanticAttributes.DB_STATEMENT" "GET _cluster/health" + "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP + } + } + span(2) { + name "HTTP GET" + kind CLIENT + childOf span(1) + attributes { + "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP + "$SemanticAttributes.NET_PEER_NAME" httpHost.hostName + "$SemanticAttributes.NET_PEER_PORT" httpHost.port + "$SemanticAttributes.HTTP_METHOD" "GET" + "$SemanticAttributes.HTTP_FLAVOR" SemanticAttributes.HttpFlavorValues.HTTP_1_1 + "$SemanticAttributes.HTTP_URL" "${httpHost.toURI()}/_cluster/health" + "$SemanticAttributes.HTTP_STATUS_CODE" 200 + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" 415 + } + } + span(3) { + name "callback" + kind INTERNAL + childOf(span(0)) + } + } + } + } +} diff --git a/instrumentation/opensearch/opensearch-rest-2.0/javaagent/build.gradle.kts b/instrumentation/opensearch/opensearch-rest-2.0/javaagent/build.gradle.kts new file mode 100644 index 000000000000..b1d6007694a9 --- /dev/null +++ b/instrumentation/opensearch/opensearch-rest-2.0/javaagent/build.gradle.kts @@ -0,0 +1,42 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +muzzle { + pass { + group.set("org.opensearch.client") + module.set("opensearch-rest-client") + versions.set("[2.0,)") + assertInverse.set(true) + } + + fail { + group.set("org.opensearch.client") + module.set("rest") + versions.set("(,)") + } +} + +dependencies { + library("org.opensearch.client:opensearch-rest-client:2.3.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") + + testCompileOnly("org.projectlombok:lombok:1.18.24") + testAnnotationProcessor("org.projectlombok:lombok:1.18.24") +} + +tasks { + test { + usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) + } +} diff --git a/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/OpenSearchRest2InstrumentationModule.java b/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/OpenSearchRest2InstrumentationModule.java new file mode 100644 index 000000000000..392da4de6e21 --- /dev/null +++ b/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/OpenSearchRest2InstrumentationModule.java @@ -0,0 +1,33 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opensearch.rest.v2_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 OpenSearchRest2InstrumentationModule extends InstrumentationModule { + public OpenSearchRest2InstrumentationModule() { + super("opensearch-rest", "opensearch-rest-2.0", "opensearch"); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + // class introduced in 1.0.0 + return hasClassesNamed("org.opensearch.client.RestClient$InternalRequest"); + } + + @Override + public List typeInstrumentations() { + return singletonList(new RestClientInstrumentation()); + } +} diff --git a/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/OpenSearchRest2Singletons.java b/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/OpenSearchRest2Singletons.java new file mode 100644 index 000000000000..87c7d5a839c2 --- /dev/null +++ b/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/OpenSearchRest2Singletons.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opensearch.rest.v2_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 OpenSearchRest2Singletons { + + private static final Instrumenter INSTRUMENTER = + OpenSearchRestInstrumenterFactory.create("io.opentelemetry.opensearch-rest-2.0"); + + public static Instrumenter instrumenter() { + return INSTRUMENTER; + } + + private OpenSearchRest2Singletons() {} +} diff --git a/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/RestClientInstrumentation.java b/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/RestClientInstrumentation.java new file mode 100644 index 000000000000..cbf6f44d1e72 --- /dev/null +++ b/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/RestClientInstrumentation.java @@ -0,0 +1,134 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opensearch.rest.v2_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 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 (!OpenSearchRest2Singletons.instrumenter().shouldStart(parentContext, otelRequest)) { + return; + } + + context = OpenSearchRest2Singletons.instrumenter().start(parentContext, otelRequest); + scope = context.makeCurrent(); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void stopSpan( + @Advice.Return(readOnly = false) 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(); + + OpenSearchRest2Singletons.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 (!OpenSearchRest2Singletons.instrumenter().shouldStart(parentContext, otelRequest)) { + return; + } + + context = OpenSearchRest2Singletons.instrumenter().start(parentContext, otelRequest); + scope = context.makeCurrent(); + + responseListener = + new RestResponseListener( + responseListener, + parentContext, + OpenSearchRest2Singletons.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) { + OpenSearchRest2Singletons.instrumenter().end(context, otelRequest, null, throwable); + } + // span ended in RestResponseListener + } + } +} diff --git a/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/test/groovy/OpenSearchRestTest.groovy b/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/test/groovy/OpenSearchRestTest.groovy new file mode 100644 index 000000000000..876fe9d93fcf --- /dev/null +++ b/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/test/groovy/OpenSearchRestTest.groovy @@ -0,0 +1,186 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import groovy.json.JsonSlurper +import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes +import org.apache.http.HttpHost +import org.apache.http.auth.AuthScope +import org.apache.http.auth.UsernamePasswordCredentials +import org.apache.http.client.CredentialsProvider +import org.apache.http.conn.ssl.TrustAllStrategy +import org.apache.http.impl.client.BasicCredentialsProvider +import org.apache.http.ssl.SSLContextBuilder +import org.apache.http.util.EntityUtils +import org.opensearch.client.Request +import org.opensearch.client.Response +import org.opensearch.client.ResponseListener +import org.opensearch.client.RestClient +import org.opensearch.testcontainers.OpensearchContainer +import org.testcontainers.utility.DockerImageName +import spock.lang.Shared + +import javax.net.ssl.SSLContext +import java.util.concurrent.CountDownLatch + +import static io.opentelemetry.api.trace.SpanKind.CLIENT +import static io.opentelemetry.api.trace.SpanKind.INTERNAL + +class OpenSearchRestTest extends AgentInstrumentationSpecification { + @Shared + OpensearchContainer opensearch + + @Shared + HttpHost httpHost + + @Shared + RestClient client + + def setupSpec() { + opensearch = new OpensearchContainer(DockerImageName.parse("opensearchproject/opensearch:2.3.0")).withSecurityEnabled() + // limit memory usage + opensearch.withEnv("OPENSEARCH_JAVA_OPTS", "-Xmx256m -Xms256m") + opensearch.start() + + final CredentialsProvider credentialsProvider = new BasicCredentialsProvider() + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(opensearch.getUsername(), opensearch.getPassword())) + + final SSLContext sslContext = SSLContextBuilder.create() + .loadTrustMaterial(null, new TrustAllStrategy()) + .build() + + httpHost = HttpHost.create(opensearch.getHttpHostAddress()) + client = RestClient.builder(httpHost) + .setHttpClientConfigCallback(httpClientBuilder -> { + return httpClientBuilder + .setSSLContext(sslContext) + .setDefaultCredentialsProvider(credentialsProvider) + }) + .build() + } + + def cleanupSpec() { + opensearch.stop() + } + + def "test opensearch status"() { + setup: + Response response = client.performRequest(new Request("GET", "_cluster/health")) + + Map result = new JsonSlurper().parseText(EntityUtils.toString(response.entity)) + + expect: + result.status == "green" + + assertTraces(1) { + trace(0, 2) { + span(0) { + name "GET" + kind CLIENT + hasNoParent() + attributes { + "$SemanticAttributes.DB_SYSTEM" "opensearch" + "$SemanticAttributes.DB_OPERATION" "GET" + "$SemanticAttributes.DB_STATEMENT" "GET _cluster/health" + "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP + } + } + span(1) { + name "HTTP GET" + kind CLIENT + childOf span(0) + attributes { + "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP + "$SemanticAttributes.NET_PEER_NAME" httpHost.hostName + "$SemanticAttributes.NET_PEER_PORT" httpHost.port + "$SemanticAttributes.HTTP_METHOD" "GET" + "$SemanticAttributes.HTTP_FLAVOR" SemanticAttributes.HttpFlavorValues.HTTP_1_1 + "$SemanticAttributes.HTTP_URL" "${httpHost.toURI()}/_cluster/health" + "$SemanticAttributes.HTTP_STATUS_CODE" 200 + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long + } + } + } + } + } + + def "test opensearch status async"() { + setup: + Response requestResponse = null + Exception exception = null + CountDownLatch countDownLatch = new CountDownLatch(1) + ResponseListener responseListener = new ResponseListener() { + @Override + void onSuccess(Response response) { + runWithSpan("callback") { + requestResponse = response + countDownLatch.countDown() + } + } + + @Override + void onFailure(Exception e) { + runWithSpan("callback") { + exception = e + countDownLatch.countDown() + } + } + } + runWithSpan("parent") { + client.performRequestAsync(new Request("GET", "_cluster/health"), responseListener) + } + countDownLatch.await() + + if (exception != null) { + throw exception + } + Map result = new JsonSlurper().parseText(EntityUtils.toString(requestResponse.entity)) as Map + + expect: + result.status == "green" + + assertTraces(1) { + trace(0, 4) { + span(0) { + name "parent" + kind INTERNAL + hasNoParent() + } + span(1) { + name "GET" + kind CLIENT + childOf(span(0)) + attributes { + "$SemanticAttributes.DB_SYSTEM" "opensearch" + "$SemanticAttributes.DB_OPERATION" "GET" + "$SemanticAttributes.DB_STATEMENT" "GET _cluster/health" + "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP + } + } + span(2) { + name "HTTP GET" + kind CLIENT + childOf span(1) + attributes { + "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP + "$SemanticAttributes.NET_PEER_NAME" httpHost.hostName + "$SemanticAttributes.NET_PEER_PORT" httpHost.port + "$SemanticAttributes.HTTP_METHOD" "GET" + "$SemanticAttributes.HTTP_FLAVOR" SemanticAttributes.HttpFlavorValues.HTTP_1_1 + "$SemanticAttributes.HTTP_URL" "${httpHost.toURI()}/_cluster/health" + "$SemanticAttributes.HTTP_STATUS_CODE" 200 + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" 449 + } + } + span(3) { + name "callback" + kind INTERNAL + childOf(span(0)) + } + } + } + } +} diff --git a/instrumentation/opensearch/opensearch-rest-common/javaagent/build.gradle.kts b/instrumentation/opensearch/opensearch-rest-common/javaagent/build.gradle.kts new file mode 100644 index 000000000000..746b735e849d --- /dev/null +++ b/instrumentation/opensearch/opensearch-rest-common/javaagent/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +dependencies { + compileOnly("org.opensearch.client:opensearch-rest-client:1.3.4") + compileOnly("com.google.auto.value:auto-value-annotations") + + annotationProcessor("com.google.auto.value:auto-value") +} diff --git a/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestAttributesGetter.java b/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestAttributesGetter.java new file mode 100644 index 000000000000..1eb7a2bf59b9 --- /dev/null +++ b/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestAttributesGetter.java @@ -0,0 +1,49 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opensearch.rest; + +import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesGetter; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import javax.annotation.Nullable; + +final class OpenSearchRestAttributesGetter + implements DbClientAttributesGetter { + + @Override + public String system(OpenSearchRestRequest request) { + return SemanticAttributes.DbSystemValues.OPENSEARCH; + } + + @Override + @Nullable + public String user(OpenSearchRestRequest request) { + return null; + } + + @Override + @Nullable + public String name(OpenSearchRestRequest request) { + return null; + } + + @Override + @Nullable + public String connectionString(OpenSearchRestRequest request) { + return null; + } + + @Override + @Nullable + public String statement(OpenSearchRestRequest request) { + return request.getMethod() + " " + request.getOperation(); + } + + @Override + @Nullable + public String operation(OpenSearchRestRequest request) { + return request.getMethod(); + } +} diff --git a/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestInstrumenterFactory.java b/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestInstrumenterFactory.java new file mode 100644 index 000000000000..2d05930701b0 --- /dev/null +++ b/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestInstrumenterFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opensearch.rest; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; +import org.opensearch.client.Response; + +public final class OpenSearchRestInstrumenterFactory { + + public static Instrumenter create(String instrumentationName) { + OpenSearchRestAttributesGetter dbClientAttributesGetter = new OpenSearchRestAttributesGetter(); + OpenSearchRestNetResponseAttributesGetter netAttributesGetter = + new OpenSearchRestNetResponseAttributesGetter(); + + return Instrumenter.builder( + GlobalOpenTelemetry.get(), + instrumentationName, + DbClientSpanNameExtractor.create(dbClientAttributesGetter)) + .addAttributesExtractor(DbClientAttributesExtractor.create(dbClientAttributesGetter)) + .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) + .buildInstrumenter(SpanKindExtractor.alwaysClient()); + } + + private OpenSearchRestInstrumenterFactory() {} +} diff --git a/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestNetResponseAttributesGetter.java b/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestNetResponseAttributesGetter.java new file mode 100644 index 000000000000..b8a945d9e4b4 --- /dev/null +++ b/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestNetResponseAttributesGetter.java @@ -0,0 +1,52 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opensearch.rest; + +import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesGetter; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.net.Inet6Address; +import javax.annotation.Nullable; +import org.opensearch.client.Response; + +final class OpenSearchRestNetResponseAttributesGetter + implements NetClientAttributesGetter { + + @Override + public String transport(OpenSearchRestRequest request, Response response) { + return SemanticAttributes.NetTransportValues.IP_TCP; + } + + @Override + @Nullable + public String peerName(OpenSearchRestRequest request) { + return null; + } + + @Override + @Nullable + public Integer peerPort(OpenSearchRestRequest request) { + return null; + } + + @Nullable + @Override + public String sockFamily( + OpenSearchRestRequest elasticsearchRestRequest, @Nullable Response response) { + if (response != null && response.getHost().getAddress() instanceof Inet6Address) { + return "inet6"; + } + return null; + } + + @Override + @Nullable + public String sockPeerAddr(OpenSearchRestRequest request, @Nullable Response response) { + if (response != null && response.getHost().getAddress() != null) { + return response.getHost().getAddress().getHostAddress(); + } + return null; + } +} diff --git a/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestRequest.java b/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestRequest.java new file mode 100644 index 000000000000..856fdbf50d53 --- /dev/null +++ b/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestRequest.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opensearch.rest; + +import com.google.auto.value.AutoValue; + +@AutoValue +public abstract class OpenSearchRestRequest { + + public static OpenSearchRestRequest create(String method, String endpoint) { + return new AutoValue_OpenSearchRestRequest(method, endpoint); + } + + public abstract String getMethod(); + + public abstract String getOperation(); +} diff --git a/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/RestResponseListener.java b/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/RestResponseListener.java new file mode 100644 index 000000000000..fe3925aaa2cc --- /dev/null +++ b/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/RestResponseListener.java @@ -0,0 +1,50 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opensearch.rest; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import org.opensearch.client.Response; +import org.opensearch.client.ResponseListener; + +public final class RestResponseListener implements ResponseListener { + + private final ResponseListener listener; + private final Context parentContext; + private final Instrumenter instrumenter; + private final Context context; + private final OpenSearchRestRequest request; + + public RestResponseListener( + ResponseListener listener, + Context parentContext, + Instrumenter instrumenter, + Context context, + OpenSearchRestRequest request) { + this.listener = listener; + this.parentContext = parentContext; + this.instrumenter = instrumenter; + this.context = context; + this.request = request; + } + + @Override + public void onSuccess(Response response) { + instrumenter.end(context, request, response, null); + try (Scope ignored = parentContext.makeCurrent()) { + listener.onSuccess(response); + } + } + + @Override + public void onFailure(Exception e) { + instrumenter.end(context, request, null, e); + try (Scope ignored = parentContext.makeCurrent()) { + listener.onFailure(e); + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index e4c8b017ea50..db23f3668372 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -204,9 +204,12 @@ include(":instrumentation:dropwizard:dropwizard-metrics-4.0:javaagent") include(":instrumentation:dropwizard:dropwizard-views-0.7:javaagent") include(":instrumentation:dropwizard:dropwizard-testing") include(":instrumentation:elasticsearch:elasticsearch-rest-common:javaagent") +include(":instrumentation:opensearch:opensearch-rest-common:javaagent") include(":instrumentation:elasticsearch:elasticsearch-rest-5.0:javaagent") include(":instrumentation:elasticsearch:elasticsearch-rest-6.4:javaagent") include(":instrumentation:elasticsearch:elasticsearch-rest-7.0:javaagent") +include(":instrumentation:opensearch:opensearch-rest-1.0:javaagent") +include(":instrumentation:opensearch:opensearch-rest-2.0:javaagent") include(":instrumentation:elasticsearch:elasticsearch-transport-common:javaagent") include(":instrumentation:elasticsearch:elasticsearch-transport-common:testing") include(":instrumentation:elasticsearch:elasticsearch-transport-5.0:javaagent") From f1395bc92e633de3b0a809b36b570849b4cdfb15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Pelvet?= Date: Thu, 27 Oct 2022 17:38:58 +0200 Subject: [PATCH 02/11] Fix Muzzle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Pelvet --- .../opensearch/opensearch-rest-2.0/javaagent/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opensearch/opensearch-rest-2.0/javaagent/build.gradle.kts b/instrumentation/opensearch/opensearch-rest-2.0/javaagent/build.gradle.kts index b1d6007694a9..7e0e8c4bcee1 100644 --- a/instrumentation/opensearch/opensearch-rest-2.0/javaagent/build.gradle.kts +++ b/instrumentation/opensearch/opensearch-rest-2.0/javaagent/build.gradle.kts @@ -6,7 +6,7 @@ muzzle { pass { group.set("org.opensearch.client") module.set("opensearch-rest-client") - versions.set("[2.0,)") + versions.set("[1.0,)") assertInverse.set(true) } From e15ffe2d0ce9a15e232278ee970d44866742688d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Pelvet?= Date: Thu, 27 Oct 2022 18:50:29 +0200 Subject: [PATCH 03/11] Fix Muzzle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Pelvet --- .../javaagent/build.gradle.kts | 42 ---- .../OpenSearchRest2InstrumentationModule.java | 33 ---- .../rest/v2_0/OpenSearchRest2Singletons.java | 23 --- .../rest/v2_0/RestClientInstrumentation.java | 134 ------------- .../src/test/groovy/OpenSearchRestTest.groovy | 186 ------------------ settings.gradle.kts | 1 - 6 files changed, 419 deletions(-) delete mode 100644 instrumentation/opensearch/opensearch-rest-2.0/javaagent/build.gradle.kts delete mode 100644 instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/OpenSearchRest2InstrumentationModule.java delete mode 100644 instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/OpenSearchRest2Singletons.java delete mode 100644 instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/RestClientInstrumentation.java delete mode 100644 instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/test/groovy/OpenSearchRestTest.groovy diff --git a/instrumentation/opensearch/opensearch-rest-2.0/javaagent/build.gradle.kts b/instrumentation/opensearch/opensearch-rest-2.0/javaagent/build.gradle.kts deleted file mode 100644 index 7e0e8c4bcee1..000000000000 --- a/instrumentation/opensearch/opensearch-rest-2.0/javaagent/build.gradle.kts +++ /dev/null @@ -1,42 +0,0 @@ -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("(,)") - } -} - -dependencies { - library("org.opensearch.client:opensearch-rest-client:2.3.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") - - testCompileOnly("org.projectlombok:lombok:1.18.24") - testAnnotationProcessor("org.projectlombok:lombok:1.18.24") -} - -tasks { - test { - usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) - } -} diff --git a/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/OpenSearchRest2InstrumentationModule.java b/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/OpenSearchRest2InstrumentationModule.java deleted file mode 100644 index 392da4de6e21..000000000000 --- a/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/OpenSearchRest2InstrumentationModule.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.opensearch.rest.v2_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 OpenSearchRest2InstrumentationModule extends InstrumentationModule { - public OpenSearchRest2InstrumentationModule() { - super("opensearch-rest", "opensearch-rest-2.0", "opensearch"); - } - - @Override - public ElementMatcher.Junction classLoaderMatcher() { - // class introduced in 1.0.0 - return hasClassesNamed("org.opensearch.client.RestClient$InternalRequest"); - } - - @Override - public List typeInstrumentations() { - return singletonList(new RestClientInstrumentation()); - } -} diff --git a/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/OpenSearchRest2Singletons.java b/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/OpenSearchRest2Singletons.java deleted file mode 100644 index 87c7d5a839c2..000000000000 --- a/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/OpenSearchRest2Singletons.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.opensearch.rest.v2_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 OpenSearchRest2Singletons { - - private static final Instrumenter INSTRUMENTER = - OpenSearchRestInstrumenterFactory.create("io.opentelemetry.opensearch-rest-2.0"); - - public static Instrumenter instrumenter() { - return INSTRUMENTER; - } - - private OpenSearchRest2Singletons() {} -} diff --git a/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/RestClientInstrumentation.java b/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/RestClientInstrumentation.java deleted file mode 100644 index cbf6f44d1e72..000000000000 --- a/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v2_0/RestClientInstrumentation.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.opensearch.rest.v2_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 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 (!OpenSearchRest2Singletons.instrumenter().shouldStart(parentContext, otelRequest)) { - return; - } - - context = OpenSearchRest2Singletons.instrumenter().start(parentContext, otelRequest); - scope = context.makeCurrent(); - } - - @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void stopSpan( - @Advice.Return(readOnly = false) 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(); - - OpenSearchRest2Singletons.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 (!OpenSearchRest2Singletons.instrumenter().shouldStart(parentContext, otelRequest)) { - return; - } - - context = OpenSearchRest2Singletons.instrumenter().start(parentContext, otelRequest); - scope = context.makeCurrent(); - - responseListener = - new RestResponseListener( - responseListener, - parentContext, - OpenSearchRest2Singletons.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) { - OpenSearchRest2Singletons.instrumenter().end(context, otelRequest, null, throwable); - } - // span ended in RestResponseListener - } - } -} diff --git a/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/test/groovy/OpenSearchRestTest.groovy b/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/test/groovy/OpenSearchRestTest.groovy deleted file mode 100644 index 876fe9d93fcf..000000000000 --- a/instrumentation/opensearch/opensearch-rest-2.0/javaagent/src/test/groovy/OpenSearchRestTest.groovy +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import groovy.json.JsonSlurper -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -import org.apache.http.HttpHost -import org.apache.http.auth.AuthScope -import org.apache.http.auth.UsernamePasswordCredentials -import org.apache.http.client.CredentialsProvider -import org.apache.http.conn.ssl.TrustAllStrategy -import org.apache.http.impl.client.BasicCredentialsProvider -import org.apache.http.ssl.SSLContextBuilder -import org.apache.http.util.EntityUtils -import org.opensearch.client.Request -import org.opensearch.client.Response -import org.opensearch.client.ResponseListener -import org.opensearch.client.RestClient -import org.opensearch.testcontainers.OpensearchContainer -import org.testcontainers.utility.DockerImageName -import spock.lang.Shared - -import javax.net.ssl.SSLContext -import java.util.concurrent.CountDownLatch - -import static io.opentelemetry.api.trace.SpanKind.CLIENT -import static io.opentelemetry.api.trace.SpanKind.INTERNAL - -class OpenSearchRestTest extends AgentInstrumentationSpecification { - @Shared - OpensearchContainer opensearch - - @Shared - HttpHost httpHost - - @Shared - RestClient client - - def setupSpec() { - opensearch = new OpensearchContainer(DockerImageName.parse("opensearchproject/opensearch:2.3.0")).withSecurityEnabled() - // limit memory usage - opensearch.withEnv("OPENSEARCH_JAVA_OPTS", "-Xmx256m -Xms256m") - opensearch.start() - - final CredentialsProvider credentialsProvider = new BasicCredentialsProvider() - credentialsProvider.setCredentials(AuthScope.ANY, - new UsernamePasswordCredentials(opensearch.getUsername(), opensearch.getPassword())) - - final SSLContext sslContext = SSLContextBuilder.create() - .loadTrustMaterial(null, new TrustAllStrategy()) - .build() - - httpHost = HttpHost.create(opensearch.getHttpHostAddress()) - client = RestClient.builder(httpHost) - .setHttpClientConfigCallback(httpClientBuilder -> { - return httpClientBuilder - .setSSLContext(sslContext) - .setDefaultCredentialsProvider(credentialsProvider) - }) - .build() - } - - def cleanupSpec() { - opensearch.stop() - } - - def "test opensearch status"() { - setup: - Response response = client.performRequest(new Request("GET", "_cluster/health")) - - Map result = new JsonSlurper().parseText(EntityUtils.toString(response.entity)) - - expect: - result.status == "green" - - assertTraces(1) { - trace(0, 2) { - span(0) { - name "GET" - kind CLIENT - hasNoParent() - attributes { - "$SemanticAttributes.DB_SYSTEM" "opensearch" - "$SemanticAttributes.DB_OPERATION" "GET" - "$SemanticAttributes.DB_STATEMENT" "GET _cluster/health" - "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP - } - } - span(1) { - name "HTTP GET" - kind CLIENT - childOf span(0) - attributes { - "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP - "$SemanticAttributes.NET_PEER_NAME" httpHost.hostName - "$SemanticAttributes.NET_PEER_PORT" httpHost.port - "$SemanticAttributes.HTTP_METHOD" "GET" - "$SemanticAttributes.HTTP_FLAVOR" SemanticAttributes.HttpFlavorValues.HTTP_1_1 - "$SemanticAttributes.HTTP_URL" "${httpHost.toURI()}/_cluster/health" - "$SemanticAttributes.HTTP_STATUS_CODE" 200 - "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long - } - } - } - } - } - - def "test opensearch status async"() { - setup: - Response requestResponse = null - Exception exception = null - CountDownLatch countDownLatch = new CountDownLatch(1) - ResponseListener responseListener = new ResponseListener() { - @Override - void onSuccess(Response response) { - runWithSpan("callback") { - requestResponse = response - countDownLatch.countDown() - } - } - - @Override - void onFailure(Exception e) { - runWithSpan("callback") { - exception = e - countDownLatch.countDown() - } - } - } - runWithSpan("parent") { - client.performRequestAsync(new Request("GET", "_cluster/health"), responseListener) - } - countDownLatch.await() - - if (exception != null) { - throw exception - } - Map result = new JsonSlurper().parseText(EntityUtils.toString(requestResponse.entity)) as Map - - expect: - result.status == "green" - - assertTraces(1) { - trace(0, 4) { - span(0) { - name "parent" - kind INTERNAL - hasNoParent() - } - span(1) { - name "GET" - kind CLIENT - childOf(span(0)) - attributes { - "$SemanticAttributes.DB_SYSTEM" "opensearch" - "$SemanticAttributes.DB_OPERATION" "GET" - "$SemanticAttributes.DB_STATEMENT" "GET _cluster/health" - "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP - } - } - span(2) { - name "HTTP GET" - kind CLIENT - childOf span(1) - attributes { - "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP - "$SemanticAttributes.NET_PEER_NAME" httpHost.hostName - "$SemanticAttributes.NET_PEER_PORT" httpHost.port - "$SemanticAttributes.HTTP_METHOD" "GET" - "$SemanticAttributes.HTTP_FLAVOR" SemanticAttributes.HttpFlavorValues.HTTP_1_1 - "$SemanticAttributes.HTTP_URL" "${httpHost.toURI()}/_cluster/health" - "$SemanticAttributes.HTTP_STATUS_CODE" 200 - "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" 449 - } - } - span(3) { - name "callback" - kind INTERNAL - childOf(span(0)) - } - } - } - } -} diff --git a/settings.gradle.kts b/settings.gradle.kts index db23f3668372..6da2fd747b6c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -209,7 +209,6 @@ include(":instrumentation:elasticsearch:elasticsearch-rest-5.0:javaagent") include(":instrumentation:elasticsearch:elasticsearch-rest-6.4:javaagent") include(":instrumentation:elasticsearch:elasticsearch-rest-7.0:javaagent") include(":instrumentation:opensearch:opensearch-rest-1.0:javaagent") -include(":instrumentation:opensearch:opensearch-rest-2.0:javaagent") include(":instrumentation:elasticsearch:elasticsearch-transport-common:javaagent") include(":instrumentation:elasticsearch:elasticsearch-transport-common:testing") include(":instrumentation:elasticsearch:elasticsearch-transport-5.0:javaagent") From 4599c7d85de3a36d20d56c34560739403d6ec6aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Pelvet?= Date: Fri, 28 Oct 2022 21:23:42 +0200 Subject: [PATCH 04/11] Set minimum Java version to 11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Pelvet --- .../opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts index d8334967181f..dca1fd233c1c 100644 --- a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts +++ b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts @@ -17,6 +17,10 @@ muzzle { } } +otelJava { + minJavaVersionSupported.set(JavaVersion.VERSION_11) +} + dependencies { library("org.opensearch.client:opensearch-rest-client:1.3.6") From f2c170a6823427dee06d99afe80efb8ffdd01d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Pelvet?= Date: Fri, 4 Nov 2022 09:35:10 +0100 Subject: [PATCH 05/11] Remove orphan lombok usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Pelvet --- .../opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts | 3 --- 1 file changed, 3 deletions(-) diff --git a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts index dca1fd233c1c..b7a5bfb87e61 100644 --- a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts +++ b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts @@ -34,9 +34,6 @@ dependencies { 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") - - testCompileOnly("org.projectlombok:lombok:1.18.24") - testAnnotationProcessor("org.projectlombok:lombok:1.18.24") } tasks { From a2e8bb857888c8f02604c125fb4e5b27e5c73c88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Pelvet?= Date: Fri, 4 Nov 2022 09:35:40 +0100 Subject: [PATCH 06/11] Remove version number from class name to be more consistent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Pelvet --- ...va => OpenSearchRestInstrumentationModule.java} | 4 ++-- ...ngletons.java => OpenSearchRestSingletons.java} | 4 ++-- .../rest/v1_0/RestClientInstrumentation.java | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) rename instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/{OpenSearchRest1InstrumentationModule.java => OpenSearchRestInstrumentationModule.java} (88%) rename instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/{OpenSearchRest1Singletons.java => OpenSearchRestSingletons.java} (89%) diff --git a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRest1InstrumentationModule.java b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRestInstrumentationModule.java similarity index 88% rename from instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRest1InstrumentationModule.java rename to instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRestInstrumentationModule.java index 73eb8246d56d..78b372a914b6 100644 --- a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRest1InstrumentationModule.java +++ b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRestInstrumentationModule.java @@ -15,8 +15,8 @@ import net.bytebuddy.matcher.ElementMatcher; @AutoService(InstrumentationModule.class) -public class OpenSearchRest1InstrumentationModule extends InstrumentationModule { - public OpenSearchRest1InstrumentationModule() { +public class OpenSearchRestInstrumentationModule extends InstrumentationModule { + public OpenSearchRestInstrumentationModule() { super("opensearch-rest", "opensearch-rest-1.0", "opensearch"); } diff --git a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRest1Singletons.java b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRestSingletons.java similarity index 89% rename from instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRest1Singletons.java rename to instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRestSingletons.java index 8ea9ac82e454..455e40f26b14 100644 --- a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRest1Singletons.java +++ b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/OpenSearchRestSingletons.java @@ -10,7 +10,7 @@ import io.opentelemetry.javaagent.instrumentation.opensearch.rest.OpenSearchRestRequest; import org.opensearch.client.Response; -public final class OpenSearchRest1Singletons { +public final class OpenSearchRestSingletons { private static final Instrumenter INSTRUMENTER = OpenSearchRestInstrumenterFactory.create("io.opentelemetry.opensearch-rest-1.0"); @@ -19,5 +19,5 @@ public static Instrumenter instrumenter() { return INSTRUMENTER; } - private OpenSearchRest1Singletons() {} + private OpenSearchRestSingletons() {} } diff --git a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/RestClientInstrumentation.java b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/RestClientInstrumentation.java index 9342af899894..9fc51dfa4e10 100644 --- a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/RestClientInstrumentation.java +++ b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/RestClientInstrumentation.java @@ -59,11 +59,11 @@ public static void onEnter( Context parentContext = currentContext(); otelRequest = OpenSearchRestRequest.create(request.getMethod(), request.getEndpoint()); - if (!OpenSearchRest1Singletons.instrumenter().shouldStart(parentContext, otelRequest)) { + if (!OpenSearchRestSingletons.instrumenter().shouldStart(parentContext, otelRequest)) { return; } - context = OpenSearchRest1Singletons.instrumenter().start(parentContext, otelRequest); + context = OpenSearchRestSingletons.instrumenter().start(parentContext, otelRequest); scope = context.makeCurrent(); } @@ -80,7 +80,7 @@ public static void stopSpan( } scope.close(); - OpenSearchRest1Singletons.instrumenter().end(context, otelRequest, response, throwable); + OpenSearchRestSingletons.instrumenter().end(context, otelRequest, response, throwable); } } @@ -97,18 +97,18 @@ public static void onEnter( Context parentContext = currentContext(); otelRequest = OpenSearchRestRequest.create(request.getMethod(), request.getEndpoint()); - if (!OpenSearchRest1Singletons.instrumenter().shouldStart(parentContext, otelRequest)) { + if (!OpenSearchRestSingletons.instrumenter().shouldStart(parentContext, otelRequest)) { return; } - context = OpenSearchRest1Singletons.instrumenter().start(parentContext, otelRequest); + context = OpenSearchRestSingletons.instrumenter().start(parentContext, otelRequest); scope = context.makeCurrent(); responseListener = new RestResponseListener( responseListener, parentContext, - OpenSearchRest1Singletons.instrumenter(), + OpenSearchRestSingletons.instrumenter(), context, otelRequest); } @@ -126,7 +126,7 @@ public static void stopSpan( scope.close(); if (throwable != null) { - OpenSearchRest1Singletons.instrumenter().end(context, otelRequest, null, throwable); + OpenSearchRestSingletons.instrumenter().end(context, otelRequest, null, throwable); } // span ended in RestResponseListener } From 3e034df8e98b2081283c089e210435f18d7df2ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Pelvet?= Date: Fri, 4 Nov 2022 09:39:49 +0100 Subject: [PATCH 07/11] Update OpenSearch client version to 1.3.6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Pelvet --- .../opensearch-rest-common/javaagent/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opensearch/opensearch-rest-common/javaagent/build.gradle.kts b/instrumentation/opensearch/opensearch-rest-common/javaagent/build.gradle.kts index 746b735e849d..5053d30b7a9f 100644 --- a/instrumentation/opensearch/opensearch-rest-common/javaagent/build.gradle.kts +++ b/instrumentation/opensearch/opensearch-rest-common/javaagent/build.gradle.kts @@ -3,7 +3,7 @@ plugins { } dependencies { - compileOnly("org.opensearch.client:opensearch-rest-client:1.3.4") + compileOnly("org.opensearch.client:opensearch-rest-client:1.3.6") compileOnly("com.google.auto.value:auto-value-annotations") annotationProcessor("com.google.auto.value:auto-value") From 56b9475c9bdf939bee3b3e21d128d6ac894baeb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Pelvet?= Date: Fri, 4 Nov 2022 15:49:30 +0100 Subject: [PATCH 08/11] Downgrade the `library` dependency for `org.opensearch.client:opensearch-rest-client` to version `1.0.0` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Pelvet --- .../opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts index b7a5bfb87e61..460f5ec15e60 100644 --- a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts +++ b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts @@ -22,7 +22,7 @@ otelJava { } dependencies { - library("org.opensearch.client:opensearch-rest-client:1.3.6") + library("org.opensearch.client:opensearch-rest-client:1.0.0") implementation(project(":instrumentation:opensearch:opensearch-rest-common:javaagent")) From 66d997223aac39f84b8078d00e413523e6df9779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Pelvet?= Date: Tue, 8 Nov 2022 12:46:43 +0100 Subject: [PATCH 09/11] Change integration test from Groovy to Java. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Pelvet --- .../src/test/groovy/OpenSearchRestTest.groovy | 186 ------------------ .../src/test/java/OpenSearchRestTest.java | 174 ++++++++++++++++ 2 files changed, 174 insertions(+), 186 deletions(-) delete mode 100644 instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/test/groovy/OpenSearchRestTest.groovy create mode 100644 instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/test/java/OpenSearchRestTest.java diff --git a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/test/groovy/OpenSearchRestTest.groovy b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/test/groovy/OpenSearchRestTest.groovy deleted file mode 100644 index 8f77969b0058..000000000000 --- a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/test/groovy/OpenSearchRestTest.groovy +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import groovy.json.JsonSlurper -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -import org.apache.http.HttpHost -import org.apache.http.auth.AuthScope -import org.apache.http.auth.UsernamePasswordCredentials -import org.apache.http.client.CredentialsProvider -import org.apache.http.conn.ssl.TrustAllStrategy -import org.apache.http.impl.client.BasicCredentialsProvider -import org.apache.http.ssl.SSLContextBuilder -import org.apache.http.util.EntityUtils -import org.opensearch.client.Request -import org.opensearch.client.Response -import org.opensearch.client.ResponseListener -import org.opensearch.client.RestClient -import org.opensearch.testcontainers.OpensearchContainer -import org.testcontainers.utility.DockerImageName -import spock.lang.Shared - -import javax.net.ssl.SSLContext -import java.util.concurrent.CountDownLatch - -import static io.opentelemetry.api.trace.SpanKind.CLIENT -import static io.opentelemetry.api.trace.SpanKind.INTERNAL - -class OpenSearchRestTest extends AgentInstrumentationSpecification { - @Shared - OpensearchContainer opensearch - - @Shared - HttpHost httpHost - - @Shared - RestClient client - - def setupSpec() { - opensearch = new OpensearchContainer(DockerImageName.parse("opensearchproject/opensearch:1.3.6")).withSecurityEnabled() - // limit memory usage - opensearch.withEnv("OPENSEARCH_JAVA_OPTS", "-Xmx256m -Xms256m") - opensearch.start() - - final CredentialsProvider credentialsProvider = new BasicCredentialsProvider() - credentialsProvider.setCredentials(AuthScope.ANY, - new UsernamePasswordCredentials(opensearch.getUsername(), opensearch.getPassword())) - - final SSLContext sslContext = SSLContextBuilder.create() - .loadTrustMaterial(null, new TrustAllStrategy()) - .build() - - httpHost = HttpHost.create(opensearch.getHttpHostAddress()) - client = RestClient.builder(httpHost) - .setHttpClientConfigCallback(httpClientBuilder -> { - return httpClientBuilder - .setSSLContext(sslContext) - .setDefaultCredentialsProvider(credentialsProvider) - }) - .build() - } - - def cleanupSpec() { - opensearch.stop() - } - - def "test opensearch status"() { - setup: - Response response = client.performRequest(new Request("GET", "_cluster/health")) - - Map result = new JsonSlurper().parseText(EntityUtils.toString(response.entity)) - - expect: - result.status == "green" - - assertTraces(1) { - trace(0, 2) { - span(0) { - name "GET" - kind CLIENT - hasNoParent() - attributes { - "$SemanticAttributes.DB_SYSTEM" "opensearch" - "$SemanticAttributes.DB_OPERATION" "GET" - "$SemanticAttributes.DB_STATEMENT" "GET _cluster/health" - "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP - } - } - span(1) { - name "HTTP GET" - kind CLIENT - childOf span(0) - attributes { - "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP - "$SemanticAttributes.NET_PEER_NAME" httpHost.hostName - "$SemanticAttributes.NET_PEER_PORT" httpHost.port - "$SemanticAttributes.HTTP_METHOD" "GET" - "$SemanticAttributes.HTTP_FLAVOR" SemanticAttributes.HttpFlavorValues.HTTP_1_1 - "$SemanticAttributes.HTTP_URL" "${httpHost.toURI()}/_cluster/health" - "$SemanticAttributes.HTTP_STATUS_CODE" 200 - "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long - } - } - } - } - } - - def "test opensearch status async"() { - setup: - Response requestResponse = null - Exception exception = null - CountDownLatch countDownLatch = new CountDownLatch(1) - ResponseListener responseListener = new ResponseListener() { - @Override - void onSuccess(Response response) { - runWithSpan("callback") { - requestResponse = response - countDownLatch.countDown() - } - } - - @Override - void onFailure(Exception e) { - runWithSpan("callback") { - exception = e - countDownLatch.countDown() - } - } - } - runWithSpan("parent") { - client.performRequestAsync(new Request("GET", "_cluster/health"), responseListener) - } - countDownLatch.await() - - if (exception != null) { - throw exception - } - Map result = new JsonSlurper().parseText(EntityUtils.toString(requestResponse.entity)) as Map - - expect: - result.status == "green" - - assertTraces(1) { - trace(0, 4) { - span(0) { - name "parent" - kind INTERNAL - hasNoParent() - } - span(1) { - name "GET" - kind CLIENT - childOf(span(0)) - attributes { - "$SemanticAttributes.DB_SYSTEM" "opensearch" - "$SemanticAttributes.DB_OPERATION" "GET" - "$SemanticAttributes.DB_STATEMENT" "GET _cluster/health" - "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP - } - } - span(2) { - name "HTTP GET" - kind CLIENT - childOf span(1) - attributes { - "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP - "$SemanticAttributes.NET_PEER_NAME" httpHost.hostName - "$SemanticAttributes.NET_PEER_PORT" httpHost.port - "$SemanticAttributes.HTTP_METHOD" "GET" - "$SemanticAttributes.HTTP_FLAVOR" SemanticAttributes.HttpFlavorValues.HTTP_1_1 - "$SemanticAttributes.HTTP_URL" "${httpHost.toURI()}/_cluster/health" - "$SemanticAttributes.HTTP_STATUS_CODE" 200 - "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" 415 - } - } - span(3) { - name "callback" - kind INTERNAL - childOf(span(0)) - } - } - } - } -} diff --git a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/test/java/OpenSearchRestTest.java b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/test/java/OpenSearchRestTest.java new file mode 100644 index 000000000000..c060df292a4c --- /dev/null +++ b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/test/java/OpenSearchRestTest.java @@ -0,0 +1,174 @@ +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; +import javax.net.ssl.SSLContext; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.conn.ssl.TrustAllStrategy; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.ssl.SSLContextBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.opensearch.client.Request; +import org.opensearch.client.Response; +import org.opensearch.client.ResponseListener; +import org.opensearch.client.RestClient; +import org.opensearch.testcontainers.OpensearchContainer; +import org.testcontainers.utility.DockerImageName; + +public class OpenSearchRestTest { + @RegisterExtension + static final AgentInstrumentationExtension testing = AgentInstrumentationExtension.create(); + + static OpensearchContainer opensearch; + static RestClient client; + + static HttpHost httpHost; + + @BeforeAll + static void setUpOpenSearch() + throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { + + opensearch = new OpensearchContainer( + DockerImageName.parse("opensearchproject/opensearch:1.3.6")).withSecurityEnabled(); + // limit memory usage + opensearch.withEnv("OPENSEARCH_JAVA_OPTS", "-Xmx256m -Xms256m"); + opensearch.start(); + + CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(opensearch.getUsername(), opensearch.getPassword())); + + SSLContext sslContext = SSLContextBuilder.create() + .loadTrustMaterial(null, new TrustAllStrategy()) + .build(); + + httpHost = HttpHost.create(opensearch.getHttpHostAddress()); + client = RestClient.builder(httpHost) + .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder + .setSSLContext(sslContext) + .setDefaultCredentialsProvider(credentialsProvider)) + .build(); + } + + @AfterAll + static void tearDownOpenSearch() { + opensearch.stop(); + } + + @Test + void shouldGetStatusWithTraces() throws IOException { + + client.performRequest(new Request("GET", "_cluster/health")); + + testing.waitAndAssertTraces( + trace -> trace.hasSpansSatisfyingExactly( + span -> span.hasName("GET") + .hasKind(SpanKind.CLIENT) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.DB_SYSTEM, "opensearch"), + equalTo(SemanticAttributes.DB_OPERATION, "GET"), + equalTo(SemanticAttributes.DB_STATEMENT, "GET _cluster/health"), + equalTo(SemanticAttributes.NET_TRANSPORT, + SemanticAttributes.NetTransportValues.IP_TCP) + ), + span -> span.hasName("HTTP GET") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.NET_TRANSPORT, + SemanticAttributes.NetTransportValues.IP_TCP), + equalTo(SemanticAttributes.NET_PEER_NAME, httpHost.getHostName()), + equalTo(SemanticAttributes.NET_PEER_PORT, httpHost.getPort()), + equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo(SemanticAttributes.HTTP_FLAVOR, + SemanticAttributes.HttpFlavorValues.HTTP_1_1), + equalTo(SemanticAttributes.HTTP_URL, httpHost.toURI() + "/_cluster/health"), + equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200L), + equalTo(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, 415L) + ) + ) + ); + } + + @Test + void shouldGetStatusAsyncWithTraces() throws Exception { + AtomicReference requestResponse = new AtomicReference<>(null); + AtomicReference exception = new AtomicReference<>(null); + CountDownLatch countDownLatch = new CountDownLatch(1); + + ResponseListener responseListener = new ResponseListener() { + @Override + public void onSuccess(Response response) { + testing.runWithSpan("callback", () -> { + requestResponse.set(response); + countDownLatch.countDown(); + }); + } + + @Override + public void onFailure(Exception e) { + testing.runWithSpan("callback", () -> { + exception.set(e); + countDownLatch.countDown(); + }); + } + }; + + testing.runWithSpan("client", () -> { + client.performRequestAsync(new Request("GET", "_cluster/health"), responseListener); + }); + countDownLatch.await(); + + if (exception.get() != null) { + throw exception.get(); + } + + testing.waitAndAssertTraces( + trace -> trace.hasSpansSatisfyingExactly( + span -> span.hasName("client") + .hasKind(SpanKind.INTERNAL), + span -> span.hasName("GET") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.DB_SYSTEM, "opensearch"), + equalTo(SemanticAttributes.DB_OPERATION, "GET"), + equalTo(SemanticAttributes.DB_STATEMENT, "GET _cluster/health"), + equalTo(SemanticAttributes.NET_TRANSPORT, + SemanticAttributes.NetTransportValues.IP_TCP) + ), + span -> span.hasName("HTTP GET") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.NET_TRANSPORT, + SemanticAttributes.NetTransportValues.IP_TCP), + equalTo(SemanticAttributes.NET_PEER_NAME, httpHost.getHostName()), + equalTo(SemanticAttributes.NET_PEER_PORT, httpHost.getPort()), + equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo(SemanticAttributes.HTTP_FLAVOR, + SemanticAttributes.HttpFlavorValues.HTTP_1_1), + equalTo(SemanticAttributes.HTTP_URL, httpHost.toURI() + "/_cluster/health"), + equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200L), + equalTo(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, 415L) + ), + span -> span.hasName("callback") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)) + ) + ); + } +} From 27e8a4e268dbd1725dd186a778afd0f9dc2c7ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Pelvet?= Date: Tue, 8 Nov 2022 12:47:11 +0100 Subject: [PATCH 10/11] Fix annotation on return value. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Pelvet --- .../opensearch/rest/v1_0/RestClientInstrumentation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/RestClientInstrumentation.java b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/RestClientInstrumentation.java index 9fc51dfa4e10..85748ea152f1 100644 --- a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/RestClientInstrumentation.java +++ b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/v1_0/RestClientInstrumentation.java @@ -69,7 +69,7 @@ public static void onEnter( @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( - @Advice.Return(readOnly = false) Response response, + @Advice.Return Response response, @Advice.Thrown Throwable throwable, @Advice.Local("otelRequest") OpenSearchRestRequest otelRequest, @Advice.Local("otelContext") Context context, From ead5d22a086c065047d9e9bc52af7a4f50443035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Pelvet?= Date: Tue, 8 Nov 2022 12:56:31 +0100 Subject: [PATCH 11/11] Fix spotless issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Pelvet --- .../src/test/java/OpenSearchRestTest.java | 204 ++++++++++-------- 1 file changed, 113 insertions(+), 91 deletions(-) diff --git a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/test/java/OpenSearchRestTest.java b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/test/java/OpenSearchRestTest.java index c060df292a4c..70e183364b6b 100644 --- a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/test/java/OpenSearchRestTest.java +++ b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/src/test/java/OpenSearchRestTest.java @@ -1,3 +1,8 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import io.opentelemetry.api.trace.SpanKind; @@ -41,26 +46,30 @@ public class OpenSearchRestTest { static void setUpOpenSearch() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { - opensearch = new OpensearchContainer( - DockerImageName.parse("opensearchproject/opensearch:1.3.6")).withSecurityEnabled(); + opensearch = + new OpensearchContainer(DockerImageName.parse("opensearchproject/opensearch:1.3.6")) + .withSecurityEnabled(); // limit memory usage opensearch.withEnv("OPENSEARCH_JAVA_OPTS", "-Xmx256m -Xms256m"); opensearch.start(); CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(AuthScope.ANY, + credentialsProvider.setCredentials( + AuthScope.ANY, new UsernamePasswordCredentials(opensearch.getUsername(), opensearch.getPassword())); - SSLContext sslContext = SSLContextBuilder.create() - .loadTrustMaterial(null, new TrustAllStrategy()) - .build(); + SSLContext sslContext = + SSLContextBuilder.create().loadTrustMaterial(null, new TrustAllStrategy()).build(); httpHost = HttpHost.create(opensearch.getHttpHostAddress()); - client = RestClient.builder(httpHost) - .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder - .setSSLContext(sslContext) - .setDefaultCredentialsProvider(credentialsProvider)) - .build(); + client = + RestClient.builder(httpHost) + .setHttpClientConfigCallback( + httpClientBuilder -> + httpClientBuilder + .setSSLContext(sslContext) + .setDefaultCredentialsProvider(credentialsProvider)) + .build(); } @AfterAll @@ -74,33 +83,36 @@ void shouldGetStatusWithTraces() throws IOException { client.performRequest(new Request("GET", "_cluster/health")); testing.waitAndAssertTraces( - trace -> trace.hasSpansSatisfyingExactly( - span -> span.hasName("GET") - .hasKind(SpanKind.CLIENT) - .hasAttributesSatisfyingExactly( - equalTo(SemanticAttributes.DB_SYSTEM, "opensearch"), - equalTo(SemanticAttributes.DB_OPERATION, "GET"), - equalTo(SemanticAttributes.DB_STATEMENT, "GET _cluster/health"), - equalTo(SemanticAttributes.NET_TRANSPORT, - SemanticAttributes.NetTransportValues.IP_TCP) - ), - span -> span.hasName("HTTP GET") - .hasKind(SpanKind.CLIENT) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(SemanticAttributes.NET_TRANSPORT, - SemanticAttributes.NetTransportValues.IP_TCP), - equalTo(SemanticAttributes.NET_PEER_NAME, httpHost.getHostName()), - equalTo(SemanticAttributes.NET_PEER_PORT, httpHost.getPort()), - equalTo(SemanticAttributes.HTTP_METHOD, "GET"), - equalTo(SemanticAttributes.HTTP_FLAVOR, - SemanticAttributes.HttpFlavorValues.HTTP_1_1), - equalTo(SemanticAttributes.HTTP_URL, httpHost.toURI() + "/_cluster/health"), - equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200L), - equalTo(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, 415L) - ) - ) - ); + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("GET") + .hasKind(SpanKind.CLIENT) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.DB_SYSTEM, "opensearch"), + equalTo(SemanticAttributes.DB_OPERATION, "GET"), + equalTo(SemanticAttributes.DB_STATEMENT, "GET _cluster/health"), + equalTo( + SemanticAttributes.NET_TRANSPORT, + SemanticAttributes.NetTransportValues.IP_TCP)), + span -> + span.hasName("HTTP GET") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo( + SemanticAttributes.NET_TRANSPORT, + SemanticAttributes.NetTransportValues.IP_TCP), + equalTo(SemanticAttributes.NET_PEER_NAME, httpHost.getHostName()), + equalTo(SemanticAttributes.NET_PEER_PORT, httpHost.getPort()), + equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo( + SemanticAttributes.HTTP_FLAVOR, + SemanticAttributes.HttpFlavorValues.HTTP_1_1), + equalTo( + SemanticAttributes.HTTP_URL, httpHost.toURI() + "/_cluster/health"), + equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200L), + equalTo(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, 415L)))); } @Test @@ -109,27 +121,34 @@ void shouldGetStatusAsyncWithTraces() throws Exception { AtomicReference exception = new AtomicReference<>(null); CountDownLatch countDownLatch = new CountDownLatch(1); - ResponseListener responseListener = new ResponseListener() { - @Override - public void onSuccess(Response response) { - testing.runWithSpan("callback", () -> { - requestResponse.set(response); - countDownLatch.countDown(); - }); - } - - @Override - public void onFailure(Exception e) { - testing.runWithSpan("callback", () -> { - exception.set(e); - countDownLatch.countDown(); + ResponseListener responseListener = + new ResponseListener() { + @Override + public void onSuccess(Response response) { + testing.runWithSpan( + "callback", + () -> { + requestResponse.set(response); + countDownLatch.countDown(); + }); + } + + @Override + public void onFailure(Exception e) { + testing.runWithSpan( + "callback", + () -> { + exception.set(e); + countDownLatch.countDown(); + }); + } + }; + + testing.runWithSpan( + "client", + () -> { + client.performRequestAsync(new Request("GET", "_cluster/health"), responseListener); }); - } - }; - - testing.runWithSpan("client", () -> { - client.performRequestAsync(new Request("GET", "_cluster/health"), responseListener); - }); countDownLatch.await(); if (exception.get() != null) { @@ -137,38 +156,41 @@ public void onFailure(Exception e) { } testing.waitAndAssertTraces( - trace -> trace.hasSpansSatisfyingExactly( - span -> span.hasName("client") - .hasKind(SpanKind.INTERNAL), - span -> span.hasName("GET") - .hasKind(SpanKind.CLIENT) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(SemanticAttributes.DB_SYSTEM, "opensearch"), - equalTo(SemanticAttributes.DB_OPERATION, "GET"), - equalTo(SemanticAttributes.DB_STATEMENT, "GET _cluster/health"), - equalTo(SemanticAttributes.NET_TRANSPORT, - SemanticAttributes.NetTransportValues.IP_TCP) - ), - span -> span.hasName("HTTP GET") - .hasKind(SpanKind.CLIENT) - .hasParent(trace.getSpan(1)) - .hasAttributesSatisfyingExactly( - equalTo(SemanticAttributes.NET_TRANSPORT, - SemanticAttributes.NetTransportValues.IP_TCP), - equalTo(SemanticAttributes.NET_PEER_NAME, httpHost.getHostName()), - equalTo(SemanticAttributes.NET_PEER_PORT, httpHost.getPort()), - equalTo(SemanticAttributes.HTTP_METHOD, "GET"), - equalTo(SemanticAttributes.HTTP_FLAVOR, - SemanticAttributes.HttpFlavorValues.HTTP_1_1), - equalTo(SemanticAttributes.HTTP_URL, httpHost.toURI() + "/_cluster/health"), - equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200L), - equalTo(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, 415L) - ), - span -> span.hasName("callback") - .hasKind(SpanKind.INTERNAL) - .hasParent(trace.getSpan(0)) - ) - ); + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("client").hasKind(SpanKind.INTERNAL), + span -> + span.hasName("GET") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.DB_SYSTEM, "opensearch"), + equalTo(SemanticAttributes.DB_OPERATION, "GET"), + equalTo(SemanticAttributes.DB_STATEMENT, "GET _cluster/health"), + equalTo( + SemanticAttributes.NET_TRANSPORT, + SemanticAttributes.NetTransportValues.IP_TCP)), + span -> + span.hasName("HTTP GET") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly( + equalTo( + SemanticAttributes.NET_TRANSPORT, + SemanticAttributes.NetTransportValues.IP_TCP), + equalTo(SemanticAttributes.NET_PEER_NAME, httpHost.getHostName()), + equalTo(SemanticAttributes.NET_PEER_PORT, httpHost.getPort()), + equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo( + SemanticAttributes.HTTP_FLAVOR, + SemanticAttributes.HttpFlavorValues.HTTP_1_1), + equalTo( + SemanticAttributes.HTTP_URL, httpHost.toURI() + "/_cluster/health"), + equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200L), + equalTo(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, 415L)), + span -> + span.hasName("callback") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)))); } }