Skip to content

Commit e947418

Browse files
authored
Merge pull request #494 from DataDog/ark/es-gap
Elasticsearch 5.3.0 instrumentation
2 parents a9a8a2a + faad782 commit e947418

File tree

6 files changed

+722
-0
lines changed

6 files changed

+722
-0
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
muzzle {
2+
pass {
3+
group = "org.elasticsearch.client"
4+
module = "transport"
5+
versions = "[5.3.0,6.0.0)"
6+
assertInverse = true
7+
}
8+
pass {
9+
group = "org.elasticsearch"
10+
module = "elasticsearch"
11+
versions = "[5.3.0,6.0.0)"
12+
assertInverse = true
13+
}
14+
}
15+
16+
apply from: "${rootDir}/gradle/java.gradle"
17+
18+
testJava8Minimum += '**/*Test.class'
19+
20+
dependencies {
21+
compileOnly group: 'org.elasticsearch.client', name: 'transport', version: '5.3.0'
22+
23+
compile project(':dd-java-agent:agent-tooling')
24+
25+
compile deps.bytebuddy
26+
compile deps.opentracing
27+
annotationProcessor deps.autoservice
28+
implementation deps.autoservice
29+
30+
testCompile project(':dd-java-agent:testing')
31+
32+
testCompile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0'
33+
testCompile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.11.0'
34+
35+
testCompile group: 'org.elasticsearch.plugin', name: 'transport-netty3-client', version: '5.3.0'
36+
testCompile group: 'org.elasticsearch.client', name: 'transport', version: '5.3.0'
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package datadog.trace.instrumentation.elasticsearch5_3;
2+
3+
import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
4+
import static io.opentracing.log.Fields.ERROR_OBJECT;
5+
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
6+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
7+
import static net.bytebuddy.matcher.ElementMatchers.named;
8+
import static net.bytebuddy.matcher.ElementMatchers.not;
9+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
10+
11+
import com.google.auto.service.AutoService;
12+
import datadog.trace.agent.tooling.Instrumenter;
13+
import datadog.trace.api.DDSpanTypes;
14+
import datadog.trace.api.DDTags;
15+
import io.opentracing.Scope;
16+
import io.opentracing.Span;
17+
import io.opentracing.tag.Tags;
18+
import io.opentracing.util.GlobalTracer;
19+
import java.util.Collections;
20+
import java.util.HashMap;
21+
import java.util.Map;
22+
import net.bytebuddy.asm.Advice;
23+
import net.bytebuddy.description.type.TypeDescription;
24+
import net.bytebuddy.matcher.ElementMatcher;
25+
import org.elasticsearch.action.Action;
26+
import org.elasticsearch.action.ActionListener;
27+
import org.elasticsearch.action.ActionRequest;
28+
import org.elasticsearch.action.ActionResponse;
29+
30+
/** Beginning in version 5.3.0, DocumentRequest was renamed to DocWriteRequest. */
31+
@AutoService(Instrumenter.class)
32+
public class Elasticsearch53TransportClientInstrumentation extends Instrumenter.Default {
33+
34+
public Elasticsearch53TransportClientInstrumentation() {
35+
super("elasticsearch", "elasticsearch-transport", "elasticsearch-transport-5");
36+
}
37+
38+
@Override
39+
public ElementMatcher<TypeDescription> typeMatcher() {
40+
// If we want to be more generic, we could instrument the interface instead:
41+
// .and(safeHasSuperType(named("org.elasticsearch.client.ElasticsearchClient"))))
42+
return not(isInterface()).and(named("org.elasticsearch.client.support.AbstractClient"));
43+
}
44+
45+
@Override
46+
public ElementMatcher<ClassLoader> classLoaderMatcher() {
47+
return classLoaderHasClasses("org.elasticsearch.percolator.TransportMultiPercolateAction");
48+
}
49+
50+
@Override
51+
public String[] helperClassNames() {
52+
return new String[] {
53+
"com.google.common.base.Preconditions",
54+
"com.google.common.base.Joiner",
55+
"com.google.common.base.Joiner$1",
56+
"com.google.common.base.Joiner$2",
57+
"com.google.common.base.Joiner$MapJoiner",
58+
"datadog.trace.instrumentation.elasticsearch5_3.TransportActionListener"
59+
};
60+
}
61+
62+
@Override
63+
public Map<ElementMatcher, String> transformers() {
64+
final Map<ElementMatcher, String> transformers = new HashMap<>();
65+
transformers.put(
66+
isMethod()
67+
.and(named("execute"))
68+
.and(takesArgument(0, named("org.elasticsearch.action.Action")))
69+
.and(takesArgument(1, named("org.elasticsearch.action.ActionRequest")))
70+
.and(takesArgument(2, named("org.elasticsearch.action.ActionListener"))),
71+
ElasticsearchTransportClientAdvice.class.getName());
72+
return transformers;
73+
}
74+
75+
public static class ElasticsearchTransportClientAdvice {
76+
77+
@Advice.OnMethodEnter(suppress = Throwable.class)
78+
public static Scope startSpan(
79+
@Advice.Argument(0) final Action action,
80+
@Advice.Argument(1) final ActionRequest actionRequest,
81+
@Advice.Argument(value = 2, readOnly = false)
82+
ActionListener<ActionResponse> actionListener) {
83+
84+
final Scope scope =
85+
GlobalTracer.get()
86+
.buildSpan("elasticsearch.query")
87+
.withTag(DDTags.SERVICE_NAME, "elasticsearch")
88+
.withTag(DDTags.RESOURCE_NAME, action.getClass().getSimpleName())
89+
.withTag(DDTags.SPAN_TYPE, DDSpanTypes.ELASTICSEARCH)
90+
.withTag(Tags.COMPONENT.getKey(), "elasticsearch-java")
91+
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
92+
.withTag("elasticsearch.action", action.getClass().getSimpleName())
93+
.withTag("elasticsearch.request", actionRequest.getClass().getSimpleName())
94+
.startActive(false);
95+
96+
actionListener = new TransportActionListener<>(actionRequest, actionListener, scope.span());
97+
return scope;
98+
}
99+
100+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
101+
public static void stopSpan(
102+
@Advice.Enter final Scope scope, @Advice.Thrown final Throwable throwable) {
103+
if (throwable != null) {
104+
final Span span = scope.span();
105+
Tags.ERROR.set(span, true);
106+
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
107+
span.finish();
108+
}
109+
scope.close();
110+
}
111+
}
112+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package datadog.trace.instrumentation.elasticsearch5_3;
2+
3+
import static io.opentracing.log.Fields.ERROR_OBJECT;
4+
5+
import com.google.common.base.Joiner;
6+
import io.opentracing.Span;
7+
import io.opentracing.tag.Tags;
8+
import java.util.Collections;
9+
import org.elasticsearch.action.ActionListener;
10+
import org.elasticsearch.action.ActionRequest;
11+
import org.elasticsearch.action.ActionResponse;
12+
import org.elasticsearch.action.DocWriteRequest;
13+
import org.elasticsearch.action.IndicesRequest;
14+
import org.elasticsearch.action.bulk.BulkShardResponse;
15+
import org.elasticsearch.action.get.GetResponse;
16+
import org.elasticsearch.action.index.IndexResponse;
17+
import org.elasticsearch.action.search.SearchRequest;
18+
import org.elasticsearch.action.support.broadcast.BroadcastResponse;
19+
import org.elasticsearch.action.support.nodes.BaseNodesResponse;
20+
import org.elasticsearch.action.support.replication.ReplicationResponse;
21+
22+
public class TransportActionListener<T extends ActionResponse> implements ActionListener<T> {
23+
24+
private final ActionListener<T> listener;
25+
private final Span span;
26+
27+
public TransportActionListener(
28+
final ActionRequest actionRequest, final ActionListener<T> listener, final Span span) {
29+
this.listener = listener;
30+
this.span = span;
31+
onRequest(actionRequest);
32+
}
33+
34+
private void onRequest(final ActionRequest request) {
35+
if (request != null) {
36+
span.setTag("elasticsearch.request.description", request.getDescription());
37+
}
38+
if (request instanceof IndicesRequest) {
39+
final IndicesRequest req = (IndicesRequest) request;
40+
if (req.indices() != null) {
41+
span.setTag("elasticsearch.request.indices", Joiner.on(",").join(req.indices()));
42+
}
43+
}
44+
if (request instanceof SearchRequest) {
45+
final SearchRequest req = (SearchRequest) request;
46+
span.setTag("elasticsearch.request.search.types", Joiner.on(",").join(req.types()));
47+
}
48+
if (request instanceof DocWriteRequest) {
49+
final DocWriteRequest req = (DocWriteRequest) request;
50+
span.setTag("elasticsearch.request.write.type", req.type());
51+
span.setTag("elasticsearch.request.write.routing", req.routing());
52+
span.setTag("elasticsearch.request.write.version", req.version());
53+
}
54+
}
55+
56+
@Override
57+
public void onResponse(final T response) {
58+
if (response.remoteAddress() != null) {
59+
Tags.PEER_HOSTNAME.set(span, response.remoteAddress().getHost());
60+
Tags.PEER_HOST_IPV4.set(span, response.remoteAddress().getAddress());
61+
Tags.PEER_PORT.set(span, response.remoteAddress().getPort());
62+
}
63+
64+
if (response instanceof GetResponse) {
65+
final GetResponse resp = (GetResponse) response;
66+
span.setTag("elasticsearch.type", resp.getType());
67+
span.setTag("elasticsearch.id", resp.getId());
68+
span.setTag("elasticsearch.version", resp.getVersion());
69+
}
70+
71+
if (response instanceof BroadcastResponse) {
72+
final BroadcastResponse resp = (BroadcastResponse) response;
73+
span.setTag("elasticsearch.shard.broadcast.total", resp.getTotalShards());
74+
span.setTag("elasticsearch.shard.broadcast.successful", resp.getSuccessfulShards());
75+
span.setTag("elasticsearch.shard.broadcast.failed", resp.getFailedShards());
76+
}
77+
78+
if (response instanceof ReplicationResponse) {
79+
final ReplicationResponse resp = (ReplicationResponse) response;
80+
span.setTag("elasticsearch.shard.replication.total", resp.getShardInfo().getTotal());
81+
span.setTag(
82+
"elasticsearch.shard.replication.successful", resp.getShardInfo().getSuccessful());
83+
span.setTag("elasticsearch.shard.replication.failed", resp.getShardInfo().getFailed());
84+
}
85+
86+
if (response instanceof IndexResponse) {
87+
span.setTag("elasticsearch.response.status", ((IndexResponse) response).status().getStatus());
88+
}
89+
90+
if (response instanceof BulkShardResponse) {
91+
final BulkShardResponse resp = (BulkShardResponse) response;
92+
span.setTag("elasticsearch.shard.bulk.id", resp.getShardId().getId());
93+
span.setTag("elasticsearch.shard.bulk.index", resp.getShardId().getIndexName());
94+
}
95+
96+
if (response instanceof BaseNodesResponse) {
97+
final BaseNodesResponse resp = (BaseNodesResponse) response;
98+
if (resp.hasFailures()) {
99+
span.setTag("elasticsearch.node.failures", resp.failures().size());
100+
}
101+
span.setTag("elasticsearch.node.cluster.name", resp.getClusterName().value());
102+
}
103+
104+
try {
105+
listener.onResponse(response);
106+
} finally {
107+
span.finish();
108+
}
109+
}
110+
111+
@Override
112+
public void onFailure(final Exception e) {
113+
Tags.ERROR.set(span, true);
114+
span.log(Collections.singletonMap(ERROR_OBJECT, e));
115+
116+
try {
117+
listener.onFailure(e);
118+
} finally {
119+
span.finish();
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)