Merge pull request #613 from DataDog/mar-kolya/improve-httpurl-connection
Improve HttpUrlConnection instrumentation
This commit is contained in:
commit
7cd3438d6b
|
@ -29,6 +29,10 @@ public class MongoAsyncClientInstrumentationTest {
|
|||
public static void setup() throws Exception {
|
||||
IntegrationTestUtils.registerOrReplaceGlobalTracer(tracer);
|
||||
MongoClientInstrumentationTest.startLocalMongo();
|
||||
// Embeded Mongo uses HttpUrlConnection to download things so we have to clear traces before
|
||||
// going to tests
|
||||
writer.clear();
|
||||
|
||||
client = MongoClients.create("mongodb://" + MONGO_HOST + ":" + MONGO_PORT);
|
||||
}
|
||||
|
||||
|
@ -50,7 +54,7 @@ public class MongoAsyncClientInstrumentationTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void insertOperation() throws InterruptedException, Exception {
|
||||
public void insertOperation() throws Exception {
|
||||
final MongoDatabase db = client.getDatabase(MONGO_DB_NAME);
|
||||
final String collectionName = "asyncCollection";
|
||||
final AtomicBoolean done = new AtomicBoolean(false);
|
||||
|
|
|
@ -16,7 +16,7 @@ import de.flapdoodle.embed.mongo.config.Net;
|
|||
import de.flapdoodle.embed.mongo.distribution.Version;
|
||||
import de.flapdoodle.embed.process.runtime.Network;
|
||||
import io.opentracing.tag.Tags;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import org.bson.Document;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
|
@ -36,7 +36,6 @@ public class MongoClientInstrumentationTest {
|
|||
|
||||
public static void startLocalMongo() throws Exception {
|
||||
final MongodStarter starter = MongodStarter.getDefaultInstance();
|
||||
|
||||
final IMongodConfig mongodConfig =
|
||||
new MongodConfigBuilder()
|
||||
.version(Version.Main.PRODUCTION)
|
||||
|
@ -62,6 +61,9 @@ public class MongoClientInstrumentationTest {
|
|||
public static void setup() throws Exception {
|
||||
IntegrationTestUtils.registerOrReplaceGlobalTracer(tracer);
|
||||
startLocalMongo();
|
||||
// Embeded Mongo uses HttpUrlConnection to download things so we have to clear traces before
|
||||
// going to tests
|
||||
writer.clear();
|
||||
|
||||
client = new MongoClient(MONGO_HOST, MONGO_PORT);
|
||||
}
|
||||
|
@ -84,7 +86,7 @@ public class MongoClientInstrumentationTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void insertOperation() throws UnknownHostException {
|
||||
public void insertOperation() throws TimeoutException, InterruptedException {
|
||||
final MongoDatabase db = client.getDatabase(MONGO_DB_NAME);
|
||||
final String collectionName = "testCollection";
|
||||
db.createCollection(collectionName);
|
||||
|
|
|
@ -105,7 +105,9 @@ class Elasticsearch2SpringTemplateTest extends AgentTestRunner {
|
|||
def "test elasticsearch get"() {
|
||||
expect:
|
||||
template.createIndex(indexName)
|
||||
TEST_WRITER.waitForTraces(1)
|
||||
template.getClient().admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT)
|
||||
TEST_WRITER.waitForTraces(2)
|
||||
|
||||
when:
|
||||
NativeSearchQuery query = new NativeSearchQueryBuilder()
|
||||
|
@ -277,7 +279,10 @@ class Elasticsearch2SpringTemplateTest extends AgentTestRunner {
|
|||
def "test results extractor"() {
|
||||
setup:
|
||||
template.createIndex(indexName)
|
||||
TEST_WRITER.waitForTraces(1)
|
||||
testNode.client().admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT)
|
||||
TEST_WRITER.waitForTraces(2)
|
||||
|
||||
template.index(IndexQueryBuilder.newInstance()
|
||||
.withObject(new Doc(id: 1, data: "doc a"))
|
||||
.withIndexName(indexName)
|
||||
|
|
|
@ -105,7 +105,9 @@ class Elasticsearch2SpringTemplateTest extends AgentTestRunner {
|
|||
def "test elasticsearch get"() {
|
||||
expect:
|
||||
template.createIndex(indexName)
|
||||
TEST_WRITER.waitForTraces(1)
|
||||
template.getClient().admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT)
|
||||
TEST_WRITER.waitForTraces(2)
|
||||
|
||||
when:
|
||||
NativeSearchQuery query = new NativeSearchQueryBuilder()
|
||||
|
|
|
@ -125,7 +125,9 @@ class Elasticsearch53SpringTemplateTest extends AgentTestRunner {
|
|||
def "test elasticsearch get"() {
|
||||
expect:
|
||||
template.createIndex(indexName)
|
||||
TEST_WRITER.waitForTraces(1)
|
||||
template.getClient().admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT)
|
||||
TEST_WRITER.waitForTraces(2)
|
||||
|
||||
when:
|
||||
NativeSearchQuery query = new NativeSearchQueryBuilder()
|
||||
|
@ -305,7 +307,10 @@ class Elasticsearch53SpringTemplateTest extends AgentTestRunner {
|
|||
def "test results extractor"() {
|
||||
setup:
|
||||
template.createIndex(indexName)
|
||||
TEST_WRITER.waitForTraces(1)
|
||||
testNode.client().admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT)
|
||||
TEST_WRITER.waitForTraces(2)
|
||||
|
||||
template.index(IndexQueryBuilder.newInstance()
|
||||
.withObject(new Doc(id: 1, data: "doc a"))
|
||||
.withIndexName(indexName)
|
||||
|
|
|
@ -17,11 +17,14 @@ import datadog.trace.bootstrap.InstrumentationContext;
|
|||
import io.opentracing.Scope;
|
||||
import io.opentracing.Span;
|
||||
import io.opentracing.Tracer;
|
||||
import io.opentracing.propagation.Format;
|
||||
import io.opentracing.propagation.TextMap;
|
||||
import io.opentracing.tag.Tags;
|
||||
import io.opentracing.util.GlobalTracer;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
|
@ -35,11 +38,6 @@ public class HttpUrlConnectionInstrumentation extends Instrumenter.Default {
|
|||
super("httpurlconnection");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean defaultEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return safeHasSuperType(named("java.net.HttpURLConnection"))
|
||||
|
@ -50,15 +48,16 @@ public class HttpUrlConnectionInstrumentation extends Instrumenter.Default {
|
|||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
HttpUrlConnectionInstrumentation.class.getName() + "$HttpURLState",
|
||||
HttpUrlConnectionInstrumentation.class.getName() + "$HttpURLState$1"
|
||||
HttpUrlConnectionInstrumentation.class.getName() + "$HeadersInjectAdapter",
|
||||
HttpUrlConnectionInstrumentation.class.getName() + "$HttpUrlState",
|
||||
HttpUrlConnectionInstrumentation.class.getName() + "$HttpUrlState$1"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> contextStore() {
|
||||
return Collections.singletonMap(
|
||||
"java.net.HttpURLConnection", getClass().getName() + "$HttpURLState");
|
||||
"java.net.HttpURLConnection", getClass().getName() + "$HttpUrlState");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -73,131 +72,170 @@ public class HttpUrlConnectionInstrumentation extends Instrumenter.Default {
|
|||
public static class HttpUrlConnectionAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static Scope startSpan(
|
||||
public static HttpUrlState methodEnter(
|
||||
@Advice.This final HttpURLConnection thiz,
|
||||
@Advice.FieldValue("connected") final boolean connected,
|
||||
@Advice.Origin("#m") final String methodName) {
|
||||
final ContextStore<HttpURLConnection, HttpURLState> contextStore =
|
||||
InstrumentationContext.get(HttpURLConnection.class, HttpURLState.class);
|
||||
final HttpURLState state = contextStore.putIfAbsent(thiz, HttpURLState.FACTORY);
|
||||
@Advice.FieldValue("connected") final boolean connected) {
|
||||
|
||||
String operationName = "http.request";
|
||||
final ContextStore<HttpURLConnection, HttpUrlState> contextStore =
|
||||
InstrumentationContext.get(HttpURLConnection.class, HttpUrlState.class);
|
||||
final HttpUrlState state = contextStore.putIfAbsent(thiz, HttpUrlState.FACTORY);
|
||||
|
||||
switch (methodName) {
|
||||
case "connect":
|
||||
if (connected) {
|
||||
return null;
|
||||
synchronized (state) {
|
||||
/*
|
||||
* AgentWriter uses HttpURLConnection to report to the trace-agent. We don't want to trace
|
||||
* those requests. Check after the connected test above because getRequestProperty will
|
||||
* throw an exception if already connected.
|
||||
*/
|
||||
final boolean isTraceRequest =
|
||||
Thread.currentThread().getName().equals("dd-agent-writer")
|
||||
|| (!connected && thiz.getRequestProperty("Datadog-Meta-Lang") != null);
|
||||
if (isTraceRequest) {
|
||||
state.finish();
|
||||
return null;
|
||||
}
|
||||
|
||||
final int callDepth = CallDepthThreadLocalMap.incrementCallDepth(HttpURLConnection.class);
|
||||
if (callDepth > 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!state.hasSpan() && !state.isFinished()) {
|
||||
final Span span = state.startSpan(thiz);
|
||||
if (!connected) {
|
||||
GlobalTracer.get()
|
||||
.inject(
|
||||
span.context(), Format.Builtin.HTTP_HEADERS, new HeadersInjectAdapter(thiz));
|
||||
}
|
||||
/*
|
||||
* Ideally, we would like to only have a single span for each of the output and input streams,
|
||||
* but since headers are also sent on connect(), there wouldn't be a span to mark as parent if
|
||||
* we don't create a span here.
|
||||
*/
|
||||
operationName += ".connect";
|
||||
break;
|
||||
|
||||
case "getOutputStream":
|
||||
if (state.calledOutputStream) {
|
||||
return null;
|
||||
}
|
||||
state.calledOutputStream = true;
|
||||
operationName += ".output-stream";
|
||||
break;
|
||||
|
||||
case "getInputStream":
|
||||
if (state.calledInputStream) {
|
||||
return null;
|
||||
}
|
||||
state.calledInputStream = true;
|
||||
operationName += ".input-stream";
|
||||
break;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
/*
|
||||
* AgentWriter uses HttpURLConnection to report to the trace-agent. We don't want to trace
|
||||
* those requests. Check after the connected test above because getRequestProperty will
|
||||
* throw an exception if already connected.
|
||||
*/
|
||||
final boolean isTraceRequest =
|
||||
Thread.currentThread().getName().equals("dd-agent-writer")
|
||||
|| (!connected && thiz.getRequestProperty("Datadog-Meta-Lang") != null);
|
||||
if (isTraceRequest) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Tracer tracer = GlobalTracer.get();
|
||||
if (tracer.activeSpan() == null) {
|
||||
// We don't want this as a top level span.
|
||||
return null;
|
||||
}
|
||||
|
||||
final int callDepth = CallDepthThreadLocalMap.incrementCallDepth(HttpURLConnection.class);
|
||||
if (callDepth > 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Scope scope =
|
||||
tracer
|
||||
.buildSpan(operationName)
|
||||
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
|
||||
.withTag(DDTags.SPAN_TYPE, DDSpanTypes.HTTP_CLIENT)
|
||||
.startActive(true);
|
||||
|
||||
final URL url = thiz.getURL();
|
||||
final Span span = scope.span();
|
||||
Tags.COMPONENT.set(scope.span(), thiz.getClass().getSimpleName());
|
||||
Tags.HTTP_URL.set(span, url.toString());
|
||||
Tags.PEER_HOSTNAME.set(span, url.getHost());
|
||||
if (url.getPort() > 0) {
|
||||
Tags.PEER_PORT.set(span, url.getPort());
|
||||
} else if (thiz instanceof HttpsURLConnection) {
|
||||
Tags.PEER_PORT.set(span, 443);
|
||||
} else {
|
||||
Tags.PEER_PORT.set(span, 80);
|
||||
}
|
||||
Tags.HTTP_METHOD.set(span, thiz.getRequestMethod());
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void stopSpan(
|
||||
public static void methodExit(
|
||||
@Advice.Enter final HttpUrlState state,
|
||||
@Advice.FieldValue("responseCode") final int responseCode,
|
||||
@Advice.Enter final Scope scope,
|
||||
@Advice.Thrown final Throwable throwable) {
|
||||
@Advice.Thrown final Throwable throwable,
|
||||
@Advice.Origin("#m") final String methodName) {
|
||||
|
||||
if (scope == null) {
|
||||
if (state == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Span span = scope.span();
|
||||
if (throwable != null) {
|
||||
Tags.ERROR.set(span, true);
|
||||
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
|
||||
} else if (responseCode > 0) {
|
||||
/*
|
||||
* responseCode field cache is sometimes not populated.
|
||||
* We can't call getResponseCode() due to some unwanted side-effects
|
||||
* (e.g. breaks getOutputStream).
|
||||
*/
|
||||
Tags.HTTP_STATUS.set(span, responseCode);
|
||||
synchronized (state) {
|
||||
if (state.hasSpan() && !state.isFinished()) {
|
||||
if (throwable != null) {
|
||||
state.finishSpan(throwable);
|
||||
} else if ("getInputStream".equals(methodName)) {
|
||||
state.finishSpan(responseCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
scope.close();
|
||||
|
||||
CallDepthThreadLocalMap.reset(HttpURLConnection.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static class HttpURLState {
|
||||
public static class HeadersInjectAdapter implements TextMap {
|
||||
|
||||
public static final ContextStore.Factory<HttpURLState> FACTORY =
|
||||
new ContextStore.Factory<HttpURLState>() {
|
||||
private final HttpURLConnection connection;
|
||||
|
||||
public HeadersInjectAdapter(final HttpURLConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(final String key, final String value) {
|
||||
try {
|
||||
connection.setRequestProperty(key, value);
|
||||
} catch (final IllegalStateException e) {
|
||||
// There are cases when this can through an exception. E.g. some implementations have
|
||||
// 'connecting' state. Just guard against that here, there is not much we can do at this
|
||||
// point.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, String>> iterator() {
|
||||
throw new UnsupportedOperationException(
|
||||
"This class should be used only with tracer#inject()");
|
||||
}
|
||||
}
|
||||
|
||||
public static class HttpUrlState {
|
||||
|
||||
public static final String OPERATION_NAME = "http.request";
|
||||
public static final String COMPONENT_NAME = "http-url-connection";
|
||||
|
||||
public static final ContextStore.Factory<HttpUrlState> FACTORY =
|
||||
new ContextStore.Factory<HttpUrlState>() {
|
||||
@Override
|
||||
public HttpURLState create() {
|
||||
return new HttpURLState();
|
||||
public HttpUrlState create() {
|
||||
return new HttpUrlState();
|
||||
}
|
||||
};
|
||||
|
||||
public boolean calledOutputStream = false;
|
||||
public boolean calledInputStream = false;
|
||||
private volatile Span span = null;
|
||||
private volatile boolean finished = false;
|
||||
|
||||
public Span startSpan(final HttpURLConnection connection) {
|
||||
final Tracer.SpanBuilder builder =
|
||||
GlobalTracer.get()
|
||||
.buildSpan(OPERATION_NAME)
|
||||
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
|
||||
.withTag(DDTags.SPAN_TYPE, DDSpanTypes.HTTP_CLIENT);
|
||||
try (final Scope scope = builder.startActive(false)) {
|
||||
span = scope.span();
|
||||
final URL url = connection.getURL();
|
||||
Tags.COMPONENT.set(span, COMPONENT_NAME);
|
||||
Tags.HTTP_URL.set(span, url.toString());
|
||||
Tags.PEER_HOSTNAME.set(span, url.getHost());
|
||||
if (url.getPort() > 0) {
|
||||
Tags.PEER_PORT.set(span, url.getPort());
|
||||
} else if (connection instanceof HttpsURLConnection) {
|
||||
Tags.PEER_PORT.set(span, 443);
|
||||
} else {
|
||||
Tags.PEER_PORT.set(span, 80);
|
||||
}
|
||||
Tags.HTTP_METHOD.set(span, connection.getRequestMethod());
|
||||
return span;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasSpan() {
|
||||
return span != null;
|
||||
}
|
||||
|
||||
public boolean isFinished() {
|
||||
return finished;
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
finished = true;
|
||||
}
|
||||
|
||||
public void finishSpan(final Throwable throwable) {
|
||||
try (final Scope scope = GlobalTracer.get().scopeManager().activate(span, true)) {
|
||||
Tags.ERROR.set(span, true);
|
||||
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
|
||||
}
|
||||
span = null;
|
||||
finished = true;
|
||||
}
|
||||
|
||||
public void finishSpan(final int responseCode) {
|
||||
/*
|
||||
* responseCode field is sometimes not populated.
|
||||
* We can't call getResponseCode() due to some unwanted side-effects
|
||||
* (e.g. breaks getOutputStream).
|
||||
*/
|
||||
if (responseCode > 0) {
|
||||
try (final Scope scope = GlobalTracer.get().scopeManager().activate(span, true)) {
|
||||
Tags.HTTP_STATUS.set(span, responseCode);
|
||||
span = null;
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
package datadog.trace.instrumentation.http_url_connection;
|
||||
|
||||
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.takesArgument;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import datadog.trace.agent.tooling.Instrumenter;
|
||||
import io.opentracing.Span;
|
||||
import io.opentracing.Tracer;
|
||||
import io.opentracing.propagation.Format;
|
||||
import io.opentracing.propagation.TextMap;
|
||||
import io.opentracing.util.GlobalTracer;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
import sun.net.www.MessageHeader;
|
||||
import sun.net.www.http.HttpClient;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public class SunHttpClientInstrumentation extends Instrumenter.Default {
|
||||
|
||||
public SunHttpClientInstrumentation() {
|
||||
super("httpurlconnection");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean defaultEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return named("sun.net.www.http.HttpClient");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
SunHttpClientInstrumentation.class.getName() + "$MessageHeadersInjectAdapter"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ElementMatcher, String> transformers() {
|
||||
return Collections.<ElementMatcher, String>singletonMap(
|
||||
isMethod()
|
||||
.and(isPublic())
|
||||
.and(named("writeRequests"))
|
||||
.and(takesArgument(0, named("sun.net.www.MessageHeader")))
|
||||
// exclude the delegating method:
|
||||
.and(takesArguments(1).or(takesArguments(2))),
|
||||
InjectAdvice.class.getName());
|
||||
}
|
||||
|
||||
public static class InjectAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static void inject(
|
||||
@Advice.Argument(0) final MessageHeader header, @Advice.This final HttpClient client) {
|
||||
final Tracer tracer = GlobalTracer.get();
|
||||
final Span span = tracer.activeSpan();
|
||||
|
||||
if (span != null) {
|
||||
tracer.inject(
|
||||
span.context(), Format.Builtin.HTTP_HEADERS, new MessageHeadersInjectAdapter(header));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class MessageHeadersInjectAdapter implements TextMap {
|
||||
|
||||
private final MessageHeader header;
|
||||
|
||||
public MessageHeadersInjectAdapter(final MessageHeader header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(final String key, final String value) {
|
||||
header.setIfNotSet(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, String>> iterator() {
|
||||
throw new UnsupportedOperationException(
|
||||
"This class should be used only with tracer#inject()");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,11 +31,6 @@ public class UrlInstrumentation extends Instrumenter.Default {
|
|||
super("urlconnection", "httpurlconnection");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean defaultEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return is(URL.class);
|
||||
|
|
|
@ -9,11 +9,10 @@ import spock.lang.Shared
|
|||
|
||||
import static datadog.trace.agent.test.TestUtils.runUnderTrace
|
||||
import static datadog.trace.agent.test.server.http.TestHttpServer.httpServer
|
||||
import static datadog.trace.instrumentation.http_url_connection.HttpUrlConnectionInstrumentation.HttpUrlState.COMPONENT_NAME
|
||||
import static datadog.trace.instrumentation.http_url_connection.HttpUrlConnectionInstrumentation.HttpUrlState.OPERATION_NAME
|
||||
|
||||
class HttpUrlConnectionTest extends AgentTestRunner {
|
||||
static {
|
||||
System.setProperty("dd.integration.httpurlconnection.enabled", "true")
|
||||
}
|
||||
|
||||
static final RESPONSE = "<html><body><h1>Hello test.</h1>"
|
||||
static final STATUS = 202
|
||||
|
@ -68,11 +67,11 @@ class HttpUrlConnectionTest extends AgentTestRunner {
|
|||
}
|
||||
}
|
||||
span(1) {
|
||||
operationName "http.request.input-stream"
|
||||
operationName OPERATION_NAME
|
||||
childOf span(0)
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "HttpURLConnection"
|
||||
"$Tags.COMPONENT.key" COMPONENT_NAME
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||
"$DDTags.SPAN_TYPE" DDSpanTypes.HTTP_CLIENT
|
||||
"$Tags.HTTP_URL.key" "$server.address"
|
||||
|
@ -84,11 +83,11 @@ class HttpUrlConnectionTest extends AgentTestRunner {
|
|||
}
|
||||
}
|
||||
span(2) {
|
||||
operationName "http.request.input-stream"
|
||||
operationName OPERATION_NAME
|
||||
childOf span(0)
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "HttpURLConnection"
|
||||
"$Tags.COMPONENT.key" COMPONENT_NAME
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||
"$DDTags.SPAN_TYPE" DDSpanTypes.HTTP_CLIENT
|
||||
"$Tags.HTTP_URL.key" "$server.address"
|
||||
|
@ -144,11 +143,11 @@ class HttpUrlConnectionTest extends AgentTestRunner {
|
|||
}
|
||||
}
|
||||
span(1) {
|
||||
operationName "http.request.input-stream"
|
||||
operationName OPERATION_NAME
|
||||
childOf span(0)
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "HttpURLConnection"
|
||||
"$Tags.COMPONENT.key" COMPONENT_NAME
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||
"$DDTags.SPAN_TYPE" DDSpanTypes.HTTP_CLIENT
|
||||
"$Tags.HTTP_URL.key" "$server.address"
|
||||
|
@ -160,11 +159,11 @@ class HttpUrlConnectionTest extends AgentTestRunner {
|
|||
}
|
||||
}
|
||||
span(2) {
|
||||
operationName "http.request.input-stream"
|
||||
operationName OPERATION_NAME
|
||||
childOf span(0)
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "HttpURLConnection"
|
||||
"$Tags.COMPONENT.key" COMPONENT_NAME
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||
"$DDTags.SPAN_TYPE" DDSpanTypes.HTTP_CLIENT
|
||||
"$Tags.HTTP_URL.key" "$server.address"
|
||||
|
@ -204,11 +203,11 @@ class HttpUrlConnectionTest extends AgentTestRunner {
|
|||
}
|
||||
}
|
||||
span(1) {
|
||||
operationName "http.request.input-stream"
|
||||
operationName OPERATION_NAME
|
||||
childOf span(0)
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "HttpURLConnection"
|
||||
"$Tags.COMPONENT.key" COMPONENT_NAME
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||
"$DDTags.SPAN_TYPE" DDSpanTypes.HTTP_CLIENT
|
||||
"$Tags.HTTP_URL.key" "$server.address"
|
||||
|
@ -246,11 +245,11 @@ class HttpUrlConnectionTest extends AgentTestRunner {
|
|||
}
|
||||
}
|
||||
span(1) {
|
||||
operationName "http.request.input-stream"
|
||||
operationName OPERATION_NAME
|
||||
childOf span(0)
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "HttpURLConnection"
|
||||
"$Tags.COMPONENT.key" COMPONENT_NAME
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||
"$DDTags.SPAN_TYPE" DDSpanTypes.HTTP_CLIENT
|
||||
"$Tags.HTTP_URL.key" "$server.address"
|
||||
|
@ -297,7 +296,7 @@ class HttpUrlConnectionTest extends AgentTestRunner {
|
|||
expect:
|
||||
assertTraces(2) {
|
||||
server.distributedRequestTrace(it, 0, TEST_WRITER[1][1])
|
||||
trace(1, 3) {
|
||||
trace(1, 2) {
|
||||
span(0) {
|
||||
operationName "someTrace"
|
||||
parent()
|
||||
|
@ -307,11 +306,11 @@ class HttpUrlConnectionTest extends AgentTestRunner {
|
|||
}
|
||||
}
|
||||
span(1) {
|
||||
operationName "http.request.input-stream"
|
||||
operationName OPERATION_NAME
|
||||
childOf span(0)
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "HttpURLConnection"
|
||||
"$Tags.COMPONENT.key" COMPONENT_NAME
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||
"$DDTags.SPAN_TYPE" DDSpanTypes.HTTP_CLIENT
|
||||
"$Tags.HTTP_URL.key" "$server.address"
|
||||
|
@ -322,21 +321,6 @@ class HttpUrlConnectionTest extends AgentTestRunner {
|
|||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(2) {
|
||||
operationName "http.request.output-stream"
|
||||
childOf span(0)
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "HttpURLConnection"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||
"$DDTags.SPAN_TYPE" DDSpanTypes.HTTP_CLIENT
|
||||
"$Tags.HTTP_URL.key" "$server.address"
|
||||
"$Tags.HTTP_METHOD.key" "POST"
|
||||
"$Tags.PEER_HOSTNAME.key" "localhost"
|
||||
"$Tags.PEER_PORT.key" server.address.port
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -380,7 +364,26 @@ class HttpUrlConnectionTest extends AgentTestRunner {
|
|||
assert lines == [RESPONSE]
|
||||
|
||||
expect:
|
||||
assertTraces(0) {}
|
||||
assertTraces(1) {
|
||||
trace(0, 1) {
|
||||
span(0) {
|
||||
operationName OPERATION_NAME
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" COMPONENT_NAME
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||
"$DDTags.SPAN_TYPE" DDSpanTypes.HTTP_CLIENT
|
||||
"$Tags.HTTP_URL.key" "$server.address"
|
||||
"$Tags.HTTP_METHOD.key" "GET"
|
||||
"$Tags.HTTP_STATUS.key" STATUS
|
||||
"$Tags.PEER_HOSTNAME.key" "localhost"
|
||||
"$Tags.PEER_PORT.key" server.address.port
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "rest template"() {
|
||||
|
@ -393,8 +396,8 @@ class HttpUrlConnectionTest extends AgentTestRunner {
|
|||
|
||||
expect:
|
||||
assertTraces(2) {
|
||||
server.distributedRequestTrace(it, 0, TEST_WRITER[1][2])
|
||||
trace(1, 4) {
|
||||
server.distributedRequestTrace(it, 0, TEST_WRITER[1][1])
|
||||
trace(1, 2) {
|
||||
span(0) {
|
||||
operationName "someTrace"
|
||||
parent()
|
||||
|
@ -404,11 +407,11 @@ class HttpUrlConnectionTest extends AgentTestRunner {
|
|||
}
|
||||
}
|
||||
span(1) {
|
||||
operationName "http.request.input-stream"
|
||||
operationName OPERATION_NAME
|
||||
childOf span(0)
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "HttpURLConnection"
|
||||
"$Tags.COMPONENT.key" COMPONENT_NAME
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||
"$DDTags.SPAN_TYPE" DDSpanTypes.HTTP_CLIENT
|
||||
"$Tags.HTTP_URL.key" "$server.address"
|
||||
|
@ -419,36 +422,6 @@ class HttpUrlConnectionTest extends AgentTestRunner {
|
|||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(2) {
|
||||
operationName "http.request.output-stream"
|
||||
childOf span(0)
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "HttpURLConnection"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||
"$DDTags.SPAN_TYPE" DDSpanTypes.HTTP_CLIENT
|
||||
"$Tags.HTTP_URL.key" "$server.address"
|
||||
"$Tags.HTTP_METHOD.key" "POST"
|
||||
"$Tags.PEER_HOSTNAME.key" "localhost"
|
||||
"$Tags.PEER_PORT.key" server.address.port
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(3) {
|
||||
operationName "http.request.connect"
|
||||
childOf span(0)
|
||||
errored false
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "HttpURLConnection"
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||
"$DDTags.SPAN_TYPE" DDSpanTypes.HTTP_CLIENT
|
||||
"$Tags.HTTP_URL.key" "$server.address"
|
||||
"$Tags.HTTP_METHOD.key" "POST"
|
||||
"$Tags.PEER_HOSTNAME.key" "localhost"
|
||||
"$Tags.PEER_PORT.key" server.address.port
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,15 +7,14 @@ import io.opentracing.tag.Tags
|
|||
import io.opentracing.util.GlobalTracer
|
||||
|
||||
import static datadog.trace.agent.test.TestUtils.runUnderTrace
|
||||
import static datadog.trace.instrumentation.http_url_connection.HttpUrlConnectionInstrumentation.HttpUrlState.COMPONENT_NAME
|
||||
import static datadog.trace.instrumentation.http_url_connection.HttpUrlConnectionInstrumentation.HttpUrlState.OPERATION_NAME
|
||||
|
||||
class UrlConnectionTest extends AgentTestRunner {
|
||||
static {
|
||||
System.setProperty("dd.integration.httpurlconnection.enabled", "true")
|
||||
}
|
||||
|
||||
private static final int INVALID_PORT = TestUtils.randomOpenPort()
|
||||
|
||||
def "trace request with connection failure"() {
|
||||
def "trace request with connection failure #scheme"() {
|
||||
when:
|
||||
runUnderTrace("someTrace") {
|
||||
URLConnection connection = url.openConnection()
|
||||
|
@ -41,11 +40,11 @@ class UrlConnectionTest extends AgentTestRunner {
|
|||
}
|
||||
}
|
||||
span(1) {
|
||||
operationName "http.request.input-stream"
|
||||
operationName OPERATION_NAME
|
||||
childOf span(0)
|
||||
errored true
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" component
|
||||
"$Tags.COMPONENT.key" COMPONENT_NAME
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||
"$DDTags.SPAN_TYPE" DDSpanTypes.HTTP_CLIENT
|
||||
"$Tags.HTTP_URL.key" "$url"
|
||||
|
@ -60,9 +59,9 @@ class UrlConnectionTest extends AgentTestRunner {
|
|||
}
|
||||
|
||||
where:
|
||||
scheme | component
|
||||
"http" | "HttpURLConnection"
|
||||
"https" | "DelegateHttpsURLConnection"
|
||||
scheme | _
|
||||
"http" | _
|
||||
"https" | _
|
||||
|
||||
url = new URI("$scheme://localhost:$INVALID_PORT").toURL()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue