Trace DNS resolution in Netty 4.1 (#4587)

* Trace DNS resolution in Netty 4.1

* Fix reactor-netty 0.9 tests

* Update instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/client/InstrumentedAddressResolverGroup.java

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
This commit is contained in:
Mateusz Rzeszutek 2021-11-05 19:37:54 +01:00 committed by GitHub
parent f3ec9a215b
commit 958f1c039c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 352 additions and 144 deletions

View File

@ -7,7 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.netty.v3_8;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.instrumentation.netty.v3_8.client.NettyClientSingletons.connectInstrumenter;
import static io.opentelemetry.javaagent.instrumentation.netty.v3_8.client.NettyClientSingletons.connectionInstrumenter;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
@ -19,7 +19,7 @@ import io.opentelemetry.instrumentation.api.field.VirtualField;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest;
import io.opentelemetry.javaagent.instrumentation.netty.v3_8.client.ConnectionListener;
import java.net.SocketAddress;
import net.bytebuddy.asm.Advice;
@ -58,7 +58,7 @@ public class NettyChannelInstrumentation implements TypeInstrumentation {
@Advice.This Channel channel,
@Advice.Argument(0) SocketAddress remoteAddress,
@Advice.Local("otelParentContext") Context parentContext,
@Advice.Local("otelRequest") NettyConnectRequest request) {
@Advice.Local("otelRequest") NettyConnectionRequest request) {
parentContext = Java8BytecodeBridge.currentContext();
Span span = Java8BytecodeBridge.spanFromContext(parentContext);
@ -73,7 +73,7 @@ public class NettyChannelInstrumentation implements TypeInstrumentation {
}
virtualField.set(channel, new NettyConnectionContext(parentContext));
request = NettyConnectRequest.create(remoteAddress);
request = NettyConnectionRequest.connect(remoteAddress);
}
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
@ -81,16 +81,16 @@ public class NettyChannelInstrumentation implements TypeInstrumentation {
@Advice.Return ChannelFuture channelFuture,
@Advice.Thrown Throwable error,
@Advice.Local("otelParentContext") Context parentContext,
@Advice.Local("otelRequest") NettyConnectRequest request) {
@Advice.Local("otelRequest") NettyConnectionRequest request) {
if (request == null) {
return;
}
if (error != null) {
if (connectInstrumenter().shouldStart(parentContext, request)) {
Context context = connectInstrumenter().start(parentContext, request);
connectInstrumenter().end(context, request, null, error);
if (connectionInstrumenter().shouldStart(parentContext, request)) {
Context context = connectionInstrumenter().start(parentContext, request);
connectionInstrumenter().end(context, request, null, error);
}
} else {
channelFuture.addListener(new ConnectionListener(parentContext, request));

View File

@ -5,19 +5,19 @@
package io.opentelemetry.javaagent.instrumentation.netty.v3_8.client;
import static io.opentelemetry.javaagent.instrumentation.netty.v3_8.client.NettyClientSingletons.connectInstrumenter;
import static io.opentelemetry.javaagent.instrumentation.netty.v3_8.client.NettyClientSingletons.connectionInstrumenter;
import io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
public final class ConnectionListener implements ChannelFutureListener {
private final Context parentContext;
private final NettyConnectRequest request;
private final NettyConnectionRequest request;
public ConnectionListener(Context parentContext, NettyConnectRequest request) {
public ConnectionListener(Context parentContext, NettyConnectionRequest request) {
this.parentContext = parentContext;
this.request = request;
}
@ -25,9 +25,9 @@ public final class ConnectionListener implements ChannelFutureListener {
@Override
public void operationComplete(ChannelFuture future) {
Throwable cause = future.getCause();
if (cause != null && connectInstrumenter().shouldStart(parentContext, request)) {
Context context = connectInstrumenter().start(parentContext, request);
connectInstrumenter().end(context, request, future.getChannel(), cause);
if (cause != null && connectionInstrumenter().shouldStart(parentContext, request)) {
Context context = connectionInstrumenter().start(parentContext, request);
connectionInstrumenter().end(context, request, future.getChannel(), cause);
}
}
}

View File

@ -12,7 +12,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyErrorHolder;
import io.opentelemetry.javaagent.instrumentation.netty.v3_8.HttpRequestAndChannel;
import org.jboss.netty.channel.Channel;
@ -23,7 +23,7 @@ public final class NettyClientSingletons {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.netty-3.8";
private static final Instrumenter<HttpRequestAndChannel, HttpResponse> INSTRUMENTER;
private static final Instrumenter<NettyConnectRequest, Channel> CONNECT_INSTRUMENTER;
private static final Instrumenter<NettyConnectionRequest, Channel> CONNECTION_INSTRUMENTER;
static {
NettyHttpClientAttributesExtractor httpClientAttributesExtractor =
@ -48,9 +48,9 @@ public final class NettyClientSingletons {
NettyConnectNetAttributesExtractor nettyConnectAttributesExtractor =
new NettyConnectNetAttributesExtractor();
CONNECT_INSTRUMENTER =
Instrumenter.<NettyConnectRequest, Channel>builder(
GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, rq -> "CONNECT")
CONNECTION_INSTRUMENTER =
Instrumenter.<NettyConnectionRequest, Channel>builder(
GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, NettyConnectionRequest::spanName)
.addAttributesExtractor(nettyConnectAttributesExtractor)
.addAttributesExtractor(
PeerServiceAttributesExtractor.create(nettyConnectAttributesExtractor))
@ -64,8 +64,8 @@ public final class NettyClientSingletons {
return INSTRUMENTER;
}
public static Instrumenter<NettyConnectRequest, Channel> connectInstrumenter() {
return CONNECT_INSTRUMENTER;
public static Instrumenter<NettyConnectionRequest, Channel> connectionInstrumenter() {
return CONNECTION_INSTRUMENTER;
}
private NettyClientSingletons() {}

View File

@ -9,7 +9,7 @@ import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTr
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_UDP;
import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetClientAttributesExtractor;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import javax.annotation.Nullable;
@ -17,11 +17,11 @@ import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.socket.DatagramChannel;
final class NettyConnectNetAttributesExtractor
extends InetSocketAddressNetClientAttributesExtractor<NettyConnectRequest, Channel> {
extends InetSocketAddressNetClientAttributesExtractor<NettyConnectionRequest, Channel> {
@Nullable
@Override
public InetSocketAddress getAddress(NettyConnectRequest request, @Nullable Channel channel) {
public InetSocketAddress getAddress(NettyConnectionRequest request, @Nullable Channel channel) {
SocketAddress remoteAddress = null;
if (channel != null) {
remoteAddress = channel.getRemoteAddress();
@ -37,7 +37,7 @@ final class NettyConnectNetAttributesExtractor
}
@Override
public String transport(NettyConnectRequest request, @Nullable Channel channel) {
public String transport(NettyConnectionRequest request, @Nullable Channel channel) {
return channel instanceof DatagramChannel ? IP_UDP : IP_TCP;
}
}

View File

@ -10,15 +10,15 @@ import io.netty.channel.ChannelFuture;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest;
public class ConnectionCompleteListener implements GenericFutureListener<Future<Void>> {
private final NettyConnectInstrumenter instrumenter;
private final NettyConnectionInstrumenter instrumenter;
private final Context context;
private final NettyConnectRequest request;
private final NettyConnectionRequest request;
public ConnectionCompleteListener(
NettyConnectInstrumenter instrumenter, Context context, NettyConnectRequest request) {
NettyConnectionInstrumenter instrumenter, Context context, NettyConnectionRequest request) {
this.instrumenter = instrumenter;
this.context = context;
this.request = request;

View File

@ -15,7 +15,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
import io.opentelemetry.javaagent.instrumentation.netty.common.HttpRequestAndChannel;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest;
public final class NettyClientInstrumenterFactory {
@ -46,12 +46,12 @@ public final class NettyClientInstrumenterFactory {
.newClientInstrumenter(new HttpRequestHeadersSetter());
}
public NettyConnectInstrumenter createConnectInstrumenter() {
public NettyConnectionInstrumenter createConnectionInstrumenter() {
NettyConnectNetAttributesExtractor netAttributesExtractor =
new NettyConnectNetAttributesExtractor();
Instrumenter<NettyConnectRequest, Channel> instrumenter =
Instrumenter.<NettyConnectRequest, Channel>builder(
GlobalOpenTelemetry.get(), instrumentationName, rq -> "CONNECT")
Instrumenter<NettyConnectionRequest, Channel> instrumenter =
Instrumenter.<NettyConnectionRequest, Channel>builder(
GlobalOpenTelemetry.get(), instrumentationName, NettyConnectionRequest::spanName)
.addAttributesExtractor(netAttributesExtractor)
.addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesExtractor))
.setTimeExtractors(
@ -63,7 +63,7 @@ public final class NettyClientInstrumenterFactory {
: SpanKindExtractor.alwaysClient());
return alwaysCreateConnectSpan
? new NettyConnectInstrumenterImpl(instrumenter)
: new NettyErrorOnlyConnectInstrumenter(instrumenter);
? new NettyConnectionInstrumenterImpl(instrumenter)
: new NettyErrorOnlyConnectionInstrumenter(instrumenter);
}
}

View File

@ -11,17 +11,17 @@ import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTr
import io.netty.channel.Channel;
import io.netty.channel.socket.DatagramChannel;
import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetClientAttributesExtractor;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import javax.annotation.Nullable;
final class NettyConnectNetAttributesExtractor
extends InetSocketAddressNetClientAttributesExtractor<NettyConnectRequest, Channel> {
extends InetSocketAddressNetClientAttributesExtractor<NettyConnectionRequest, Channel> {
@Nullable
@Override
public InetSocketAddress getAddress(NettyConnectRequest request, @Nullable Channel channel) {
public InetSocketAddress getAddress(NettyConnectionRequest request, @Nullable Channel channel) {
SocketAddress remoteAddress = null;
if (channel != null) {
remoteAddress = channel.remoteAddress();
@ -37,7 +37,7 @@ final class NettyConnectNetAttributesExtractor
}
@Override
public String transport(NettyConnectRequest request, @Nullable Channel channel) {
public String transport(NettyConnectionRequest request, @Nullable Channel channel) {
return channel instanceof DatagramChannel ? IP_UDP : IP_TCP;
}
}

View File

@ -7,15 +7,15 @@ package io.opentelemetry.javaagent.instrumentation.netty.common.client;
import io.netty.channel.Channel;
import io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest;
import javax.annotation.Nullable;
public interface NettyConnectInstrumenter {
public interface NettyConnectionInstrumenter {
boolean shouldStart(Context parentContext, NettyConnectRequest request);
boolean shouldStart(Context parentContext, NettyConnectionRequest request);
Context start(Context parentContext, NettyConnectRequest request);
Context start(Context parentContext, NettyConnectionRequest request);
void end(
Context context, NettyConnectRequest request, Channel channel, @Nullable Throwable error);
Context context, NettyConnectionRequest request, Channel channel, @Nullable Throwable error);
}

View File

@ -8,30 +8,30 @@ package io.opentelemetry.javaagent.instrumentation.netty.common.client;
import io.netty.channel.Channel;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest;
import javax.annotation.Nullable;
final class NettyConnectInstrumenterImpl implements NettyConnectInstrumenter {
final class NettyConnectionInstrumenterImpl implements NettyConnectionInstrumenter {
private final Instrumenter<NettyConnectRequest, Channel> instrumenter;
private final Instrumenter<NettyConnectionRequest, Channel> instrumenter;
NettyConnectInstrumenterImpl(Instrumenter<NettyConnectRequest, Channel> instrumenter) {
NettyConnectionInstrumenterImpl(Instrumenter<NettyConnectionRequest, Channel> instrumenter) {
this.instrumenter = instrumenter;
}
@Override
public boolean shouldStart(Context parentContext, NettyConnectRequest request) {
public boolean shouldStart(Context parentContext, NettyConnectionRequest request) {
return instrumenter.shouldStart(parentContext, request);
}
@Override
public Context start(Context parentContext, NettyConnectRequest request) {
public Context start(Context parentContext, NettyConnectionRequest request) {
return instrumenter.start(parentContext, request);
}
@Override
public void end(
Context context, NettyConnectRequest request, Channel channel, @Nullable Throwable error) {
Context context, NettyConnectionRequest request, Channel channel, @Nullable Throwable error) {
instrumenter.end(context, request, channel, error);
}
}

View File

@ -8,31 +8,31 @@ package io.opentelemetry.javaagent.instrumentation.netty.common.client;
import io.netty.channel.Channel;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest;
import javax.annotation.Nullable;
final class NettyErrorOnlyConnectInstrumenter implements NettyConnectInstrumenter {
final class NettyErrorOnlyConnectionInstrumenter implements NettyConnectionInstrumenter {
private final Instrumenter<NettyConnectRequest, Channel> instrumenter;
private final Instrumenter<NettyConnectionRequest, Channel> instrumenter;
NettyErrorOnlyConnectInstrumenter(Instrumenter<NettyConnectRequest, Channel> instrumenter) {
NettyErrorOnlyConnectionInstrumenter(Instrumenter<NettyConnectionRequest, Channel> instrumenter) {
this.instrumenter = instrumenter;
}
@Override
public boolean shouldStart(Context parentContext, NettyConnectRequest request) {
public boolean shouldStart(Context parentContext, NettyConnectionRequest request) {
// the "real" check is done on end() anyway
return true;
}
@Override
public Context start(Context parentContext, NettyConnectRequest request) {
public Context start(Context parentContext, NettyConnectionRequest request) {
return parentContext;
}
@Override
public void end(
Context context, NettyConnectRequest request, Channel channel, @Nullable Throwable error) {
Context context, NettyConnectionRequest request, Channel channel, @Nullable Throwable error) {
if (error != null && instrumenter.shouldStart(context, request)) {
Context connectContext = instrumenter.start(context, request);
instrumenter.end(connectContext, request, channel, error);

View File

@ -5,7 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.netty.v4_0;
import static io.opentelemetry.javaagent.instrumentation.netty.v4_0.client.NettyClientSingletons.connectInstrumenter;
import static io.opentelemetry.javaagent.instrumentation.netty.v4_0.client.NettyClientSingletons.connectionInstrumenter;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
@ -15,7 +15,7 @@ import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.client.ConnectionCompleteListener;
import java.net.SocketAddress;
import net.bytebuddy.asm.Advice;
@ -40,17 +40,17 @@ public class BootstrapInstrumentation implements TypeInstrumentation {
public static void startConnect(
@Advice.Argument(0) SocketAddress remoteAddress,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelRequest") NettyConnectRequest request,
@Advice.Local("otelRequest") NettyConnectionRequest request,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = Java8BytecodeBridge.currentContext();
request = NettyConnectRequest.create(remoteAddress);
request = NettyConnectionRequest.connect(remoteAddress);
if (!connectInstrumenter().shouldStart(parentContext, request)) {
if (!connectionInstrumenter().shouldStart(parentContext, request)) {
return;
}
context = connectInstrumenter().start(parentContext, request);
context = connectionInstrumenter().start(parentContext, request);
scope = context.makeCurrent();
}
@ -59,7 +59,7 @@ public class BootstrapInstrumentation implements TypeInstrumentation {
@Advice.Thrown Throwable throwable,
@Advice.Return ChannelFuture channelFuture,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelRequest") NettyConnectRequest request,
@Advice.Local("otelRequest") NettyConnectionRequest request,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
@ -68,10 +68,10 @@ public class BootstrapInstrumentation implements TypeInstrumentation {
scope.close();
if (throwable != null) {
connectInstrumenter().end(context, request, null, throwable);
connectionInstrumenter().end(context, request, null, throwable);
} else {
channelFuture.addListener(
new ConnectionCompleteListener(connectInstrumenter(), context, request));
new ConnectionCompleteListener(connectionInstrumenter(), context, request));
}
}
}

View File

@ -10,7 +10,7 @@ import io.opentelemetry.instrumentation.api.config.Config;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.javaagent.instrumentation.netty.common.HttpRequestAndChannel;
import io.opentelemetry.javaagent.instrumentation.netty.common.client.NettyClientInstrumenterFactory;
import io.opentelemetry.javaagent.instrumentation.netty.common.client.NettyConnectInstrumenter;
import io.opentelemetry.javaagent.instrumentation.netty.common.client.NettyConnectionInstrumenter;
public final class NettyClientSingletons {
@ -18,21 +18,21 @@ public final class NettyClientSingletons {
Config.get().getBoolean("otel.instrumentation.netty.always-create-connect-span", false);
private static final Instrumenter<HttpRequestAndChannel, HttpResponse> INSTRUMENTER;
private static final NettyConnectInstrumenter CONNECT_INSTRUMENTER;
private static final NettyConnectionInstrumenter CONNECTION_INSTRUMENTER;
static {
NettyClientInstrumenterFactory factory =
new NettyClientInstrumenterFactory("io.opentelemetry.netty-4.0", alwaysCreateConnectSpan);
INSTRUMENTER = factory.createHttpInstrumenter();
CONNECT_INSTRUMENTER = factory.createConnectInstrumenter();
CONNECTION_INSTRUMENTER = factory.createConnectionInstrumenter();
}
public static Instrumenter<HttpRequestAndChannel, HttpResponse> instrumenter() {
return INSTRUMENTER;
}
public static NettyConnectInstrumenter connectInstrumenter() {
return CONNECT_INSTRUMENTER;
public static NettyConnectionInstrumenter connectionInstrumenter() {
return CONNECTION_INSTRUMENTER;
}
private NettyClientSingletons() {}

View File

@ -5,18 +5,24 @@
package io.opentelemetry.javaagent.instrumentation.netty.v4_1;
import static io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.NettyClientSingletons.connectInstrumenter;
import static io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.NettyClientSingletons.connectionInstrumenter;
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.netty.channel.ChannelFuture;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelPromise;
import io.netty.resolver.AddressResolverGroup;
import io.netty.resolver.DefaultAddressResolverGroup;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.client.ConnectionCompleteListener;
import io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.InstrumentedAddressResolverGroup;
import java.net.SocketAddress;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
@ -31,35 +37,68 @@ public class BootstrapInstrumentation implements TypeInstrumentation {
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("doResolveAndConnect").and(takesArgument(0, SocketAddress.class)),
isConstructor().and(takesArguments(0)),
BootstrapInstrumentation.class.getName() + "$ConstructorAdvice");
transformer.applyAdviceToMethod(
named("resolver")
.and(takesArguments(1))
.and(takesArgument(0, named("io.netty.resolver.AddressResolverGroup"))),
BootstrapInstrumentation.class.getName() + "$SetResolverAdvice");
transformer.applyAdviceToMethod(
named("doConnect")
.and(takesArguments(3))
.and(takesArgument(0, SocketAddress.class))
.and(takesArgument(2, named("io.netty.channel.ChannelPromise"))),
BootstrapInstrumentation.class.getName() + "$ConnectAdvice");
}
@SuppressWarnings("unused")
public static class ConstructorAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void onExit(@Advice.This Bootstrap bootstrap) {
// this is already the default value, but we're calling the resolver() method to invoke its
// instrumentation
bootstrap.resolver(DefaultAddressResolverGroup.INSTANCE);
}
}
@SuppressWarnings("unused")
public static class SetResolverAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false) AddressResolverGroup<?> resolver) {
resolver = InstrumentedAddressResolverGroup.wrap(connectionInstrumenter(), resolver);
}
}
@SuppressWarnings("unused")
public static class ConnectAdvice {
@Advice.OnMethodEnter
public static void startConnect(
@Advice.Argument(0) SocketAddress remoteAddress,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelRequest") NettyConnectRequest request,
@Advice.Local("otelRequest") NettyConnectionRequest request,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = Java8BytecodeBridge.currentContext();
request = NettyConnectRequest.create(remoteAddress);
request = NettyConnectionRequest.connect(remoteAddress);
if (!connectInstrumenter().shouldStart(parentContext, request)) {
if (!connectionInstrumenter().shouldStart(parentContext, request)) {
return;
}
context = connectInstrumenter().start(parentContext, request);
context = connectionInstrumenter().start(parentContext, request);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void endConnect(
@Advice.Thrown Throwable throwable,
@Advice.Return ChannelFuture channelFuture,
@Advice.Argument(2) ChannelPromise channelPromise,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelRequest") NettyConnectRequest request,
@Advice.Local("otelRequest") NettyConnectionRequest request,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
@ -68,10 +107,10 @@ public class BootstrapInstrumentation implements TypeInstrumentation {
scope.close();
if (throwable != null) {
connectInstrumenter().end(context, request, null, throwable);
connectionInstrumenter().end(context, request, null, throwable);
} else {
channelFuture.addListener(
new ConnectionCompleteListener(connectInstrumenter(), context, request));
channelPromise.addListener(
new ConnectionCompleteListener(connectionInstrumenter(), context, request));
}
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.netty.v4_1.client;
import static io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.NettyClientSingletons.connectionInstrumenter;
import io.netty.resolver.AddressResolver;
import io.netty.resolver.AddressResolverGroup;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.client.NettyConnectionInstrumenter;
import java.net.SocketAddress;
import java.util.List;
import java.util.function.Supplier;
public final class InstrumentedAddressResolverGroup<T extends SocketAddress>
extends AddressResolverGroup<T> {
public static <T extends SocketAddress> AddressResolverGroup<T> wrap(
NettyConnectionInstrumenter instrumenter, AddressResolverGroup<T> delegate) {
if (delegate instanceof InstrumentedAddressResolverGroup) {
return delegate;
}
return new InstrumentedAddressResolverGroup<>(connectionInstrumenter(), delegate);
}
private final NettyConnectionInstrumenter instrumenter;
private final AddressResolverGroup<T> delegate;
private InstrumentedAddressResolverGroup(
NettyConnectionInstrumenter instrumenter, AddressResolverGroup<T> delegate) {
this.instrumenter = instrumenter;
this.delegate = delegate;
}
@Override
public AddressResolver<T> getResolver(EventExecutor executor) {
return new InstrumentedResolver<>(instrumenter, delegate.getResolver(executor));
}
@Override
public void close() {
delegate.close();
}
@Override
protected AddressResolver<T> newResolver(EventExecutor eventExecutor) {
// this method is called from the super class's implementation of `getResolver` which is
// overridden by this class
throw new UnsupportedOperationException("This method should never be called");
}
private static final class InstrumentedResolver<T extends SocketAddress>
implements AddressResolver<T> {
private final NettyConnectionInstrumenter instrumenter;
private final AddressResolver<T> delegate;
private InstrumentedResolver(
NettyConnectionInstrumenter instrumenter, AddressResolver<T> delegate) {
this.instrumenter = instrumenter;
this.delegate = delegate;
}
@Override
public boolean isSupported(SocketAddress socketAddress) {
return delegate.isSupported(socketAddress);
}
@Override
public boolean isResolved(SocketAddress socketAddress) {
return delegate.isResolved(socketAddress);
}
@Override
public Future<T> resolve(SocketAddress socketAddress) {
return instrumentResolve(socketAddress, () -> delegate.resolve(socketAddress));
}
@Override
public Future<T> resolve(SocketAddress socketAddress, Promise<T> promise) {
return instrumentResolve(socketAddress, () -> delegate.resolve(socketAddress, promise));
}
@Override
public Future<List<T>> resolveAll(SocketAddress socketAddress) {
return instrumentResolve(socketAddress, () -> delegate.resolveAll(socketAddress));
}
@Override
public Future<List<T>> resolveAll(SocketAddress socketAddress, Promise<List<T>> promise) {
return instrumentResolve(socketAddress, () -> delegate.resolveAll(socketAddress, promise));
}
private <U> Future<U> instrumentResolve(
SocketAddress socketAddress, Supplier<Future<U>> resolveFunc) {
Context parentContext = Context.current();
NettyConnectionRequest request = NettyConnectionRequest.resolve(socketAddress);
if (!instrumenter.shouldStart(parentContext, request)) {
return resolveFunc.get();
}
Context context = instrumenter.start(parentContext, request);
try {
Future<U> future = resolveFunc.get();
return future.addListener(f -> instrumenter.end(context, request, null, f.cause()));
} catch (Throwable t) {
instrumenter.end(context, request, null, t);
throw t;
}
}
@Override
public void close() {
delegate.close();
}
}
}

View File

@ -11,7 +11,7 @@ import io.opentelemetry.instrumentation.api.config.Config;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.javaagent.instrumentation.netty.common.HttpRequestAndChannel;
import io.opentelemetry.javaagent.instrumentation.netty.common.client.NettyClientInstrumenterFactory;
import io.opentelemetry.javaagent.instrumentation.netty.common.client.NettyConnectInstrumenter;
import io.opentelemetry.javaagent.instrumentation.netty.common.client.NettyConnectionInstrumenter;
public final class NettyClientSingletons {
@ -24,21 +24,21 @@ public final class NettyClientSingletons {
Config.get().getBoolean("otel.instrumentation.netty.always-create-connect-span", false);
private static final Instrumenter<HttpRequestAndChannel, HttpResponse> INSTRUMENTER;
private static final NettyConnectInstrumenter CONNECT_INSTRUMENTER;
private static final NettyConnectionInstrumenter CONNECTION_INSTRUMENTER;
static {
NettyClientInstrumenterFactory factory =
new NettyClientInstrumenterFactory("io.opentelemetry.netty-4.1", alwaysCreateConnectSpan);
INSTRUMENTER = factory.createHttpInstrumenter();
CONNECT_INSTRUMENTER = factory.createConnectInstrumenter();
CONNECTION_INSTRUMENTER = factory.createConnectionInstrumenter();
}
public static Instrumenter<HttpRequestAndChannel, HttpResponse> instrumenter() {
return INSTRUMENTER;
}
public static NettyConnectInstrumenter connectInstrumenter() {
return CONNECT_INSTRUMENTER;
public static NettyConnectionInstrumenter connectionInstrumenter() {
return CONNECTION_INSTRUMENTER;
}
private NettyClientSingletons() {}

View File

@ -94,13 +94,23 @@ class Netty41ConnectionSpanTest extends InstrumentationSpecification implements
then:
responseCode == 200
assertTraces(1) {
trace(0, 4) {
trace(0, 5) {
span(0) {
name "parent"
kind INTERNAL
hasNoParent()
}
span(1) {
name "RESOLVE"
kind INTERNAL
childOf span(0)
attributes {
"${SemanticAttributes.NET_TRANSPORT.key}" IP_TCP
"${SemanticAttributes.NET_PEER_NAME.key}" uri.host
"${SemanticAttributes.NET_PEER_PORT.key}" uri.port
}
}
span(2) {
name "CONNECT"
kind INTERNAL
childOf(span(0))
@ -111,15 +121,15 @@ class Netty41ConnectionSpanTest extends InstrumentationSpecification implements
"${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1"
}
}
span(2) {
span(3) {
name "HTTP GET"
kind CLIENT
childOf(span(0))
}
span(3) {
span(4) {
name "test-http-server"
kind SERVER
childOf(span(2))
childOf(span(3))
}
}
}
@ -138,7 +148,7 @@ class Netty41ConnectionSpanTest extends InstrumentationSpecification implements
and:
assertTraces(1) {
trace(0, 2) {
trace(0, 3) {
span(0) {
name "parent"
kind INTERNAL
@ -147,9 +157,19 @@ class Netty41ConnectionSpanTest extends InstrumentationSpecification implements
errorEvent(thrownException.class, thrownException.message)
}
span(1) {
name "RESOLVE"
kind INTERNAL
childOf span(0)
attributes {
"${SemanticAttributes.NET_TRANSPORT.key}" IP_TCP
"${SemanticAttributes.NET_PEER_NAME.key}" uri.host
"${SemanticAttributes.NET_PEER_PORT.key}" uri.port
}
}
span(2) {
name "CONNECT"
kind INTERNAL
childOf(span(0))
childOf span(0)
status ERROR
errorEvent(thrownException.class, thrownException.message)
attributes {

View File

@ -1,23 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.netty.common;
import com.google.auto.value.AutoValue;
import java.net.SocketAddress;
import javax.annotation.Nullable;
@AutoValue
public abstract class NettyConnectRequest {
public static NettyConnectRequest create(SocketAddress remoteAddress) {
return new AutoValue_NettyConnectRequest(Timer.start(), remoteAddress);
}
public abstract Timer timer();
@Nullable
public abstract SocketAddress remoteAddressOnStart();
}

View File

@ -0,0 +1,29 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.netty.common;
import com.google.auto.value.AutoValue;
import java.net.SocketAddress;
import javax.annotation.Nullable;
@AutoValue
public abstract class NettyConnectionRequest {
public static NettyConnectionRequest resolve(SocketAddress remoteAddress) {
return new AutoValue_NettyConnectionRequest("RESOLVE", Timer.start(), remoteAddress);
}
public static NettyConnectionRequest connect(SocketAddress remoteAddress) {
return new AutoValue_NettyConnectionRequest("CONNECT", Timer.start(), remoteAddress);
}
public abstract String spanName();
public abstract Timer timer();
@Nullable
public abstract SocketAddress remoteAddressOnStart();
}

View File

@ -50,13 +50,23 @@ class ReactorNettyConnectionSpanTest extends InstrumentationSpecification implem
then:
responseCode == 200
assertTraces(1) {
trace(0, 4) {
trace(0, 5) {
span(0) {
name "parent"
kind INTERNAL
hasNoParent()
}
span(1) {
name "RESOLVE"
kind INTERNAL
childOf span(0)
attributes {
"${SemanticAttributes.NET_TRANSPORT.key}" IP_TCP
"${SemanticAttributes.NET_PEER_NAME.key}" "localhost"
"${SemanticAttributes.NET_PEER_PORT.key}" server.httpPort()
}
}
span(2) {
name "CONNECT"
kind INTERNAL
childOf(span(0))
@ -67,15 +77,15 @@ class ReactorNettyConnectionSpanTest extends InstrumentationSpecification implem
"${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1"
}
}
span(2) {
span(3) {
name "HTTP GET"
kind CLIENT
childOf(span(0))
}
span(3) {
span(4) {
name "test-http-server"
kind SERVER
childOf(span(2))
childOf(span(3))
}
}
}
@ -100,7 +110,7 @@ class ReactorNettyConnectionSpanTest extends InstrumentationSpecification implem
and:
assertTraces(1) {
trace(0, 2) {
trace(0, 3) {
span(0) {
name "parent"
kind INTERNAL
@ -109,6 +119,16 @@ class ReactorNettyConnectionSpanTest extends InstrumentationSpecification implem
errorEvent(thrownException.class, thrownException.message)
}
span(1) {
name "RESOLVE"
kind INTERNAL
childOf span(0)
attributes {
"${SemanticAttributes.NET_TRANSPORT.key}" IP_TCP
"${SemanticAttributes.NET_PEER_NAME.key}" "localhost"
"${SemanticAttributes.NET_PEER_PORT.key}" PortUtils.UNUSABLE_PORT
}
}
span(2) {
name "CONNECT"
kind INTERNAL
childOf(span(0))

View File

@ -5,18 +5,18 @@
package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0;
import static io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0.ReactorNettySingletons.connectInstrumenter;
import static io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0.ReactorNettySingletons.connectionInstrumenter;
import io.netty.channel.Channel;
import io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest;
import reactor.core.publisher.Mono;
public class ConnectionWrapper {
public static Mono<Channel> wrap(
Context context, NettyConnectRequest request, Mono<Channel> mono) {
return mono.doOnError(error -> connectInstrumenter().end(context, request, null, error))
.doOnSuccess(channel -> connectInstrumenter().end(context, request, channel, null));
Context context, NettyConnectionRequest request, Mono<Channel> mono) {
return mono.doOnError(error -> connectionInstrumenter().end(context, request, null, error))
.doOnSuccess(channel -> connectionInstrumenter().end(context, request, channel, null));
}
}

View File

@ -7,7 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0;
import io.opentelemetry.instrumentation.api.config.Config;
import io.opentelemetry.javaagent.instrumentation.netty.common.client.NettyClientInstrumenterFactory;
import io.opentelemetry.javaagent.instrumentation.netty.common.client.NettyConnectInstrumenter;
import io.opentelemetry.javaagent.instrumentation.netty.common.client.NettyConnectionInstrumenter;
public final class ReactorNettySingletons {
@ -15,17 +15,17 @@ public final class ReactorNettySingletons {
Config.get()
.getBoolean("otel.instrumentation.reactor-netty.always-create-connect-span", false);
private static final NettyConnectInstrumenter CONNECT_INSTRUMENTER;
private static final NettyConnectionInstrumenter CONNECTION_INSTRUMENTER;
static {
NettyClientInstrumenterFactory instrumenterFactory =
new NettyClientInstrumenterFactory(
"io.opentelemetry.reactor-netty-1.0", alwaysCreateConnectSpan);
CONNECT_INSTRUMENTER = instrumenterFactory.createConnectInstrumenter();
CONNECTION_INSTRUMENTER = instrumenterFactory.createConnectionInstrumenter();
}
public static NettyConnectInstrumenter connectInstrumenter() {
return CONNECT_INSTRUMENTER;
public static NettyConnectionInstrumenter connectionInstrumenter() {
return CONNECTION_INSTRUMENTER;
}
private ReactorNettySingletons() {}

View File

@ -5,7 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0;
import static io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0.ReactorNettySingletons.connectInstrumenter;
import static io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0.ReactorNettySingletons.connectionInstrumenter;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
@ -15,7 +15,7 @@ import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectRequest;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest;
import java.net.SocketAddress;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
@ -41,16 +41,16 @@ public class TransportConnectorInstrumentation implements TypeInstrumentation {
public static void startConnect(
@Advice.Argument(1) SocketAddress remoteAddress,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelRequest") NettyConnectRequest request,
@Advice.Local("otelRequest") NettyConnectionRequest request,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = Java8BytecodeBridge.currentContext();
request = NettyConnectRequest.create(remoteAddress);
if (!connectInstrumenter().shouldStart(parentContext, request)) {
request = NettyConnectionRequest.connect(remoteAddress);
if (!connectionInstrumenter().shouldStart(parentContext, request)) {
return;
}
context = connectInstrumenter().start(parentContext, request);
context = connectionInstrumenter().start(parentContext, request);
scope = context.makeCurrent();
}
@ -59,7 +59,7 @@ public class TransportConnectorInstrumentation implements TypeInstrumentation {
@Advice.Thrown Throwable throwable,
@Advice.Return(readOnly = false) Mono<Channel> mono,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelRequest") NettyConnectRequest request,
@Advice.Local("otelRequest") NettyConnectionRequest request,
@Advice.Local("otelScope") Scope scope) {
if (scope != null) {
@ -67,7 +67,7 @@ public class TransportConnectorInstrumentation implements TypeInstrumentation {
}
if (throwable != null) {
connectInstrumenter().end(context, request, null, throwable);
connectionInstrumenter().end(context, request, null, throwable);
} else {
mono = ConnectionWrapper.wrap(context, request, mono);
}