Migrate Netty instrumentation to Decorator

This commit is contained in:
Tyler Benson 2019-03-01 14:40:14 -08:00
parent 246bdfe081
commit 084c2eb51f
26 changed files with 394 additions and 144 deletions

View File

@ -8,6 +8,10 @@ import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.tag.Tags;
import java.lang.reflect.Method;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.Collections;
import java.util.TreeSet;
@ -86,6 +90,22 @@ public abstract class BaseDecorator {
return span;
}
public Span onPeerConnection(final Span span, final InetSocketAddress remoteConnection) {
assert span != null;
if (remoteConnection != null) {
span.setTag(Tags.PEER_HOSTNAME.getKey(), remoteConnection.getHostName());
span.setTag(Tags.PEER_PORT.getKey(), remoteConnection.getPort());
final InetAddress remoteAddress = remoteConnection.getAddress();
if (remoteAddress instanceof Inet4Address) {
Tags.PEER_HOST_IPV4.set(span, remoteAddress.getHostAddress());
} else if (remoteAddress instanceof Inet6Address) {
Tags.PEER_HOST_IPV6.set(span, remoteAddress.getHostAddress());
}
}
return span;
}
/**
* This method is used to generate an acceptable span (operation) name based on a given method
* reference. Anonymous classes are named based on their parent.

View File

@ -28,6 +28,28 @@ class BaseDecoratorTest extends Specification {
0 * _
}
def "test onPeerConnection"() {
when:
decorator.onPeerConnection(span, connection)
then:
1 * span.setTag(Tags.PEER_HOSTNAME.key, connection.hostName)
1 * span.setTag(Tags.PEER_PORT.key, connection.port)
if (connection.address instanceof Inet4Address) {
1 * span.setTag(Tags.PEER_HOST_IPV4.key, connection.address.hostAddress)
}
if (connection.address instanceof Inet6Address) {
1 * span.setTag(Tags.PEER_HOST_IPV6.key, connection.address.hostAddress)
}
0 * _
where:
connection | _
new InetSocketAddress("localhost", 888) | _
new InetSocketAddress("ipv6.google.com", 999) | _
new InetSocketAddress("bad.address.local", 999) | _
}
def "test onError"() {
when:
decorator.onError(span, error)
@ -64,6 +86,12 @@ class BaseDecoratorTest extends Specification {
then:
thrown(AssertionError)
when:
decorator.onPeerConnection((Span) null, null)
then:
thrown(AssertionError)
when:
decorator.beforeFinish((Span) null)

View File

@ -193,6 +193,7 @@ class AwsClientTest extends AgentTestRunner {
"$Tags.HTTP_STATUS.key" 200
"$Tags.HTTP_URL.key" expectedUrl
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" server.address.port
"$Tags.HTTP_METHOD.key" "$method"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT

View File

@ -42,7 +42,11 @@ public class ChannelFutureListenerInstrumentation extends Instrumenter.Default {
public String[] helperClassNames() {
return new String[] {
packageName + ".AttributeKeys",
"datadog.trace.agent.decorator.BaseDecorator",
// server helpers
"datadog.trace.agent.decorator.ServerDecorator",
"datadog.trace.agent.decorator.HttpServerDecorator",
packageName + ".server.NettyHttpServerDecorator",
packageName + ".server.NettyRequestExtractAdapter",
packageName + ".server.HttpServerRequestTracingHandler",
packageName + ".server.HttpServerResponseTracingHandler",

View File

@ -56,12 +56,19 @@ public class NettyChannelPipelineInstrumentation extends Instrumenter.Default {
public String[] helperClassNames() {
return new String[] {
packageName + ".AttributeKeys",
"datadog.trace.agent.decorator.BaseDecorator",
// client helpers
"datadog.trace.agent.decorator.ClientDecorator",
"datadog.trace.agent.decorator.HttpClientDecorator",
packageName + ".client.NettyHttpClientDecorator",
packageName + ".client.NettyResponseInjectAdapter",
packageName + ".client.HttpClientRequestTracingHandler",
packageName + ".client.HttpClientResponseTracingHandler",
packageName + ".client.HttpClientTracingHandler",
// server helpers
"datadog.trace.agent.decorator.ServerDecorator",
"datadog.trace.agent.decorator.HttpServerDecorator",
packageName + ".server.NettyHttpServerDecorator",
packageName + ".server.NettyRequestExtractAdapter",
packageName + ".server.HttpServerRequestTracingHandler",
packageName + ".server.HttpServerResponseTracingHandler",

View File

@ -1,10 +1,7 @@
package datadog.trace.instrumentation.netty40.client;
import static io.netty.handler.codec.http.HttpHeaders.Names.HOST;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static datadog.trace.instrumentation.netty40.client.NettyHttpClientDecorator.DECORATE;
import datadog.trace.api.DDSpanTypes;
import datadog.trace.api.DDTags;
import datadog.trace.context.TraceScope;
import datadog.trace.instrumentation.netty40.AttributeKeys;
import io.netty.channel.ChannelHandlerContext;
@ -13,12 +10,8 @@ import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.HttpRequest;
import io.opentracing.Span;
import io.opentracing.propagation.Format;
import io.opentracing.tag.Tags;
import io.opentracing.util.GlobalTracer;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@ -39,19 +32,11 @@ public class HttpClientRequestTracingHandler extends ChannelOutboundHandlerAdapt
}
final HttpRequest request = (HttpRequest) msg;
final InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();
final Span span =
GlobalTracer.get()
.buildSpan("netty.client.request")
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
.withTag(Tags.PEER_HOSTNAME.getKey(), remoteAddress.getHostName())
.withTag(Tags.PEER_PORT.getKey(), remoteAddress.getPort())
.withTag(Tags.HTTP_METHOD.getKey(), request.getMethod().name())
.withTag(Tags.HTTP_URL.getKey(), formatUrl(request))
.withTag(Tags.COMPONENT.getKey(), "netty-client")
.withTag(DDTags.SPAN_TYPE, DDSpanTypes.HTTP_CLIENT)
.start();
final Span span = GlobalTracer.get().buildSpan("netty.client.request").start();
DECORATE.afterStart(span);
DECORATE.onRequest(span, request);
DECORATE.onPeerConnection(span, (InetSocketAddress) ctx.channel().remoteAddress());
// AWS calls are often signed, so we can't add headers without breaking the signature.
if (!request.headers().contains("amz-sdk-invocation-id")) {
@ -65,9 +50,9 @@ public class HttpClientRequestTracingHandler extends ChannelOutboundHandlerAdapt
try {
ctx.write(msg, prm);
} catch (final Throwable throwable) {
Tags.ERROR.set(span, Boolean.TRUE);
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
span.finish(); // Finish the span manually since finishSpanOnClose was false
DECORATE.onError(span, throwable);
DECORATE.beforeFinish(span);
span.finish();
throw throwable;
}
@ -75,18 +60,4 @@ public class HttpClientRequestTracingHandler extends ChannelOutboundHandlerAdapt
scope.close();
}
}
private String formatUrl(final HttpRequest request) {
try {
URI uri = new URI(request.getUri());
if ((uri.getHost() == null || uri.getHost().equals("")) && request.headers().contains(HOST)) {
uri = new URI("http://" + request.headers().get(HOST) + request.getUri());
}
return new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), null, null)
.toString();
} catch (final URISyntaxException e) {
log.debug("Cannot parse netty uri: {}", request.getUri());
return request.getUri();
}
}
}

View File

@ -1,6 +1,6 @@
package datadog.trace.instrumentation.netty40.client;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static datadog.trace.instrumentation.netty40.client.NettyHttpClientDecorator.DECORATE;
import datadog.trace.context.TraceScope;
import datadog.trace.instrumentation.netty40.AttributeKeys;
@ -11,7 +11,6 @@ import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.tag.Tags;
import io.opentracing.util.GlobalTracer;
import java.util.Collections;
public class HttpClientResponseTracingHandler extends ChannelInboundHandlerAdapter {
@ -33,8 +32,8 @@ public class HttpClientResponseTracingHandler extends ChannelInboundHandlerAdapt
ctx.fireChannelRead(msg);
} catch (final Throwable throwable) {
if (finishSpan) {
Tags.ERROR.set(span, Boolean.TRUE);
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
DECORATE.onError(span, throwable);
DECORATE.beforeFinish(span);
Tags.HTTP_STATUS.set(span, 500);
span.finish(); // Finish the span manually since finishSpanOnClose was false
throw throwable;
@ -42,7 +41,8 @@ public class HttpClientResponseTracingHandler extends ChannelInboundHandlerAdapt
}
if (finishSpan) {
Tags.HTTP_STATUS.set(span, ((HttpResponse) msg).getStatus().code());
DECORATE.onResponse(span, (HttpResponse) msg);
DECORATE.beforeFinish(span);
span.finish(); // Finish the span manually since finishSpanOnClose was false
}
}

View File

@ -0,0 +1,61 @@
package datadog.trace.instrumentation.netty40.client;
import static io.netty.handler.codec.http.HttpHeaders.Names.HOST;
import datadog.trace.agent.decorator.HttpClientDecorator;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import java.net.URI;
import java.net.URISyntaxException;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class NettyHttpClientDecorator extends HttpClientDecorator<HttpRequest, HttpResponse> {
public static final NettyHttpClientDecorator DECORATE = new NettyHttpClientDecorator();
@Override
protected String[] instrumentationNames() {
return new String[] {"netty", "netty-4.0"};
}
@Override
protected String component() {
return "netty-client";
}
@Override
protected String method(final HttpRequest httpRequest) {
return httpRequest.getMethod().name();
}
@Override
protected String url(final HttpRequest request) {
// FIXME: This code is duplicated across netty integrations.
try {
URI uri = new URI(request.getUri());
if ((uri.getHost() == null || uri.getHost().equals("")) && request.headers().contains(HOST)) {
uri = new URI("http://" + request.headers().get(HOST) + request.getUri());
}
return new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), null, null)
.toString();
} catch (final URISyntaxException e) {
log.debug("Cannot parse netty uri: {}", request.getUri());
return request.getUri();
}
}
@Override
protected String hostname(final HttpRequest httpRequest) {
return null;
}
@Override
protected Integer port(final HttpRequest httpRequest) {
return null;
}
@Override
protected Integer status(final HttpResponse httpResponse) {
return httpResponse.getStatus().code();
}
}

View File

@ -1,10 +1,7 @@
package datadog.trace.instrumentation.netty40.server;
import static io.netty.handler.codec.http.HttpHeaders.Names.HOST;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static datadog.trace.instrumentation.netty40.server.NettyHttpServerDecorator.DECORATE;
import datadog.trace.api.DDSpanTypes;
import datadog.trace.api.DDTags;
import datadog.trace.context.TraceScope;
import datadog.trace.instrumentation.netty40.AttributeKeys;
import io.netty.channel.ChannelHandlerContext;
@ -14,10 +11,8 @@ import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.propagation.Format;
import io.opentracing.tag.Tags;
import io.opentracing.util.GlobalTracer;
import java.net.InetSocketAddress;
import java.util.Collections;
public class HttpServerRequestTracingHandler extends ChannelInboundHandlerAdapter {
@ -34,35 +29,27 @@ public class HttpServerRequestTracingHandler extends ChannelInboundHandlerAdapte
GlobalTracer.get()
.extract(Format.Builtin.HTTP_HEADERS, new NettyRequestExtractAdapter(request));
String url = request.getUri();
if (request.headers().contains(HOST)) {
url = "http://" + request.headers().get(HOST) + url;
}
final Scope scope =
GlobalTracer.get()
.buildSpan("netty.request")
.asChildOf(extractedContext)
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)
.withTag(Tags.PEER_HOSTNAME.getKey(), remoteAddress.getHostName())
.withTag(Tags.PEER_PORT.getKey(), remoteAddress.getPort())
.withTag(Tags.HTTP_METHOD.getKey(), request.getMethod().name())
.withTag(Tags.HTTP_URL.getKey(), url)
.withTag(Tags.COMPONENT.getKey(), "netty")
.withTag(DDTags.SPAN_TYPE, DDSpanTypes.HTTP_SERVER)
.startActive(false);
final Span span = scope.span();
DECORATE.afterStart(span);
DECORATE.onRequest(span, request);
DECORATE.onPeerConnection(span, remoteAddress);
if (scope instanceof TraceScope) {
((TraceScope) scope).setAsyncPropagation(true);
}
final Span span = scope.span();
ctx.channel().attr(AttributeKeys.SERVER_ATTRIBUTE_KEY).set(span);
try {
ctx.fireChannelRead(msg);
} catch (final Throwable throwable) {
Tags.ERROR.set(span, Boolean.TRUE);
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
DECORATE.onError(span, throwable);
DECORATE.beforeFinish(span);
span.finish(); // Finish the span manually since finishSpanOnClose was false
throw throwable;
} finally {

View File

@ -1,6 +1,6 @@
package datadog.trace.instrumentation.netty40.server;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static datadog.trace.instrumentation.netty40.server.NettyHttpServerDecorator.DECORATE;
import datadog.trace.instrumentation.netty40.AttributeKeys;
import io.netty.channel.ChannelHandlerContext;
@ -9,7 +9,6 @@ import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.HttpResponse;
import io.opentracing.Span;
import io.opentracing.tag.Tags;
import java.util.Collections;
public class HttpServerResponseTracingHandler extends ChannelOutboundHandlerAdapter {
@ -26,14 +25,13 @@ public class HttpServerResponseTracingHandler extends ChannelOutboundHandlerAdap
try {
ctx.write(msg, prm);
} catch (final Throwable throwable) {
Tags.ERROR.set(span, Boolean.TRUE);
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
DECORATE.onError(span, throwable);
Tags.HTTP_STATUS.set(span, 500);
span.finish(); // Finish the span manually since finishSpanOnClose was false
throw throwable;
}
Tags.HTTP_STATUS.set(span, response.getStatus().code());
DECORATE.onResponse(span, response);
DECORATE.beforeFinish(span);
span.finish(); // Finish the span manually since finishSpanOnClose was false
}
}

View File

@ -0,0 +1,61 @@
package datadog.trace.instrumentation.netty40.server;
import static io.netty.handler.codec.http.HttpHeaders.Names.HOST;
import datadog.trace.agent.decorator.HttpServerDecorator;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import java.net.URI;
import java.net.URISyntaxException;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class NettyHttpServerDecorator extends HttpServerDecorator<HttpRequest, HttpResponse> {
public static final NettyHttpServerDecorator DECORATE = new NettyHttpServerDecorator();
@Override
protected String[] instrumentationNames() {
return new String[] {"netty", "netty-4.0"};
}
@Override
protected String component() {
return "netty";
}
@Override
protected String method(final HttpRequest httpRequest) {
return httpRequest.getMethod().name();
}
@Override
protected String url(final HttpRequest request) {
// FIXME: This code is duplicated across netty integrations.
try {
URI uri = new URI(request.getUri());
if ((uri.getHost() == null || uri.getHost().equals("")) && request.headers().contains(HOST)) {
uri = new URI("http://" + request.headers().get(HOST) + request.getUri());
}
return new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), null, null)
.toString();
} catch (final URISyntaxException e) {
log.debug("Cannot parse netty uri: {}", request.getUri());
return request.getUri();
}
}
@Override
protected String hostname(final HttpRequest httpRequest) {
return null;
}
@Override
protected Integer port(final HttpRequest httpRequest) {
return null;
}
@Override
protected Integer status(final HttpResponse httpResponse) {
return httpResponse.getStatus().code();
}
}

View File

@ -57,7 +57,8 @@ class Netty40ClientTest extends AgentTestRunner {
"$Tags.HTTP_STATUS.key" 200
"$Tags.HTTP_URL.key" "$server.address/"
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_PORT.key" Integer
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" server.address.port
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
defaultTags()
}

View File

@ -13,7 +13,6 @@ import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.nio.NioServerSocketChannel
import io.netty.handler.codec.http.DefaultFullHttpResponse
import io.netty.handler.codec.http.FullHttpResponse
import io.netty.handler.codec.http.HttpHeaders
import io.netty.handler.codec.http.HttpRequestDecoder
import io.netty.handler.codec.http.HttpResponseEncoder
import io.netty.handler.codec.http.HttpResponseStatus
@ -26,9 +25,14 @@ import io.netty.util.CharsetUtil
import io.opentracing.tag.Tags
import okhttp3.OkHttpClient
import okhttp3.Request
import spock.lang.Shared
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE
class Netty40ServerTest extends AgentTestRunner {
@Shared
OkHttpClient client = OkHttpUtils.client()
def "test server request/response"() {
@ -66,6 +70,7 @@ class Netty40ServerTest extends AgentTestRunner {
"$Tags.HTTP_STATUS.key" 200
"$Tags.HTTP_URL.key" "http://localhost:$port/"
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" Integer
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
defaultTags(true)
@ -111,6 +116,7 @@ class Netty40ServerTest extends AgentTestRunner {
"$Tags.HTTP_STATUS.key" responseCode.code()
"$Tags.HTTP_URL.key" "http://localhost:$port/"
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" Integer
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
if (error) {
@ -145,8 +151,8 @@ class Netty40ServerTest extends AgentTestRunner {
if (msg instanceof LastHttpContent) {
ByteBuf content = Unpooled.copiedBuffer("Hello World", CharsetUtil.UTF_8)
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, responseCode, content)
response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/plain")
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, content.readableBytes())
response.headers().set(CONTENT_TYPE, "text/plain")
response.headers().set(CONTENT_LENGTH, content.readableBytes())
ctx.write(response)
}
},

View File

@ -42,7 +42,11 @@ public class ChannelFutureListenerInstrumentation extends Instrumenter.Default {
public String[] helperClassNames() {
return new String[] {
packageName + ".AttributeKeys",
"datadog.trace.agent.decorator.BaseDecorator",
// server helpers
"datadog.trace.agent.decorator.ServerDecorator",
"datadog.trace.agent.decorator.HttpServerDecorator",
packageName + ".server.NettyHttpServerDecorator",
packageName + ".server.NettyRequestExtractAdapter",
packageName + ".server.HttpServerRequestTracingHandler",
packageName + ".server.HttpServerResponseTracingHandler",

View File

@ -56,12 +56,19 @@ public class NettyChannelPipelineInstrumentation extends Instrumenter.Default {
public String[] helperClassNames() {
return new String[] {
packageName + ".AttributeKeys",
"datadog.trace.agent.decorator.BaseDecorator",
// client helpers
"datadog.trace.agent.decorator.ClientDecorator",
"datadog.trace.agent.decorator.HttpClientDecorator",
packageName + ".client.NettyHttpClientDecorator",
packageName + ".client.NettyResponseInjectAdapter",
packageName + ".client.HttpClientRequestTracingHandler",
packageName + ".client.HttpClientResponseTracingHandler",
packageName + ".client.HttpClientTracingHandler",
// server helpers
"datadog.trace.agent.decorator.ServerDecorator",
"datadog.trace.agent.decorator.HttpServerDecorator",
packageName + ".server.NettyHttpServerDecorator",
packageName + ".server.NettyRequestExtractAdapter",
packageName + ".server.HttpServerRequestTracingHandler",
packageName + ".server.HttpServerResponseTracingHandler",

View File

@ -1,10 +1,7 @@
package datadog.trace.instrumentation.netty41.client;
import static io.netty.handler.codec.http.HttpHeaderNames.HOST;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static datadog.trace.instrumentation.netty41.client.NettyHttpClientDecorator.DECORATE;
import datadog.trace.api.DDSpanTypes;
import datadog.trace.api.DDTags;
import datadog.trace.context.TraceScope;
import datadog.trace.instrumentation.netty41.AttributeKeys;
import io.netty.channel.ChannelHandlerContext;
@ -13,12 +10,8 @@ import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.HttpRequest;
import io.opentracing.Span;
import io.opentracing.propagation.Format;
import io.opentracing.tag.Tags;
import io.opentracing.util.GlobalTracer;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@ -39,19 +32,11 @@ public class HttpClientRequestTracingHandler extends ChannelOutboundHandlerAdapt
}
final HttpRequest request = (HttpRequest) msg;
final InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();
final Span span =
GlobalTracer.get()
.buildSpan("netty.client.request")
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
.withTag(Tags.PEER_HOSTNAME.getKey(), remoteAddress.getHostName())
.withTag(Tags.PEER_PORT.getKey(), remoteAddress.getPort())
.withTag(Tags.HTTP_METHOD.getKey(), request.method().name())
.withTag(Tags.HTTP_URL.getKey(), formatUrl(request))
.withTag(Tags.COMPONENT.getKey(), "netty-client")
.withTag(DDTags.SPAN_TYPE, DDSpanTypes.HTTP_CLIENT)
.start();
final Span span = GlobalTracer.get().buildSpan("netty.client.request").start();
DECORATE.afterStart(span);
DECORATE.onRequest(span, request);
DECORATE.onPeerConnection(span, (InetSocketAddress) ctx.channel().remoteAddress());
// AWS calls are often signed, so we can't add headers without breaking the signature.
if (!request.headers().contains("amz-sdk-invocation-id")) {
@ -65,9 +50,9 @@ public class HttpClientRequestTracingHandler extends ChannelOutboundHandlerAdapt
try {
ctx.write(msg, prm);
} catch (final Throwable throwable) {
Tags.ERROR.set(span, Boolean.TRUE);
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
span.finish(); // Finish the span manually since finishSpanOnClose was false
DECORATE.onError(span, throwable);
DECORATE.beforeFinish(span);
span.finish();
throw throwable;
}
@ -75,18 +60,4 @@ public class HttpClientRequestTracingHandler extends ChannelOutboundHandlerAdapt
scope.close();
}
}
private String formatUrl(final HttpRequest request) {
try {
URI uri = new URI(request.uri());
if ((uri.getHost() == null || uri.getHost().equals("")) && request.headers().contains(HOST)) {
uri = new URI("http://" + request.headers().get(HOST) + request.uri());
}
return new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), null, null)
.toString();
} catch (final URISyntaxException e) {
log.debug("Cannot parse netty uri: {}", request.uri());
return request.uri();
}
}
}

View File

@ -1,6 +1,6 @@
package datadog.trace.instrumentation.netty41.client;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static datadog.trace.instrumentation.netty41.client.NettyHttpClientDecorator.DECORATE;
import datadog.trace.context.TraceScope;
import datadog.trace.instrumentation.netty41.AttributeKeys;
@ -11,7 +11,6 @@ import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.tag.Tags;
import io.opentracing.util.GlobalTracer;
import java.util.Collections;
public class HttpClientResponseTracingHandler extends ChannelInboundHandlerAdapter {
@ -33,8 +32,8 @@ public class HttpClientResponseTracingHandler extends ChannelInboundHandlerAdapt
ctx.fireChannelRead(msg);
} catch (final Throwable throwable) {
if (finishSpan) {
Tags.ERROR.set(span, Boolean.TRUE);
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
DECORATE.onError(span, throwable);
DECORATE.beforeFinish(span);
Tags.HTTP_STATUS.set(span, 500);
span.finish(); // Finish the span manually since finishSpanOnClose was false
throw throwable;
@ -42,7 +41,8 @@ public class HttpClientResponseTracingHandler extends ChannelInboundHandlerAdapt
}
if (finishSpan) {
Tags.HTTP_STATUS.set(span, ((HttpResponse) msg).status().code());
DECORATE.onResponse(span, (HttpResponse) msg);
DECORATE.beforeFinish(span);
span.finish(); // Finish the span manually since finishSpanOnClose was false
}
}

View File

@ -0,0 +1,61 @@
package datadog.trace.instrumentation.netty41.client;
import static io.netty.handler.codec.http.HttpHeaders.Names.HOST;
import datadog.trace.agent.decorator.HttpClientDecorator;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import java.net.URI;
import java.net.URISyntaxException;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class NettyHttpClientDecorator extends HttpClientDecorator<HttpRequest, HttpResponse> {
public static final NettyHttpClientDecorator DECORATE = new NettyHttpClientDecorator();
@Override
protected String[] instrumentationNames() {
return new String[] {"netty", "netty-4.0"};
}
@Override
protected String component() {
return "netty-client";
}
@Override
protected String method(final HttpRequest httpRequest) {
return httpRequest.method().name();
}
@Override
protected String url(final HttpRequest request) {
// FIXME: This code is duplicated across netty integrations.
try {
URI uri = new URI(request.uri());
if ((uri.getHost() == null || uri.getHost().equals("")) && request.headers().contains(HOST)) {
uri = new URI("http://" + request.headers().get(HOST) + request.uri());
}
return new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), null, null)
.toString();
} catch (final URISyntaxException e) {
log.debug("Cannot parse netty uri: {}", request.uri());
return request.uri();
}
}
@Override
protected String hostname(final HttpRequest httpRequest) {
return null;
}
@Override
protected Integer port(final HttpRequest httpRequest) {
return null;
}
@Override
protected Integer status(final HttpResponse httpResponse) {
return httpResponse.status().code();
}
}

View File

@ -1,10 +1,7 @@
package datadog.trace.instrumentation.netty41.server;
import static io.netty.handler.codec.http.HttpHeaderNames.HOST;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static datadog.trace.instrumentation.netty41.server.NettyHttpServerDecorator.DECORATE;
import datadog.trace.api.DDSpanTypes;
import datadog.trace.api.DDTags;
import datadog.trace.context.TraceScope;
import datadog.trace.instrumentation.netty41.AttributeKeys;
import io.netty.channel.ChannelHandlerContext;
@ -14,10 +11,8 @@ import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.propagation.Format;
import io.opentracing.tag.Tags;
import io.opentracing.util.GlobalTracer;
import java.net.InetSocketAddress;
import java.util.Collections;
public class HttpServerRequestTracingHandler extends ChannelInboundHandlerAdapter {
@ -34,35 +29,27 @@ public class HttpServerRequestTracingHandler extends ChannelInboundHandlerAdapte
GlobalTracer.get()
.extract(Format.Builtin.HTTP_HEADERS, new NettyRequestExtractAdapter(request));
String url = request.uri();
if (request.headers().contains(HOST)) {
url = "http://" + request.headers().get(HOST) + url;
}
final Scope scope =
GlobalTracer.get()
.buildSpan("netty.request")
.asChildOf(extractedContext)
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)
.withTag(Tags.PEER_HOSTNAME.getKey(), remoteAddress.getHostName())
.withTag(Tags.PEER_PORT.getKey(), remoteAddress.getPort())
.withTag(Tags.HTTP_METHOD.getKey(), request.method().name())
.withTag(Tags.HTTP_URL.getKey(), url)
.withTag(Tags.COMPONENT.getKey(), "netty")
.withTag(DDTags.SPAN_TYPE, DDSpanTypes.HTTP_SERVER)
.startActive(false);
final Span span = scope.span();
DECORATE.afterStart(span);
DECORATE.onRequest(span, request);
DECORATE.onPeerConnection(span, remoteAddress);
if (scope instanceof TraceScope) {
((TraceScope) scope).setAsyncPropagation(true);
}
final Span span = scope.span();
ctx.channel().attr(AttributeKeys.SERVER_ATTRIBUTE_KEY).set(span);
try {
ctx.fireChannelRead(msg);
} catch (final Throwable throwable) {
Tags.ERROR.set(span, Boolean.TRUE);
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
DECORATE.onError(span, throwable);
DECORATE.beforeFinish(span);
span.finish(); // Finish the span manually since finishSpanOnClose was false
throw throwable;
} finally {

View File

@ -1,6 +1,6 @@
package datadog.trace.instrumentation.netty41.server;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static datadog.trace.instrumentation.netty41.server.NettyHttpServerDecorator.DECORATE;
import datadog.trace.instrumentation.netty41.AttributeKeys;
import io.netty.channel.ChannelHandlerContext;
@ -9,7 +9,6 @@ import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.HttpResponse;
import io.opentracing.Span;
import io.opentracing.tag.Tags;
import java.util.Collections;
public class HttpServerResponseTracingHandler extends ChannelOutboundHandlerAdapter {
@ -26,14 +25,13 @@ public class HttpServerResponseTracingHandler extends ChannelOutboundHandlerAdap
try {
ctx.write(msg, prm);
} catch (final Throwable throwable) {
Tags.ERROR.set(span, Boolean.TRUE);
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
DECORATE.onError(span, throwable);
Tags.HTTP_STATUS.set(span, 500);
span.finish(); // Finish the span manually since finishSpanOnClose was false
throw throwable;
}
Tags.HTTP_STATUS.set(span, response.status().code());
DECORATE.onResponse(span, response);
DECORATE.beforeFinish(span);
span.finish(); // Finish the span manually since finishSpanOnClose was false
}
}

View File

@ -0,0 +1,61 @@
package datadog.trace.instrumentation.netty41.server;
import static io.netty.handler.codec.http.HttpHeaders.Names.HOST;
import datadog.trace.agent.decorator.HttpServerDecorator;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import java.net.URI;
import java.net.URISyntaxException;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class NettyHttpServerDecorator extends HttpServerDecorator<HttpRequest, HttpResponse> {
public static final NettyHttpServerDecorator DECORATE = new NettyHttpServerDecorator();
@Override
protected String[] instrumentationNames() {
return new String[] {"netty", "netty-4.0"};
}
@Override
protected String component() {
return "netty";
}
@Override
protected String method(final HttpRequest httpRequest) {
return httpRequest.method().name();
}
@Override
protected String url(final HttpRequest request) {
// FIXME: This code is duplicated across netty integrations.
try {
URI uri = new URI(request.uri());
if ((uri.getHost() == null || uri.getHost().equals("")) && request.headers().contains(HOST)) {
uri = new URI("http://" + request.headers().get(HOST) + request.uri());
}
return new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), null, null)
.toString();
} catch (final URISyntaxException e) {
log.debug("Cannot parse netty uri: {}", request.uri());
return request.uri();
}
}
@Override
protected String hostname(final HttpRequest httpRequest) {
return null;
}
@Override
protected Integer port(final HttpRequest httpRequest) {
return null;
}
@Override
protected Integer status(final HttpResponse httpResponse) {
return httpResponse.status().code();
}
}

View File

@ -58,7 +58,8 @@ class Netty41ClientTest extends AgentTestRunner {
"$Tags.HTTP_STATUS.key" 200
"$Tags.HTTP_URL.key" "$server.address/"
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_PORT.key" Integer
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" server.address.port
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
defaultTags()
}

View File

@ -13,7 +13,6 @@ import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.nio.NioServerSocketChannel
import io.netty.handler.codec.http.DefaultFullHttpResponse
import io.netty.handler.codec.http.FullHttpResponse
import io.netty.handler.codec.http.HttpHeaderNames
import io.netty.handler.codec.http.HttpRequestDecoder
import io.netty.handler.codec.http.HttpResponseEncoder
import io.netty.handler.codec.http.HttpResponseStatus
@ -28,6 +27,9 @@ import okhttp3.OkHttpClient
import okhttp3.Request
import spock.lang.Shared
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE
class Netty41ServerTest extends AgentTestRunner {
@Shared
@ -68,6 +70,7 @@ class Netty41ServerTest extends AgentTestRunner {
"$Tags.HTTP_STATUS.key" 200
"$Tags.HTTP_URL.key" "http://localhost:$port/"
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" Integer
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
defaultTags(true)
@ -113,6 +116,7 @@ class Netty41ServerTest extends AgentTestRunner {
"$Tags.HTTP_STATUS.key" responseCode.code()
"$Tags.HTTP_URL.key" "http://localhost:$port/"
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" Integer
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
if (error) {
@ -147,8 +151,8 @@ class Netty41ServerTest extends AgentTestRunner {
if (msg instanceof LastHttpContent) {
ByteBuf content = Unpooled.copiedBuffer("Hello World", CharsetUtil.UTF_8)
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, responseCode, content)
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain")
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes())
response.headers().set(CONTENT_TYPE, "text/plain")
response.headers().set(CONTENT_LENGTH, content.readableBytes())
ctx.write(response)
}
},

View File

@ -84,6 +84,7 @@ class SpringWebfluxTest extends AgentTestRunner {
"$Tags.COMPONENT.key" "netty"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" Integer
"$Tags.HTTP_METHOD.key" "GET"
"$Tags.HTTP_STATUS.key" 200
@ -156,6 +157,7 @@ class SpringWebfluxTest extends AgentTestRunner {
"$Tags.COMPONENT.key" "netty"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" Integer
"$Tags.HTTP_METHOD.key" "GET"
"$Tags.HTTP_STATUS.key" 200
@ -216,6 +218,7 @@ class SpringWebfluxTest extends AgentTestRunner {
"$Tags.COMPONENT.key" "netty"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" Integer
"$Tags.HTTP_METHOD.key" "GET"
"$Tags.HTTP_STATUS.key" 404
@ -278,6 +281,7 @@ class SpringWebfluxTest extends AgentTestRunner {
"$Tags.COMPONENT.key" "netty"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" Integer
"$Tags.HTTP_METHOD.key" "POST"
"$Tags.HTTP_STATUS.key" 202
@ -320,6 +324,7 @@ class SpringWebfluxTest extends AgentTestRunner {
"$Tags.COMPONENT.key" "netty"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" Integer
"$Tags.HTTP_METHOD.key" "GET"
"$Tags.HTTP_STATUS.key" 500
@ -392,6 +397,7 @@ class SpringWebfluxTest extends AgentTestRunner {
"$Tags.COMPONENT.key" "netty"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" Integer
"$Tags.HTTP_METHOD.key" "GET"
"$Tags.HTTP_STATUS.key" 307
@ -439,6 +445,7 @@ class SpringWebfluxTest extends AgentTestRunner {
"$Tags.COMPONENT.key" "netty"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" Integer
"$Tags.HTTP_METHOD.key" "GET"
"$Tags.HTTP_STATUS.key" 200
@ -500,6 +507,7 @@ class SpringWebfluxTest extends AgentTestRunner {
"$Tags.COMPONENT.key" "netty"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" Integer
"$Tags.HTTP_METHOD.key" "GET"
"$Tags.HTTP_STATUS.key" 200

View File

@ -80,6 +80,7 @@ class VertxHttpClientTest extends AgentTestRunner {
"$Tags.HTTP_STATUS.key" expectedStatus
"$Tags.HTTP_URL.key" "${server.address}/$route"
"$Tags.PEER_HOSTNAME.key" server.address.host
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" server.address.port
"$Tags.HTTP_METHOD.key" "GET"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT

View File

@ -59,6 +59,7 @@ class VertxServerTest extends AgentTestRunner {
"$Tags.HTTP_STATUS.key" 200
"$Tags.HTTP_URL.key" "http://localhost:$port/test"
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" Integer
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
defaultTags(true)
@ -95,6 +96,7 @@ class VertxServerTest extends AgentTestRunner {
"$Tags.HTTP_STATUS.key" responseCode.code()
"$Tags.HTTP_URL.key" "http://localhost:$port/$path"
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1"
"$Tags.PEER_PORT.key" Integer
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
if (error) {