Skip to content

Commit 32a3642

Browse files
authored
Support older ES client versions (#268)
* Support older ES client versions * Using activeSpan() API * Fixing a doc typo * Adding header
1 parent a15f2c6 commit 32a3642

File tree

18 files changed

+671
-40
lines changed

18 files changed

+671
-40
lines changed

apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/plugin/api/ApiScopeInstrumentation.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
/*-
2+
* #%L
3+
* Elastic APM Java agent
4+
* %%
5+
* Copyright (C) 2018 Elastic and contributors
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
120
package co.elastic.apm.plugin.api;
221

322
import co.elastic.apm.bci.ElasticApmInstrumentation;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<artifactId>apm-es-restclient-plugin</artifactId>
9+
<groupId>co.elastic.apm</groupId>
10+
<version>0.9.0-SNAPSHOT</version>
11+
</parent>
12+
13+
<artifactId>apm-es-restclient-plugin-5_6</artifactId>
14+
<name>${project.groupId}:${project.artifactId}</name>
15+
16+
<properties>
17+
<version.elasticsearch>5.6.0</version.elasticsearch>
18+
</properties>
19+
20+
<dependencies>
21+
<!-- Elasticsearch rest client -->
22+
<dependency>
23+
<groupId>org.elasticsearch.client</groupId>
24+
<artifactId>elasticsearch-rest-client</artifactId>
25+
<version>${version.elasticsearch}</version>
26+
<scope>provided</scope>
27+
</dependency>
28+
<dependency>
29+
<groupId>org.elasticsearch.client</groupId>
30+
<artifactId>elasticsearch-rest-high-level-client</artifactId>
31+
<version>${version.elasticsearch}</version>
32+
<scope>provided</scope>
33+
</dependency>
34+
35+
<!-- Elasticsearch testcontainer -->
36+
<dependency>
37+
<groupId>fr.pilato.elasticsearch.testcontainers</groupId>
38+
<artifactId>testcontainers-elasticsearch</artifactId>
39+
<version>0.1</version>
40+
<scope>test</scope>
41+
</dependency>
42+
</dependencies>
43+
</project>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* limitations under the License.
1818
* #L%
1919
*/
20-
package co.elastic.apm.es.restclient;
20+
package co.elastic.apm.es.restclient.v5_6;
2121

2222
import co.elastic.apm.bci.VisibleForAdvice;
2323
import co.elastic.apm.report.serialize.DslJsonSerializer;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*-
2+
* #%L
3+
* Elastic APM Java agent
4+
* %%
5+
* Copyright (C) 2018 Elastic and contributors
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package co.elastic.apm.es.restclient.v5_6;
21+
22+
import co.elastic.apm.bci.ElasticApmInstrumentation;
23+
import co.elastic.apm.bci.VisibleForAdvice;
24+
import co.elastic.apm.impl.transaction.AbstractSpan;
25+
import co.elastic.apm.impl.transaction.Span;
26+
import net.bytebuddy.asm.Advice;
27+
import net.bytebuddy.description.method.MethodDescription;
28+
import net.bytebuddy.description.type.TypeDescription;
29+
import net.bytebuddy.matcher.ElementMatcher;
30+
import org.apache.http.HttpEntity;
31+
import org.elasticsearch.client.Response;
32+
import org.elasticsearch.client.ResponseException;
33+
34+
import javax.annotation.Nullable;
35+
import java.io.IOException;
36+
import java.util.Collection;
37+
import java.util.Collections;
38+
39+
import static net.bytebuddy.matcher.ElementMatchers.declaresMethod;
40+
import static net.bytebuddy.matcher.ElementMatchers.named;
41+
import static net.bytebuddy.matcher.ElementMatchers.not;
42+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
43+
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
44+
45+
/**
46+
* Instrumentation for Elasticsearch RestClient, currently supporting only synchronized queries.
47+
* All sync operations go through org.elasticsearch.client.RestClient#performRequest(org.elasticsearch.client.Request)
48+
*/
49+
public class ElasticsearchRestClientInstrumentation extends ElasticApmInstrumentation {
50+
@VisibleForAdvice
51+
public static final String SEARCH_QUERY_PATH_SUFFIX = "_search";
52+
@VisibleForAdvice
53+
public static final String SPAN_TYPE = "db.elasticsearch.request";
54+
@VisibleForAdvice
55+
public static final String DB_CONTEXT_TYPE = "elasticsearch";
56+
57+
@Advice.OnMethodEnter
58+
private static void onBeforeExecute(@Advice.Argument(0) String method,
59+
@Advice.Argument(1) String endpoint,
60+
@Advice.Argument(3) HttpEntity entity,
61+
@Advice.Local("span") Span span) {
62+
if (tracer == null) {
63+
return;
64+
}
65+
final AbstractSpan<?> activeSpan = tracer.activeSpan();
66+
if (activeSpan == null || !activeSpan.isSampled()) {
67+
return;
68+
}
69+
span = activeSpan.createSpan()
70+
.withType(SPAN_TYPE)
71+
.appendToName("Elasticsearch: ").appendToName(method).appendToName(" ").appendToName(endpoint);
72+
span.getContext().getDb().withType(DB_CONTEXT_TYPE);
73+
span.activate();
74+
75+
if (span.isSampled()) {
76+
span.getContext().getHttp().withMethod(method);
77+
if (endpoint.endsWith(SEARCH_QUERY_PATH_SUFFIX)) {
78+
if (entity != null && entity.isRepeatable()) {
79+
try {
80+
String body = ESRestClientInstrumentationHelper.readRequestBody(entity.getContent(), endpoint);
81+
if (body != null && !body.isEmpty()) {
82+
span.getContext().getDb().withStatement(body);
83+
}
84+
} catch (IOException e) {
85+
// We can't log from here
86+
}
87+
}
88+
}
89+
}
90+
}
91+
92+
@Advice.OnMethodExit(onThrowable = Throwable.class)
93+
public static void onAfterExecute(@Advice.Return @Nullable Response response,
94+
@Advice.Local("span") @Nullable Span span,
95+
@Advice.Thrown @Nullable Throwable t) {
96+
if (span != null) {
97+
try {
98+
String url = null;
99+
int statusCode = -1;
100+
if(response != null) {
101+
url = response.getHost().toURI();
102+
statusCode = response.getStatusLine().getStatusCode();
103+
} else if(t != null) {
104+
if (t instanceof ResponseException) {
105+
ResponseException esre = (ResponseException) t;
106+
url = esre.getResponse().getHost().toURI();
107+
statusCode = esre.getResponse().getStatusLine().getStatusCode();
108+
109+
/*
110+
// Add tags so that they will be copied to error capture
111+
span.addTag(QUERY_STATUS_CODE_KEY, Integer.toString(statusCode));
112+
span.addTag(ELASTICSEARCH_NODE_URL_KEY, url);
113+
span.addTag(ERROR_REASON_KEY, esre.getResponse().getStatusLine().getReasonPhrase());
114+
*/
115+
}
116+
span.captureException(t);
117+
}
118+
119+
if(url != null && !url.isEmpty()) {
120+
span.getContext().getHttp().withUrl(url);
121+
}
122+
if(statusCode > 0) {
123+
span.getContext().getHttp().withStatusCode(statusCode);
124+
}
125+
} finally {
126+
span.deactivate().end();
127+
}
128+
129+
}
130+
}
131+
132+
@Override
133+
public ElementMatcher<? super TypeDescription> getTypeMatcher() {
134+
return named("org.elasticsearch.client.RestClient").
135+
and(not(
136+
declaresMethod(named("performRequest")
137+
.and(takesArguments(1)
138+
.and(takesArgument(0, named("org.elasticsearch.client.Request")))))));
139+
}
140+
141+
@Override
142+
public ElementMatcher<? super MethodDescription> getMethodMatcher() {
143+
return named("performRequest")
144+
.and(takesArguments(6)
145+
.and(takesArgument(4, named("org.elasticsearch.client.HttpAsyncResponseConsumerFactory"))));
146+
}
147+
148+
@Override
149+
public Collection<String> getInstrumentationGroupNames() {
150+
return Collections.singleton("elasticsearch-restclient");
151+
}
152+
}

apm-agent-plugins/apm-es-restclient-plugin/src/main/java/co/elastic/apm/es/restclient/package-info.java renamed to apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/es/restclient/v5_6/package-info.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@
1818
* #L%
1919
*/
2020
@NonnullApi
21-
package co.elastic.apm.es.restclient;
21+
package co.elastic.apm.es.restclient.v5_6;
2222

2323
import co.elastic.apm.annotation.NonnullApi;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
co.elastic.apm.es.restclient.v5_6.ElasticsearchRestClientInstrumentation

0 commit comments

Comments
 (0)