Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ES client 8.x instrumentation #3157

Merged
merged 12 commits into from Jul 26, 2023
5 changes: 5 additions & 0 deletions apm-agent-builds/pom.xml
Expand Up @@ -97,6 +97,11 @@
<artifactId>apm-es-restclient-plugin-7_x</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>apm-es-restclient-plugin-8_x</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>apm-grails-plugin</artifactId>
Expand Down
Expand Up @@ -19,8 +19,8 @@
package co.elastic.apm.agent.esrestclient.v5_6;

import co.elastic.apm.agent.esrestclient.AbstractEsClientInstrumentationTest;
import co.elastic.apm.agent.tracer.Outcome;
import co.elastic.apm.agent.impl.transaction.Span;
import co.elastic.apm.agent.tracer.Outcome;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
Expand Down Expand Up @@ -65,14 +65,13 @@
@RunWith(Parameterized.class)
public class ElasticsearchRestClientInstrumentationIT extends AbstractEsClientInstrumentationTest {

private static final String ELASTICSEARCH_CONTAINER_VERSION = "docker.elastic.co/elasticsearch/elasticsearch:5.6.0";
protected static final String USER_NAME = "elastic";
protected static final String PASSWORD = "changeme";

protected static final String DOC_TYPE = "doc";
private static RestHighLevelClient client;
private static final String ELASTICSEARCH_CONTAINER_VERSION = "docker.elastic.co/elasticsearch/elasticsearch:5.6.0";
@SuppressWarnings("NullableProblems")
protected static RestClient lowLevelClient;
private static RestHighLevelClient client;

public ElasticsearchRestClientInstrumentationIT(boolean async) {
this.async = async;
Expand All @@ -86,7 +85,7 @@ public static void startElasticsearchContainerAndClient() throws IOException {
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(USER_NAME, PASSWORD));

RestClientBuilder builder = RestClient.builder(HttpHost.create(container.getHttpHostAddress()))
RestClientBuilder builder = RestClient.builder(HttpHost.create(container.getHttpHostAddress()))
.setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
lowLevelClient = builder.build();
client = new RestHighLevelClient(lowLevelClient);
Expand Down Expand Up @@ -124,14 +123,14 @@ public void testCreateAndDeleteIndex() throws IOException, ExecutionException {
// Create an Index
doPerformRequest("PUT", "/" + SECOND_INDEX);

validateSpanContentAfterIndexCreateRequest();
validateSpanContentAfterIndexCreateRequest(false);

// Delete the index
reporter.reset();

doPerformRequest("DELETE", "/" + SECOND_INDEX);

validateSpanContentAfterIndexDeleteRequest();
validateSpanContentAfterIndexDeleteRequest(false);

assertThat(reporter.getFirstSpan().getOutcome()).isEqualTo(Outcome.SUCCESS);
}
Expand Down Expand Up @@ -187,8 +186,8 @@ public void testDocumentScenario() throws IOException, ExecutionException, Inter
spans = reporter.getSpans();
assertThat(spans).hasSize(2);
boolean updateSpanFound = false;
for(Span span: spans) {
if(span.getNameAsString().contains("_update")) {
for (Span span : spans) {
if (span.getNameAsString().contains("_update")) {
updateSpanFound = true;
break;
}
Expand All @@ -215,10 +214,6 @@ public void testScenarioAsBulkRequest() throws IOException, ExecutionException,
validateSpanContentAfterBulkRequest();
}

private interface ClientMethod<Req, Res> {
void invoke(Req request, ActionListener<Res> listener);
}

private <Req, Res> Res invokeAsync(Req request, ClientMethod<Req, Res> method) throws InterruptedException, ExecutionException {
final CompletableFuture<Res> resultFuture = new CompletableFuture<>();
method.invoke(request, new ActionListener<>() {
Expand Down Expand Up @@ -280,7 +275,6 @@ private BulkResponse doBulk(BulkRequest bulkRequest) throws IOException, Executi
return client.bulk(bulkRequest);
}


private Response doPerformRequest(String method, String path) throws IOException, ExecutionException {
if (async) {
final CompletableFuture<Response> resultFuture = new CompletableFuture<>();
Expand All @@ -304,4 +298,9 @@ public void onFailure(Exception exception) {
return lowLevelClient.performRequest(method, path);
}


private interface ClientMethod<Req, Res> {
void invoke(Req request, ActionListener<Res> listener);
}

}
Expand Up @@ -66,7 +66,7 @@ public static class ElasticsearchRestClientAsyncAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class, inline = false)
public static Object[] onBeforeExecute(@Advice.Argument(0) Request request,
@Advice.Argument(1) ResponseListener responseListener) {
Span<?> span = helper.createClientSpan(request.getMethod(), request.getEndpoint(), request.getEntity());
Span<?> span = helper.createClientSpan(request, request.getMethod(), request.getEndpoint(), request.getEntity());
if (span != null) {
Object[] ret = new Object[2];
ret[0] = span;
Expand Down
Expand Up @@ -60,7 +60,7 @@ public static class ElasticsearchRestClientSyncAdvice {
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class, inline = false)
public static Object onBeforeExecute(@Advice.Argument(0) Request request) {
return helper.createClientSpan(request.getMethod(), request.getEndpoint(), request.getEntity());
return helper.createClientSpan(request, request.getMethod(), request.getEndpoint(), request.getEntity());
}

@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false)
Expand Down
Expand Up @@ -76,13 +76,13 @@ public void testCreateAndDeleteIndex() throws IOException, ExecutionException, I
// Create an Index
doCreateIndex(new CreateIndexRequest(SECOND_INDEX));

validateSpanContentAfterIndexCreateRequest();
validateSpanContentAfterIndexCreateRequest(false);
// Delete the index
reporter.reset();

doDeleteIndex(new DeleteIndexRequest(SECOND_INDEX));

validateSpanContentAfterIndexDeleteRequest();
validateSpanContentAfterIndexDeleteRequest(false);
}

@Test
Expand Down
Expand Up @@ -7,11 +7,11 @@
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>apm-es-api-client-test</artifactId>
<artifactId>apm-es-restclient-plugin-8_x</artifactId>
<name>${project.groupId}:${project.artifactId}</name>

<properties>
<version.elasticsearch-java>8.7.1</version.elasticsearch-java>
<version.elasticsearch-java>8.8.0</version.elasticsearch-java>
<apm-agent-parent.base.dir>${project.basedir}/../../..</apm-agent-parent.base.dir>
</properties>

Expand All @@ -25,6 +25,7 @@
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>${version.elasticsearch-java}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
JonasKunz marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
@@ -0,0 +1,66 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package co.elastic.apm.agent.esapiclient.v8_x;
JonasKunz marked this conversation as resolved.
Show resolved Hide resolved

import co.elastic.apm.agent.esrestclient.ElasticsearchRestClientInstrumentation;
import co.elastic.apm.agent.esrestclient.ElasticsearchRestClientInstrumentationHelper;
import co.elastic.clients.transport.Endpoint;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.elasticsearch.client.Request;

import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;

public class RestClientTransportInstrumentation extends ElasticsearchRestClientInstrumentation {

@Override
public ElementMatcher<? super TypeDescription> getTypeMatcher() {
return named("co.elastic.clients.transport.rest_client.RestClientTransport");
}

@Override
public ElementMatcher<? super MethodDescription> getMethodMatcher() {
return named("prepareLowLevelRequest")
.and(takesArguments(3))
.and(takesArgument(1, named("co.elastic.clients.transport.Endpoint")))
.and(returns(named("org.elasticsearch.client.Request")));
}

@Override
public String getAdviceClassName() {
return getClass().getName() + "$RestClientTransportAdvice";
}

public static class RestClientTransportAdvice {

private static final ElasticsearchRestClientInstrumentationHelper helper = ElasticsearchRestClientInstrumentationHelper.get();


@Advice.OnMethodExit(suppress = Throwable.class, inline = false)
public static void onPrepareLowLevelRequest(@Advice.Argument(1) Endpoint<?, ?, ?> endpoint, @Advice.Return Request request) {
helper.registerEndpointId(request, endpoint.id());
}

}
}
@@ -0,0 +1,22 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
@NonnullApi
package co.elastic.apm.agent.esapiclient.v8_x;

import co.elastic.apm.agent.sdk.NonnullApi;
@@ -0,0 +1 @@
co.elastic.apm.agent.esapiclient.v8_x.RestClientTransportInstrumentation
Expand Up @@ -19,11 +19,12 @@
package co.elastic.apm.esjavaclient;

import co.elastic.apm.agent.esrestclient.AbstractEsClientInstrumentationTest;
import co.elastic.apm.agent.tracer.Outcome;
import co.elastic.apm.agent.impl.transaction.Span;
import co.elastic.apm.agent.tracer.Outcome;
import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.ElasticsearchException;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.Refresh;
import co.elastic.clients.elasticsearch._types.SearchType;
import co.elastic.clients.elasticsearch._types.StoredScript;
Expand Down Expand Up @@ -134,7 +135,7 @@ public void testTryToDeleteNonExistingIndex() {

Span span = reporter.getFirstSpan();
assertThat(span.getOutcome()).isEqualTo(Outcome.FAILURE);
validateSpanContent(span, String.format("Elasticsearch: DELETE /%s", SECOND_INDEX), 404, "DELETE");
validateSpanContent(span, "Elasticsearch: DELETE /{index}", 404, "DELETE", SECOND_INDEX);
}

@Test
Expand All @@ -144,7 +145,7 @@ public void testDocumentScenario() throws Exception {
List<Span> spans = reporter.getSpans();
try {
assertThat(spans).hasSize(1);
validateSpanContent(spans.get(0), String.format("Elasticsearch: PUT /%s/%s/%s", INDEX, DOC_TYPE, DOC_ID), 201, "PUT");
validateSpanContent(spans.get(0), "Elasticsearch: PUT /{index}/_doc/{id}", 201, "PUT", INDEX, DOC_ID);

// *** RESET ***
reporter.reset();
Expand All @@ -158,7 +159,7 @@ public void testDocumentScenario() throws Exception {
spans = reporter.getSpans();
assertThat(spans).hasSize(1);
Span searchSpan = spans.get(0);
validateSpanContent(searchSpan, String.format("Elasticsearch: POST /%s/_search", INDEX), 200, "POST");
validateSpanContent(searchSpan, "Elasticsearch: POST /{index}/_search", 200, "POST", INDEX);
validateDbContextContent(searchSpan, "{\"from\":0,\"query\":{\"term\":{\"foo\":{\"value\":\"bar\"}}},\"size\":5}");

// *** RESET ***
Expand All @@ -178,9 +179,9 @@ public void testDocumentScenario() throws Exception {
spans = reporter.getSpans();
assertThat(spans).hasSize(2);
Span updateSpan = spans.get(0);
validateSpanContent(updateSpan, String.format("Elasticsearch: POST /%s/_update/%s", INDEX, DOC_ID), 200, "POST");
validateSpanContent(updateSpan, "Elasticsearch: POST /{index}/_update/{id}", 200, "POST", INDEX, DOC_ID);
searchSpan = spans.get(1);
validateSpanContent(searchSpan, String.format("Elasticsearch: POST /%s/_search", INDEX), 200, "POST");
validateSpanContent(searchSpan, "Elasticsearch: POST /{index}/_search", 200, "POST", INDEX);
validateDbContextContent(searchSpan, "{}");

// *** RESET ***
Expand All @@ -190,7 +191,7 @@ public void testDocumentScenario() throws Exception {
// 4. Delete document and validate span content.
co.elastic.clients.elasticsearch.core.DeleteResponse dr = deleteDocument();
assertThat(dr.result().jsonValue()).isEqualTo("deleted");
validateSpanContent(spans.get(0), String.format("Elasticsearch: DELETE /%s/%s/%s", INDEX, DOC_TYPE, DOC_ID), 200, "DELETE");
validateSpanContent(spans.get(0), "Elasticsearch: DELETE /{index}/_doc/{id}", 200, "DELETE", INDEX, DOC_ID);
}
}

Expand All @@ -200,7 +201,7 @@ public void testCountRequest_validateSpanContentAndDbContext() throws Exception
reporter.reset();

CountRequest countRequest = new CountRequest.Builder().index(INDEX).query(new Query.Builder()
.term(new TermQuery.Builder().field(FOO).value(BAR).build())
.term(new TermQuery.Builder().field(FOO).value(FieldValue.of(BAR)).build())
.build()).build();

try {
Expand All @@ -209,7 +210,7 @@ public void testCountRequest_validateSpanContentAndDbContext() throws Exception
List<Span> spans = reporter.getSpans();
assertThat(spans).hasSize(1);
Span span = spans.get(0);
validateSpanContent(span, String.format("Elasticsearch: POST /%s/_count", INDEX), 200, "POST");
validateSpanContent(span, "Elasticsearch: POST /{index}/_count", 200, "POST", INDEX);
validateDbContextContent(span, "{\"query\":{\"term\":{\"foo\":{\"value\":\"bar\"}}}}");
} finally {
deleteDocument();
Expand All @@ -227,7 +228,7 @@ public void testMultiSearchRequest_validateSpanContentAndDbContext() throws Inte
try {
doMultiSearchAndSpanValidate(multiSearchRequest, "Elasticsearch: POST /_msearch");
reporter.reset();
doMultiSearchAndSpanValidate(multiSearchRequestWithIndex, String.format("Elasticsearch: POST /%s/_msearch", INDEX));
doMultiSearchAndSpanValidate(multiSearchRequestWithIndex, "Elasticsearch: POST /{index}/_msearch");
} finally {
deleteDocument();
}
Expand All @@ -244,7 +245,7 @@ private MsearchRequest.Builder getMultiSearchRequestBuilder() {
.query(new Query.Builder()
.match(new MatchQuery.Builder()
.field(FOO)
.query(BAR)
.query(FieldValue.of(BAR))
.build())
.build())
.build())
Expand All @@ -269,7 +270,7 @@ public void testRollupSearch_validateSpanContentAndDbContext() throws Interrupte
RollupSearchRequest searchRequest = new RollupSearchRequest.Builder()
.index(INDEX)
.query(new Query.Builder()
.term(new TermQuery.Builder().field(FOO).value(BAR).build())
.term(new TermQuery.Builder().field(FOO).value(FieldValue.of(BAR)).build())
.build())
.size(5)
.build();
Expand All @@ -280,7 +281,7 @@ public void testRollupSearch_validateSpanContentAndDbContext() throws Interrupte
List<Span> spans = reporter.getSpans();
assertThat(spans).hasSize(1);
Span span = spans.get(0);
validateSpanContent(span, String.format("Elasticsearch: POST /%s/_rollup_search", INDEX), 200, "POST");
validateSpanContent(span, "Elasticsearch: POST /{index}/_rollup_search", 200, "POST", INDEX);
validateDbContextContent(span, "{\"query\":{\"term\":{\"foo\":{\"value\":\"bar\"}}},\"size\":5}");
} finally {
deleteDocument();
Expand All @@ -301,7 +302,7 @@ public void testSearchTemplateRequest_validateSpanContentAndDbContext() throws I
List<Span> spans = reporter.getSpans();
assertThat(spans).hasSize(1);
Span span = spans.get(0);
validateSpanContent(span, String.format("Elasticsearch: POST /%s/_search/template", INDEX), 200, "POST");
validateSpanContent(span, "Elasticsearch: POST /{index}/_search/template", 200, "POST", INDEX);
validateDbContextContent(span, "{\"id\":\"elastic-search-template\",\"params\":{\"field\":\"foo\",\"size\":5,\"value\":\"bar\"}}");
} finally {
deleteMustacheScript();
Expand Down Expand Up @@ -340,7 +341,7 @@ public void testMultisearchTemplateRequest_validateSpanContentAndDbContext() thr
List<Span> spans = reporter.getSpans();
assertThat(spans).hasSize(1);
Span span = spans.get(0);
validateSpanContent(span, String.format("Elasticsearch: POST /_msearch/template", INDEX), 200, "POST");
validateSpanContent(span, "Elasticsearch: POST /_msearch/template", 200, "POST");
verifyMultiSearchTemplateSpanContent(span);
} finally {
deleteMustacheScript();
Expand Down Expand Up @@ -484,7 +485,7 @@ private DeleteResponse deleteDocument() throws IOException {
private SearchRequest prepareSearchRequestWithTermQuery() {
return new SearchRequest.Builder().index(INDEX)
.query(new Query.Builder()
.term(new TermQuery.Builder().field(FOO).value(BAR).build())
.term(new TermQuery.Builder().field(FOO).value(FieldValue.of(BAR)).build())
.build())
.from(0)
.size(5)
Expand Down