Elasticsearch Instrumentation

This commit is contained in:
Tyler Benson 2018-05-18 11:57:56 +10:00
parent 164689eeb3
commit 0cf8fef8d8
25 changed files with 1566 additions and 23 deletions

View File

@ -0,0 +1,58 @@
//apply plugin: 'version-scan'
//
//versionScan {
// group = "org.elasticsearch.client"
//// module = "transport"
// module = "rest"
// versions = "[5.0,)"
// legacyGroup = "org.elasticsearch"
// legacyModule = "elasticsearch"
// scanDependencies = true
// verifyPresent = [
// "org.elasticsearch.percolator.TransportMultiPercolateAction": null,
// ]
//}
apply from: "${rootDir}/gradle/java.gradle"
apply plugin: 'org.unbroken-dome.test-sets'
testSets {
latestDepTest
}
dependencies {
compileOnly group: 'org.elasticsearch.client', name: 'rest', version: '5.0.0'
compile project(':dd-java-agent:agent-tooling')
compile deps.bytebuddy
compile deps.opentracing
annotationProcessor deps.autoservice
implementation deps.autoservice
testCompile project(':dd-java-agent:testing')
// Ensure no cross interference
testCompile project(':dd-java-agent:instrumentation:elasticsearch-transport-5')
testCompile project(':dd-java-agent:instrumentation:elasticsearch-transport-6')
// Include httpclient instrumentation for testing because it is a dependency for elasticsearch-rest-client.
// It doesn't actually work though. They use HttpAsyncClient, which isn't currently instrumented.
// TODO: add HttpAsyncClient instrumentation when that is complete.
testCompile project(':dd-java-agent:instrumentation:apache-httpclient-4.3')
// TODO: add netty instrumentation when that is complete.
testCompile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0'
testCompile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.11.0'
testCompile group: 'org.elasticsearch.client', name: 'rest', version: '5.0.0'
testCompile group: 'org.elasticsearch', name: 'elasticsearch', version: '5.0.0'
testCompile group: 'org.elasticsearch.plugin', name: 'transport-netty3-client', version: '5.0.0'
latestDepTestCompile group: 'org.elasticsearch.client', name: 'elasticsearch-rest-client', version: '+'
latestDepTestCompile group: 'org.elasticsearch', name: 'elasticsearch', version: '6.+'
latestDepTestCompile group: 'org.elasticsearch.plugin', name: 'transport-netty4-client', version: '+'
}
configurations.latestDepTestCompile {
exclude group: "org.elasticsearch.client", module: "rest"
}

View File

@ -0,0 +1,117 @@
import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.agent.test.TestUtils
import groovy.json.JsonSlurper
import io.opentracing.tag.Tags
import org.apache.http.HttpHost
import org.apache.http.client.config.RequestConfig
import org.apache.http.util.EntityUtils
import org.elasticsearch.client.Response
import org.elasticsearch.client.RestClient
import org.elasticsearch.client.RestClientBuilder
import org.elasticsearch.common.io.FileSystemUtils
import org.elasticsearch.common.settings.Settings
import org.elasticsearch.node.InternalSettingsPreparer
import org.elasticsearch.node.Node
import org.elasticsearch.transport.Netty4Plugin
import spock.lang.Shared
import static datadog.trace.agent.test.ListWriterAssert.assertTraces
class Elasticsearch6RestClientTest extends AgentTestRunner {
static {
System.setProperty("dd.integration.elasticsearch.enabled", "true")
}
static final int HTTP_PORT = TestUtils.randomOpenPort()
static final int TCP_PORT = TestUtils.randomOpenPort()
@Shared
static Node testNode
static File esWorkingDir
@Shared
static RestClient client
def setupSpec() {
esWorkingDir = File.createTempFile("test-es-working-dir-", "")
esWorkingDir.delete()
esWorkingDir.mkdir()
esWorkingDir.deleteOnExit()
println "ES work dir: $esWorkingDir"
def settings = Settings.builder()
.put("path.home", esWorkingDir.path)
.put("http.port", HTTP_PORT)
.put("transport.tcp.port", TCP_PORT)
.put("cluster.name", "test-cluster")
.build()
testNode = new Node(InternalSettingsPreparer.prepareEnvironment(settings, null), [Netty4Plugin])
testNode.start()
client = RestClient.builder(new HttpHost("localhost", HTTP_PORT))
.setMaxRetryTimeoutMillis(Integer.MAX_VALUE)
.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {
@Override
RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder builder) {
return builder.setConnectTimeout(Integer.MAX_VALUE).setSocketTimeout(Integer.MAX_VALUE)
}
})
.build()
}
def cleanupSpec() {
testNode?.close()
if (esWorkingDir != null) {
FileSystemUtils.deleteSubDirectories(esWorkingDir.toPath())
esWorkingDir.delete()
}
}
def "test elasticsearch status"() {
setup:
Response response = client.performRequest("GET", "_cluster/health")
Map result = new JsonSlurper().parseText(EntityUtils.toString(response.entity))
expect:
result.status == "green"
assertTraces(TEST_WRITER, 2) {
trace(0, 1) {
span(0) {
serviceName "elasticsearch"
resourceName "ClusterHealthAction"
operationName "elasticsearch.query"
spanType null
parent()
tags {
"$Tags.COMPONENT.key" "elasticsearch-java"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"elasticsearch.action" "ClusterHealthAction"
"elasticsearch.request" "ClusterHealthRequest"
defaultTags()
}
}
}
trace(1, 1) {
span(0) {
serviceName "elasticsearch"
resourceName "GET _cluster/health"
operationName "elasticsearch.rest.query"
spanType null
parent()
tags {
"$Tags.COMPONENT.key" "elasticsearch-java"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"$Tags.HTTP_METHOD.key" "GET"
"$Tags.HTTP_URL.key" "_cluster/health"
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_PORT.key" HTTP_PORT
defaultTags()
}
}
}
}
}
}

View File

@ -0,0 +1,91 @@
package datadog.trace.instrumentation.elasticsearch5;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.DDAdvice;
import datadog.trace.agent.tooling.DDTransformers;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.api.DDTags;
import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.tag.Tags;
import io.opentracing.util.GlobalTracer;
import java.util.Collections;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import org.elasticsearch.client.ResponseListener;
@AutoService(Instrumenter.class)
public class Elasticsearch5RestClientInstrumentation extends Instrumenter.Configurable {
public Elasticsearch5RestClientInstrumentation() {
super("elasticsearch", "elasticsearch-rest", "elasticsearch-rest-5");
}
@Override
protected boolean defaultEnabled() {
return false;
}
@Override
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(not(isInterface()).and(named("org.elasticsearch.client.RestClient")))
.transform(DDTransformers.defaultTransformers())
.transform(
DDAdvice.create()
.advice(
isMethod()
.and(isPublic())
.and(named("performRequestAsync"))
.and(takesArguments(7))
.and(takesArgument(0, named("java.lang.String"))) // method
.and(takesArgument(1, named("java.lang.String"))) // endpoint
.and(takesArgument(5, named("org.elasticsearch.client.ResponseListener"))),
ElasticsearchRestClientAdvice.class.getName()))
.asDecorator();
}
public static class ElasticsearchRestClientAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static Scope startSpan(
@Advice.Argument(0) final String method,
@Advice.Argument(1) final String endpoint,
@Advice.Argument(value = 5, readOnly = false) ResponseListener responseListener) {
final Scope scope =
GlobalTracer.get()
.buildSpan("elasticsearch.rest.query")
.withTag(DDTags.SERVICE_NAME, "elasticsearch")
.withTag(Tags.HTTP_METHOD.getKey(), method)
.withTag(Tags.HTTP_URL.getKey(), endpoint)
.withTag(Tags.COMPONENT.getKey(), "elasticsearch-java")
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
.startActive(false);
responseListener = new RestResponseListener(responseListener, scope.span());
return scope;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Enter final Scope scope, @Advice.Thrown final Throwable throwable) {
if (throwable != null) {
final Span span = scope.span();
Tags.ERROR.set(span, true);
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
span.finish();
}
scope.close();
}
}
}

View File

@ -0,0 +1,45 @@
package datadog.trace.instrumentation.elasticsearch5;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import io.opentracing.Span;
import io.opentracing.tag.Tags;
import java.util.Collections;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseListener;
public class RestResponseListener implements ResponseListener {
private final ResponseListener listener;
private final Span span;
public RestResponseListener(final ResponseListener listener, final Span span) {
this.listener = listener;
this.span = span;
}
@Override
public void onSuccess(final Response response) {
if (response.getHost() != null) {
Tags.PEER_HOSTNAME.set(span, response.getHost().getHostName());
Tags.PEER_PORT.set(span, response.getHost().getPort());
}
try {
listener.onSuccess(response);
} finally {
span.finish();
}
}
@Override
public void onFailure(final Exception e) {
span.log(Collections.singletonMap(ERROR_OBJECT, e));
try {
listener.onFailure(e);
} finally {
span.finish();
}
}
}

View File

@ -0,0 +1,105 @@
import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.agent.test.TestUtils
import groovy.json.JsonSlurper
import io.opentracing.tag.Tags
import org.apache.http.HttpHost
import org.apache.http.client.config.RequestConfig
import org.apache.http.util.EntityUtils
import org.elasticsearch.client.Response
import org.elasticsearch.client.RestClient
import org.elasticsearch.client.RestClientBuilder
import org.elasticsearch.common.io.FileSystemUtils
import org.elasticsearch.common.settings.Settings
import org.elasticsearch.env.Environment
import org.elasticsearch.node.Node
import org.elasticsearch.node.internal.InternalSettingsPreparer
import org.elasticsearch.transport.Netty3Plugin
import spock.lang.Shared
import static datadog.trace.agent.test.ListWriterAssert.assertTraces
import static org.elasticsearch.cluster.ClusterName.CLUSTER_NAME_SETTING
class Elasticsearch5RestClientTest extends AgentTestRunner {
static {
System.setProperty("dd.integration.elasticsearch.enabled", "true")
}
static final int HTTP_PORT = TestUtils.randomOpenPort()
static final int TCP_PORT = TestUtils.randomOpenPort()
@Shared
static Node testNode
static File esWorkingDir
@Shared
static RestClient client
def setupSpec() {
esWorkingDir = File.createTempFile("test-es-working-dir-", "")
esWorkingDir.delete()
esWorkingDir.mkdir()
esWorkingDir.deleteOnExit()
println "ES work dir: $esWorkingDir"
def settings = Settings.builder()
.put("path.home", esWorkingDir.path)
.put("http.port", HTTP_PORT)
.put("transport.tcp.port", TCP_PORT)
.put("transport.type", "netty3")
.put("http.type", "netty3")
.put(CLUSTER_NAME_SETTING.getKey(), "test-cluster")
.build()
testNode = new Node(new Environment(InternalSettingsPreparer.prepareSettings(settings)), [Netty3Plugin])
testNode.start()
client = RestClient.builder(new HttpHost("localhost", HTTP_PORT))
.setMaxRetryTimeoutMillis(Integer.MAX_VALUE)
.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {
@Override
RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder builder) {
return builder.setConnectTimeout(Integer.MAX_VALUE).setSocketTimeout(Integer.MAX_VALUE)
}
})
.build()
}
def cleanupSpec() {
testNode?.close()
if (esWorkingDir != null) {
FileSystemUtils.deleteSubDirectories(esWorkingDir.toPath())
esWorkingDir.delete()
}
}
def "test elasticsearch status"() {
setup:
Response response = client.performRequest("GET", "_cluster/health")
Map result = new JsonSlurper().parseText(EntityUtils.toString(response.entity))
expect:
result.status == "green"
assertTraces(TEST_WRITER, 1) {
trace(0, 1) {
span(0) {
serviceName "elasticsearch"
resourceName "GET _cluster/health"
operationName "elasticsearch.rest.query"
spanType null
parent()
tags {
"$Tags.COMPONENT.key" "elasticsearch-java"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"$Tags.HTTP_METHOD.key" "GET"
"$Tags.HTTP_URL.key" "_cluster/health"
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_PORT.key" HTTP_PORT
defaultTags()
}
}
}
}
}
}

View File

@ -0,0 +1,45 @@
apply plugin: 'version-scan'
versionScan {
group = "org.elasticsearch"
module = "elasticsearch"
versions = "[2.0,3)"
verifyPresent = [
"org.elasticsearch.plugins.SitePlugin": null,
]
}
apply from: "${rootDir}/gradle/java.gradle"
apply plugin: 'org.unbroken-dome.test-sets'
testSets {
latestDepTest {
dirName = 'test'
}
}
dependencies {
compileOnly group: 'org.elasticsearch', name: 'elasticsearch', version: '2.0.0'
compile project(':dd-java-agent:agent-tooling')
compile deps.bytebuddy
compile deps.opentracing
annotationProcessor deps.autoservice
implementation deps.autoservice
testCompile project(':dd-java-agent:testing')
// Ensure no cross interference
testCompile project(':dd-java-agent:instrumentation:elasticsearch-rest-5')
testCompile project(':dd-java-agent:instrumentation:elasticsearch-transport-5')
// TODO: add netty instrumentation when that is complete.
testCompile group: 'org.elasticsearch', name: 'elasticsearch', version: '2.0.0'
testCompile group: 'net.java.dev.jna', name: 'jna', version: '4.5.1'
testCompile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0'
testCompile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.11.0'
latestDepTestCompile group: 'org.elasticsearch', name: 'elasticsearch', version: '2.+'
}

View File

@ -0,0 +1,97 @@
package datadog.trace.instrumentation.elasticsearch2;
import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.DDAdvice;
import datadog.trace.agent.tooling.DDTransformers;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.api.DDTags;
import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.tag.Tags;
import io.opentracing.util.GlobalTracer;
import java.util.Collections;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import org.elasticsearch.action.Action;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionResponse;
@AutoService(Instrumenter.class)
public class Elasticsearch2TransportClientInstrumentation extends Instrumenter.Configurable {
public Elasticsearch2TransportClientInstrumentation() {
super("elasticsearch", "elasticsearch-transport", "elasticsearch-transport-2");
}
@Override
protected boolean defaultEnabled() {
return false;
}
@Override
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(named("org.elasticsearch.client.support.AbstractClient")),
// If we want to be more generic, we could instrument the interface instead:
// .and(hasSuperType(named("org.elasticsearch.client.ElasticsearchClient"))))
classLoaderHasClasses("org.elasticsearch.plugins.SitePlugin"))
.transform(DDTransformers.defaultTransformers())
.transform(
DDAdvice.create()
.advice(
isMethod()
.and(named("execute"))
.and(takesArgument(0, named("org.elasticsearch.action.Action")))
.and(takesArgument(1, named("org.elasticsearch.action.ActionRequest")))
.and(takesArgument(2, named("org.elasticsearch.action.ActionListener"))),
ElasticsearchTransportClientAdvice.class.getName()))
.asDecorator();
}
public static class ElasticsearchTransportClientAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static Scope startSpan(
@Advice.Argument(0) final Action action,
@Advice.Argument(1) final ActionRequest actionRequest,
@Advice.Argument(value = 2, readOnly = false)
ActionListener<ActionResponse> actionListener) {
final Scope scope =
GlobalTracer.get()
.buildSpan("elasticsearch.query")
.withTag(DDTags.SERVICE_NAME, "elasticsearch")
.withTag(DDTags.RESOURCE_NAME, action.getClass().getSimpleName())
.withTag(Tags.COMPONENT.getKey(), "elasticsearch-java")
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
.withTag("elasticsearch.action", action.getClass().getSimpleName())
.withTag("elasticsearch.request", actionRequest.getClass().getSimpleName())
.startActive(false);
actionListener = new TransportActionListener<>(actionListener, scope.span());
return scope;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Enter final Scope scope, @Advice.Thrown final Throwable throwable) {
if (throwable != null) {
final Span span = scope.span();
Tags.ERROR.set(span, true);
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
span.finish();
}
scope.close();
}
}
}

View File

@ -0,0 +1,48 @@
package datadog.trace.instrumentation.elasticsearch2;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import io.opentracing.Span;
import io.opentracing.tag.Tags;
import java.util.Collections;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionResponse;
public class TransportActionListener<T> implements ActionListener<T> {
private final ActionListener<T> listener;
private final Span span;
public TransportActionListener(final ActionListener<T> listener, final Span span) {
this.listener = listener;
this.span = span;
}
@Override
public void onResponse(final T response) {
if (response instanceof ActionResponse) {
final ActionResponse ar = (ActionResponse) response;
if (ar.remoteAddress() != null) {
Tags.PEER_HOSTNAME.set(span, ar.remoteAddress().getHost());
Tags.PEER_PORT.set(span, ar.remoteAddress().getPort());
}
}
try {
listener.onResponse(response);
} finally {
span.finish();
}
}
@Override
public void onFailure(final Throwable e) {
span.log(Collections.singletonMap(ERROR_OBJECT, e));
try {
listener.onFailure(e);
} finally {
span.finish();
}
}
}

View File

@ -0,0 +1,78 @@
import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.agent.test.TestUtils
import io.opentracing.tag.Tags
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest
import org.elasticsearch.common.io.FileSystemUtils
import org.elasticsearch.common.settings.Settings
import org.elasticsearch.node.Node
import org.elasticsearch.node.NodeBuilder
import spock.lang.Shared
import static datadog.trace.agent.test.ListWriterAssert.assertTraces
class Elasticsearch2NodeClientTest extends AgentTestRunner {
static {
System.setProperty("dd.integration.elasticsearch.enabled", "true")
}
static final int HTTP_PORT = TestUtils.randomOpenPort()
static final int TCP_PORT = TestUtils.randomOpenPort()
@Shared
static Node testNode
static File esWorkingDir
def client = testNode.client()
def setupSpec() {
esWorkingDir = File.createTempFile("test-es-working-dir-", "")
esWorkingDir.delete()
esWorkingDir.mkdir()
esWorkingDir.deleteOnExit()
println "ES work dir: $esWorkingDir"
def settings = Settings.builder()
.put("path.home", esWorkingDir.path)
.put("http.port", HTTP_PORT)
.put("transport.tcp.port", TCP_PORT)
.build()
testNode = NodeBuilder.newInstance().clusterName("test-cluster").settings(settings).build()
testNode.start()
}
def cleanupSpec() {
testNode?.close()
if (esWorkingDir != null) {
FileSystemUtils.deleteSubDirectories(esWorkingDir.toPath())
esWorkingDir.delete()
}
}
def "test elasticsearch status"() {
setup:
def result = client.admin().cluster().health(new ClusterHealthRequest(new String[0]))
def status = result.get().status
expect:
status.name() == "GREEN"
assertTraces(TEST_WRITER, 1) {
trace(0, 1) {
span(0) {
serviceName "elasticsearch"
resourceName "ClusterHealthAction"
operationName "elasticsearch.query"
spanType null
tags {
"$Tags.COMPONENT.key" "elasticsearch-java"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"elasticsearch.action" "ClusterHealthAction"
"elasticsearch.request" "ClusterHealthRequest"
defaultTags()
}
}
}
}
}
}

View File

@ -0,0 +1,90 @@
import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.agent.test.TestUtils
import io.opentracing.tag.Tags
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest
import org.elasticsearch.client.transport.TransportClient
import org.elasticsearch.common.io.FileSystemUtils
import org.elasticsearch.common.settings.Settings
import org.elasticsearch.common.transport.InetSocketTransportAddress
import org.elasticsearch.node.Node
import org.elasticsearch.node.NodeBuilder
import spock.lang.Shared
import static datadog.trace.agent.test.ListWriterAssert.assertTraces
class Elasticsearch2TransportClientTest extends AgentTestRunner {
static {
System.setProperty("dd.integration.elasticsearch.enabled", "true")
}
static final int HTTP_PORT = TestUtils.randomOpenPort()
static final int TCP_PORT = TestUtils.randomOpenPort()
@Shared
static Node testNode
static File esWorkingDir
@Shared
static TransportClient client
def setupSpec() {
esWorkingDir = File.createTempFile("test-es-working-dir-", "")
esWorkingDir.delete()
esWorkingDir.mkdir()
esWorkingDir.deleteOnExit()
println "ES work dir: $esWorkingDir"
def settings = Settings.builder()
.put("path.home", esWorkingDir.path)
.put("http.port", HTTP_PORT)
.put("transport.tcp.port", TCP_PORT)
.build()
testNode = NodeBuilder.newInstance().clusterName("test-cluster").settings(settings).build()
testNode.start()
client = TransportClient.builder().settings(
Settings.builder()
.put("cluster.name", "test-cluster")
.build()
).build()
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), TCP_PORT))
}
def cleanupSpec() {
testNode?.close()
if (esWorkingDir != null) {
FileSystemUtils.deleteSubDirectories(esWorkingDir.toPath())
esWorkingDir.delete()
}
}
def "test elasticsearch status"() {
setup:
def result = client.admin().cluster().health(new ClusterHealthRequest(new String[0]))
def status = result.get().status
expect:
status.name() == "GREEN"
assertTraces(TEST_WRITER, 1) {
trace(0, 1) {
span(0) {
serviceName "elasticsearch"
resourceName "ClusterHealthAction"
operationName "elasticsearch.query"
spanType null
tags {
"$Tags.COMPONENT.key" "elasticsearch-java"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"$Tags.PEER_HOSTNAME.key" "127.0.0.1"
"$Tags.PEER_PORT.key" TCP_PORT
"elasticsearch.action" "ClusterHealthAction"
"elasticsearch.request" "ClusterHealthRequest"
defaultTags()
}
}
}
}
}
}

View File

@ -0,0 +1,52 @@
apply plugin: 'version-scan'
versionScan {
group = "org.elasticsearch.client"
module = "transport"
versions = "[5.0,6)"
legacyGroup = "org.elasticsearch"
legacyModule = "elasticsearch"
scanDependencies = true
verifyPresent = [
"org.elasticsearch.percolator.TransportMultiPercolateAction": null,
]
}
apply from: "${rootDir}/gradle/java.gradle"
apply plugin: 'org.unbroken-dome.test-sets'
testSets {
latestDepTest {
dirName = 'test'
}
}
dependencies {
compileOnly group: 'org.elasticsearch.client', name: 'transport', version: '5.0.0'
compile project(':dd-java-agent:agent-tooling')
compile deps.bytebuddy
compile deps.opentracing
annotationProcessor deps.autoservice
implementation deps.autoservice
testCompile project(':dd-java-agent:testing')
// Ensure no cross interference
testCompile project(':dd-java-agent:instrumentation:elasticsearch-rest-5')
// Include httpclient instrumentation for testing because it is a dependency for elasticsearch-rest-client.
// It doesn't actually work though. They use HttpAsyncClient, which isn't currently instrumented.
// TODO: add HttpAsyncClient instrumentation when that is complete.
testCompile project(':dd-java-agent:instrumentation:apache-httpclient-4.3')
// TODO: add netty instrumentation when that is complete.
testCompile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0'
testCompile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.11.0'
testCompile group: 'org.elasticsearch.plugin', name: 'transport-netty3-client', version: '5.0.0'
testCompile group: 'org.elasticsearch.client', name: 'transport', version: '5.0.0'
latestDepTestCompile group: 'org.elasticsearch.plugin', name: 'transport-netty3-client', version: '+'
latestDepTestCompile group: 'org.elasticsearch.client', name: 'transport', version: '+'
}

View File

@ -0,0 +1,97 @@
package datadog.trace.instrumentation.elasticsearch5;
import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.DDAdvice;
import datadog.trace.agent.tooling.DDTransformers;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.api.DDTags;
import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.tag.Tags;
import io.opentracing.util.GlobalTracer;
import java.util.Collections;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import org.elasticsearch.action.Action;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionResponse;
@AutoService(Instrumenter.class)
public class Elasticsearch5TransportClientInstrumentation extends Instrumenter.Configurable {
public Elasticsearch5TransportClientInstrumentation() {
super("elasticsearch", "elasticsearch-transport", "elasticsearch-transport-5");
}
@Override
protected boolean defaultEnabled() {
return false;
}
@Override
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(named("org.elasticsearch.client.support.AbstractClient")),
// If we want to be more generic, we could instrument the interface instead:
// .and(hasSuperType(named("org.elasticsearch.client.ElasticsearchClient"))))
classLoaderHasClasses("org.elasticsearch.percolator.TransportMultiPercolateAction"))
.transform(DDTransformers.defaultTransformers())
.transform(
DDAdvice.create()
.advice(
isMethod()
.and(named("execute"))
.and(takesArgument(0, named("org.elasticsearch.action.Action")))
.and(takesArgument(1, named("org.elasticsearch.action.ActionRequest")))
.and(takesArgument(2, named("org.elasticsearch.action.ActionListener"))),
ElasticsearchTransportClientAdvice.class.getName()))
.asDecorator();
}
public static class ElasticsearchTransportClientAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static Scope startSpan(
@Advice.Argument(0) final Action action,
@Advice.Argument(1) final ActionRequest actionRequest,
@Advice.Argument(value = 2, readOnly = false)
ActionListener<ActionResponse> actionListener) {
final Scope scope =
GlobalTracer.get()
.buildSpan("elasticsearch.query")
.withTag(DDTags.SERVICE_NAME, "elasticsearch")
.withTag(DDTags.RESOURCE_NAME, action.getClass().getSimpleName())
.withTag(Tags.COMPONENT.getKey(), "elasticsearch-java")
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
.withTag("elasticsearch.action", action.getClass().getSimpleName())
.withTag("elasticsearch.request", actionRequest.getClass().getSimpleName())
.startActive(false);
actionListener = new TransportActionListener<>(actionListener, scope.span());
return scope;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Enter final Scope scope, @Advice.Thrown final Throwable throwable) {
if (throwable != null) {
final Span span = scope.span();
Tags.ERROR.set(span, true);
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
span.finish();
}
scope.close();
}
}
}

View File

@ -0,0 +1,46 @@
package datadog.trace.instrumentation.elasticsearch5;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import io.opentracing.Span;
import io.opentracing.tag.Tags;
import java.util.Collections;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionResponse;
public class TransportActionListener<T extends ActionResponse> implements ActionListener<T> {
private final ActionListener<T> listener;
private final Span span;
public TransportActionListener(final ActionListener<T> listener, final Span span) {
this.listener = listener;
this.span = span;
}
@Override
public void onResponse(final T response) {
if (response.remoteAddress() != null) {
Tags.PEER_HOSTNAME.set(span, response.remoteAddress().getHost());
Tags.PEER_HOST_IPV4.set(span, response.remoteAddress().getAddress());
Tags.PEER_PORT.set(span, response.remoteAddress().getPort());
}
try {
listener.onResponse(response);
} finally {
span.finish();
}
}
@Override
public void onFailure(final Exception e) {
span.log(Collections.singletonMap(ERROR_OBJECT, e));
try {
listener.onFailure(e);
} finally {
span.finish();
}
}
}

View File

@ -0,0 +1,85 @@
import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.agent.test.TestUtils
import io.opentracing.tag.Tags
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest
import org.elasticsearch.common.io.FileSystemUtils
import org.elasticsearch.common.settings.Settings
import org.elasticsearch.env.Environment
import org.elasticsearch.node.Node
import org.elasticsearch.node.internal.InternalSettingsPreparer
import org.elasticsearch.transport.Netty3Plugin
import spock.lang.Shared
import static datadog.trace.agent.test.ListWriterAssert.assertTraces
import static org.elasticsearch.cluster.ClusterName.CLUSTER_NAME_SETTING
class Elasticsearch5NodeClientTest extends AgentTestRunner {
static {
System.setProperty("dd.integration.elasticsearch.enabled", "true")
}
static final int HTTP_PORT = TestUtils.randomOpenPort()
static final int TCP_PORT = TestUtils.randomOpenPort()
@Shared
static Node testNode
static File esWorkingDir
def client = testNode.client()
def setupSpec() {
esWorkingDir = File.createTempFile("test-es-working-dir-", "")
esWorkingDir.delete()
esWorkingDir.mkdir()
esWorkingDir.deleteOnExit()
println "ES work dir: $esWorkingDir"
def settings = Settings.builder()
.put("path.home", esWorkingDir.path)
.put("http.port", HTTP_PORT)
.put("transport.tcp.port", TCP_PORT)
.put("transport.type", "netty3")
.put("http.type", "netty3")
.put(CLUSTER_NAME_SETTING.getKey(), "test-cluster")
.build()
testNode = new Node(new Environment(InternalSettingsPreparer.prepareSettings(settings)), [Netty3Plugin])
testNode.start()
}
def cleanupSpec() {
testNode?.close()
if (esWorkingDir != null) {
FileSystemUtils.deleteSubDirectories(esWorkingDir.toPath())
esWorkingDir.delete()
}
}
def "test elasticsearch status"() {
setup:
def result = client.admin().cluster().health(new ClusterHealthRequest())
def status = result.get().status
expect:
status.name() == "GREEN"
assertTraces(TEST_WRITER, 1) {
trace(0, 1) {
span(0) {
serviceName "elasticsearch"
resourceName "ClusterHealthAction"
operationName "elasticsearch.query"
spanType null
parent()
tags {
"$Tags.COMPONENT.key" "elasticsearch-java"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"elasticsearch.action" "ClusterHealthAction"
"elasticsearch.request" "ClusterHealthRequest"
defaultTags()
}
}
}
}
}
}

View File

@ -0,0 +1,99 @@
import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.agent.test.TestUtils
import io.opentracing.tag.Tags
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest
import org.elasticsearch.client.transport.TransportClient
import org.elasticsearch.common.io.FileSystemUtils
import org.elasticsearch.common.settings.Settings
import org.elasticsearch.common.transport.InetSocketTransportAddress
import org.elasticsearch.env.Environment
import org.elasticsearch.node.Node
import org.elasticsearch.node.internal.InternalSettingsPreparer
import org.elasticsearch.transport.Netty3Plugin
import org.elasticsearch.transport.client.PreBuiltTransportClient
import spock.lang.Shared
import static datadog.trace.agent.test.ListWriterAssert.assertTraces
import static org.elasticsearch.cluster.ClusterName.CLUSTER_NAME_SETTING
class Elasticsearch5TransportClientTest extends AgentTestRunner {
static {
System.setProperty("dd.integration.elasticsearch.enabled", "true")
}
static final int HTTP_PORT = TestUtils.randomOpenPort()
static final int TCP_PORT = TestUtils.randomOpenPort()
@Shared
static Node testNode
static File esWorkingDir
@Shared
static TransportClient client
def setupSpec() {
esWorkingDir = File.createTempFile("test-es-working-dir-", "")
esWorkingDir.delete()
esWorkingDir.mkdir()
esWorkingDir.deleteOnExit()
println "ES work dir: $esWorkingDir"
def settings = Settings.builder()
.put("path.home", esWorkingDir.path)
.put("http.port", HTTP_PORT)
.put("transport.tcp.port", TCP_PORT)
.put("transport.type", "netty3")
.put("http.type", "netty3")
.put(CLUSTER_NAME_SETTING.getKey(), "test-cluster")
.build()
testNode = new Node(new Environment(InternalSettingsPreparer.prepareSettings(settings)), [Netty3Plugin])
testNode.start()
client = new PreBuiltTransportClient(
Settings.builder()
.put(CLUSTER_NAME_SETTING.getKey(), "test-cluster")
.build()
)
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), TCP_PORT))
}
def cleanupSpec() {
testNode?.close()
if (esWorkingDir != null) {
FileSystemUtils.deleteSubDirectories(esWorkingDir.toPath())
esWorkingDir.delete()
}
}
def "test elasticsearch status"() {
setup:
def result = client.admin().cluster().health(new ClusterHealthRequest())
def status = result.get().status
expect:
status.name() == "GREEN"
assertTraces(TEST_WRITER, 1) {
trace(0, 1) {
span(0) {
serviceName "elasticsearch"
resourceName "ClusterHealthAction"
operationName "elasticsearch.query"
spanType null
parent()
tags {
"$Tags.COMPONENT.key" "elasticsearch-java"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"$Tags.PEER_HOSTNAME.key" "127.0.0.1"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" TCP_PORT
"elasticsearch.action" "ClusterHealthAction"
"elasticsearch.request" "ClusterHealthRequest"
defaultTags()
}
}
}
}
}
}

View File

@ -0,0 +1,53 @@
apply plugin: 'version-scan'
versionScan {
group = "org.elasticsearch.client"
module = "transport"
versions = "[6.0,)"
legacyGroup = "org.elasticsearch"
legacyModule = "elasticsearch"
scanDependencies = true
verifyPresent = [
"org.elasticsearch.client.RestClientBuilder\$2": null,
]
}
apply from: "${rootDir}/gradle/java.gradle"
apply plugin: 'org.unbroken-dome.test-sets'
testSets {
latestDepTest {
dirName = 'test'
}
}
dependencies {
compileOnly group: 'org.elasticsearch.client', name: 'transport', version: '6.0.0'
compile project(':dd-java-agent:agent-tooling')
compile deps.bytebuddy
compile deps.opentracing
annotationProcessor deps.autoservice
implementation deps.autoservice
testCompile project(':dd-java-agent:testing')
// Ensure no cross interference
testCompile project(':dd-java-agent:instrumentation:elasticsearch-rest-5')
// Include httpclient instrumentation for testing because it is a dependency for elasticsearch-rest-client.
// It doesn't actually work though. They use HttpAsyncClient, which isn't currently instrumented.
// TODO: add HttpAsyncClient instrumentation when that is complete.
testCompile project(':dd-java-agent:instrumentation:apache-httpclient-4.3')
// TODO: add netty instrumentation when that is complete.
testCompile group: 'org.elasticsearch.plugin', name: 'transport-netty4-client', version: '6.0.0'
testCompile group: 'org.elasticsearch.client', name: 'transport', version: '6.0.0'
testCompile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0'
testCompile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.11.0'
latestDepTestCompile group: 'org.elasticsearch.plugin', name: 'transport-netty4-client', version: '+'
latestDepTestCompile group: 'org.elasticsearch.client', name: 'transport', version: '+'
latestDepTestCompile group: 'org.elasticsearch.client', name: 'elasticsearch-rest-client', version: '+'
}

View File

@ -0,0 +1,101 @@
package datadog.trace.instrumentation.elasticsearch6;
import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.DDAdvice;
import datadog.trace.agent.tooling.DDTransformers;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.api.DDTags;
import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.tag.Tags;
import io.opentracing.util.GlobalTracer;
import java.util.Collections;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import org.elasticsearch.action.Action;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionResponse;
/**
* Most of this class is identical to version 5's instrumentation, but they changed an interface to
* an abstract class, so the bytecode isn't directly compatible.
*/
@AutoService(Instrumenter.class)
public class Elasticsearch6TransportClientInstrumentation extends Instrumenter.Configurable {
public Elasticsearch6TransportClientInstrumentation() {
super("elasticsearch", "elasticsearch-transport", "elasticsearch-transport-6");
}
@Override
protected boolean defaultEnabled() {
return false;
}
@Override
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(named("org.elasticsearch.client.support.AbstractClient")),
// If we want to be more generic, we could instrument the interface instead:
// .and(hasSuperType(named("org.elasticsearch.client.ElasticsearchClient"))))
classLoaderHasClasses("org.elasticsearch.client.RestClientBuilder$2"))
.transform(DDTransformers.defaultTransformers())
.transform(
DDAdvice.create()
.advice(
isMethod()
.and(named("execute"))
.and(takesArgument(0, named("org.elasticsearch.action.Action")))
.and(takesArgument(1, named("org.elasticsearch.action.ActionRequest")))
.and(takesArgument(2, named("org.elasticsearch.action.ActionListener"))),
Elasticsearch6TransportClientAdvice.class.getName()))
.asDecorator();
}
public static class Elasticsearch6TransportClientAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static Scope startSpan(
@Advice.Argument(0) final Action action,
@Advice.Argument(1) final ActionRequest actionRequest,
@Advice.Argument(value = 2, readOnly = false)
ActionListener<ActionResponse> actionListener) {
final Scope scope =
GlobalTracer.get()
.buildSpan("elasticsearch.query")
.withTag(DDTags.SERVICE_NAME, "elasticsearch")
.withTag(DDTags.RESOURCE_NAME, action.getClass().getSimpleName())
.withTag(Tags.COMPONENT.getKey(), "elasticsearch-java")
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
.withTag("elasticsearch.action", action.getClass().getSimpleName())
.withTag("elasticsearch.request", actionRequest.getClass().getSimpleName())
.startActive(false);
actionListener = new TransportActionListener<>(actionListener, scope.span());
return scope;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Enter final Scope scope, @Advice.Thrown final Throwable throwable) {
if (throwable != null) {
final Span span = scope.span();
Tags.ERROR.set(span, true);
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
span.finish();
}
scope.close();
}
}
}

View File

@ -0,0 +1,50 @@
package datadog.trace.instrumentation.elasticsearch6;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import io.opentracing.Span;
import io.opentracing.tag.Tags;
import java.util.Collections;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionResponse;
/**
* Most of this class is identical to version 5's instrumentation, but they changed an interface to
* an abstract class, so the bytecode isn't directly compatible.
*/
public class TransportActionListener<T extends ActionResponse> implements ActionListener<T> {
private final ActionListener<T> listener;
private final Span span;
public TransportActionListener(final ActionListener<T> listener, final Span span) {
this.listener = listener;
this.span = span;
}
@Override
public void onResponse(final T response) {
if (response.remoteAddress() != null) {
Tags.PEER_HOSTNAME.set(span, response.remoteAddress().address().getHostName());
Tags.PEER_HOST_IPV4.set(span, response.remoteAddress().getAddress());
Tags.PEER_PORT.set(span, response.remoteAddress().getPort());
}
try {
listener.onResponse(response);
} finally {
span.finish();
}
}
@Override
public void onFailure(final Exception e) {
span.log(Collections.singletonMap(ERROR_OBJECT, e));
try {
listener.onFailure(e);
} finally {
span.finish();
}
}
}

View File

@ -0,0 +1,81 @@
import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.agent.test.TestUtils
import io.opentracing.tag.Tags
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest
import org.elasticsearch.common.io.FileSystemUtils
import org.elasticsearch.common.settings.Settings
import org.elasticsearch.node.InternalSettingsPreparer
import org.elasticsearch.node.Node
import org.elasticsearch.transport.Netty4Plugin
import spock.lang.Shared
import static datadog.trace.agent.test.ListWriterAssert.assertTraces
import static org.elasticsearch.cluster.ClusterName.CLUSTER_NAME_SETTING
class Elasticsearch6NodeClientTest extends AgentTestRunner {
static {
System.setProperty("dd.integration.elasticsearch.enabled", "true")
}
static final int HTTP_PORT = TestUtils.randomOpenPort()
static final int TCP_PORT = TestUtils.randomOpenPort()
@Shared
static Node testNode
static File esWorkingDir
def client = testNode.client()
def setupSpec() {
esWorkingDir = File.createTempFile("test-es-working-dir-", "")
esWorkingDir.delete()
esWorkingDir.mkdir()
esWorkingDir.deleteOnExit()
println "ES work dir: $esWorkingDir"
def settings = Settings.builder()
.put("path.home", esWorkingDir.path)
.put("http.port", HTTP_PORT)
.put("transport.tcp.port", TCP_PORT)
.put(CLUSTER_NAME_SETTING.getKey(), "test-cluster")
.build()
testNode = new Node(InternalSettingsPreparer.prepareEnvironment(settings, null), [Netty4Plugin])
testNode.start()
}
def cleanupSpec() {
testNode?.close()
if (esWorkingDir != null) {
FileSystemUtils.deleteSubDirectories(esWorkingDir.toPath())
esWorkingDir.delete()
}
}
def "test elasticsearch status"() {
setup:
def result = client.admin().cluster().health(new ClusterHealthRequest())
def status = result.get().status
expect:
status.name() == "GREEN"
assertTraces(TEST_WRITER, 1) {
trace(0, 1) {
span(0) {
serviceName "elasticsearch"
resourceName "ClusterHealthAction"
operationName "elasticsearch.query"
spanType null
tags {
"$Tags.COMPONENT.key" "elasticsearch-java"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"elasticsearch.action" "ClusterHealthAction"
"elasticsearch.request" "ClusterHealthRequest"
defaultTags()
}
}
}
}
}
}

View File

@ -0,0 +1,95 @@
import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.agent.test.TestUtils
import io.opentracing.tag.Tags
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest
import org.elasticsearch.client.transport.TransportClient
import org.elasticsearch.common.io.FileSystemUtils
import org.elasticsearch.common.settings.Settings
import org.elasticsearch.common.transport.TransportAddress
import org.elasticsearch.node.InternalSettingsPreparer
import org.elasticsearch.node.Node
import org.elasticsearch.transport.Netty4Plugin
import org.elasticsearch.transport.client.PreBuiltTransportClient
import spock.lang.Shared
import static datadog.trace.agent.test.ListWriterAssert.assertTraces
import static org.elasticsearch.cluster.ClusterName.CLUSTER_NAME_SETTING
class Elasticsearch6TransportClientTest extends AgentTestRunner {
static {
System.setProperty("dd.integration.elasticsearch.enabled", "true")
}
static final int HTTP_PORT = TestUtils.randomOpenPort()
static final int TCP_PORT = TestUtils.randomOpenPort()
@Shared
static Node testNode
static File esWorkingDir
@Shared
static TransportClient client
def setupSpec() {
esWorkingDir = File.createTempFile("test-es-working-dir-", "")
esWorkingDir.delete()
esWorkingDir.mkdir()
esWorkingDir.deleteOnExit()
println "ES work dir: $esWorkingDir"
def settings = Settings.builder()
.put("path.home", esWorkingDir.path)
.put("http.port", HTTP_PORT)
.put("transport.tcp.port", TCP_PORT)
.put(CLUSTER_NAME_SETTING.getKey(), "test-cluster")
.build()
testNode = new Node(InternalSettingsPreparer.prepareEnvironment(settings, null), [Netty4Plugin])
testNode.start()
client = new PreBuiltTransportClient(
Settings.builder()
.put(CLUSTER_NAME_SETTING.getKey(), "test-cluster")
.build()
)
client.addTransportAddress(new TransportAddress(InetAddress.getByName("localhost"), TCP_PORT))
}
def cleanupSpec() {
testNode?.close()
if (esWorkingDir != null) {
FileSystemUtils.deleteSubDirectories(esWorkingDir.toPath())
esWorkingDir.delete()
}
}
def "test elasticsearch status"() {
setup:
def result = client.admin().cluster().health(new ClusterHealthRequest())
def status = result.get().status
expect:
status.name() == "GREEN"
assertTraces(TEST_WRITER, 1) {
trace(0, 1) {
span(0) {
serviceName "elasticsearch"
resourceName "ClusterHealthAction"
operationName "elasticsearch.query"
spanType null
tags {
"$Tags.COMPONENT.key" "elasticsearch-java"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" TCP_PORT
"elasticsearch.action" "ClusterHealthAction"
"elasticsearch.request" "ClusterHealthRequest"
defaultTags()
}
}
}
}
}
}

View File

@ -1,10 +1,10 @@
apply plugin: 'version-scan'
versionScan {
group = "javax.ws.rs"
module = "jsr311-api"
versions = "(,)"
}
//apply plugin: 'version-scan'
//
//versionScan {
// group = "javax.ws.rs"
// module = "jsr311-api"
// versions = "(,)"
//}
apply from: "${rootDir}/gradle/java.gradle"

View File

@ -1,10 +1,10 @@
apply plugin: 'version-scan'
versionScan {
group = "javax.ws.rs"
module = "jsr311-api"
versions = "(,)"
}
//apply plugin: 'version-scan'
//
//versionScan {
// group = "javax.ws.rs"
// module = "jsr311-api"
// versions = "(,)"
//}
apply from: "${rootDir}/gradle/java.gradle"

View File

@ -2,8 +2,9 @@ apply plugin: 'version-scan'
versionScan {
group = "io.ratpack"
module = 'ratpack'
module = 'ratpack-core'
versions = "[1.4.0,)"
scanMethods = true
verifyPresent = [
"ratpack.path.PathBinding": "getDescription",
]

View File

@ -116,14 +116,19 @@ public class DDTracer implements io.opentracing.Tracer {
this.sampler = sampler;
this.spanTags = defaultSpanTags;
Runtime.getRuntime()
.addShutdownHook(
new Thread() {
@Override
public void run() {
DDTracer.this.close();
}
});
try {
Runtime.getRuntime()
.addShutdownHook(
new Thread() {
@Override
public void run() {
DDTracer.this.close();
}
});
} catch (final IllegalStateException ex) {
// The JVM might be shutting down.
log.debug("Error adding shutdown hook.", ex);
}
registry = new CodecRegistry();
registry.register(Format.Builtin.HTTP_HEADERS, new HTTPCodec(taggedHeaders));

View File

@ -14,6 +14,10 @@ include ':dd-java-agent:instrumentation:aws-java-sdk-1.11.0'
include ':dd-java-agent:instrumentation:aws-java-sdk-1.11.106'
include ':dd-java-agent:instrumentation:classloaders'
include ':dd-java-agent:instrumentation:datastax-cassandra-3.2'
include ':dd-java-agent:instrumentation:elasticsearch-rest-5'
include ':dd-java-agent:instrumentation:elasticsearch-transport-2'
include ':dd-java-agent:instrumentation:elasticsearch-transport-5'
include ':dd-java-agent:instrumentation:elasticsearch-transport-6'
include ':dd-java-agent:instrumentation:hystrix-1.4'
include ':dd-java-agent:instrumentation:jax-rs-annotations'
include ':dd-java-agent:instrumentation:jax-rs-client'