make netty-* indy compatible (#11559)

Co-authored-by: Jonas Kunz <jonas.kunz@elastic.co>
Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
This commit is contained in:
SylvainJuge 2024-08-22 17:07:14 +02:00 committed by GitHub
parent d47289a4b2
commit d0d39b8698
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 282 additions and 159 deletions

View File

@ -34,16 +34,16 @@ public class ChannelTransportInstrumentation implements TypeInstrumentation {
public static class WriteAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter(@Advice.Local("otelScope") Scope scope) {
public static Scope methodEnter() {
Option<Context> ref = Helpers.CONTEXT_LOCAL.apply();
if (ref.isDefined()) {
scope = ref.get().makeCurrent();
return ref.get().makeCurrent();
}
return null;
}
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void methodExit(
@Advice.Local("otelScope") Scope scope, @Advice.Thrown Throwable thrown) {
public static void methodExit(@Advice.Enter Scope scope, @Advice.Thrown Throwable thrown) {
if (scope != null) {
scope.close();
}

View File

@ -8,11 +8,13 @@ package io.opentelemetry.javaagent.instrumentation.finaglehttp.v23_11;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.Arrays;
import java.util.List;
@AutoService(InstrumentationModule.class)
public class FinagleHttpInstrumentationModule extends InstrumentationModule {
public class FinagleHttpInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public FinagleHttpInstrumentationModule() {
super("finagle-http", "finagle-http-23.11");
@ -27,9 +29,17 @@ public class FinagleHttpInstrumentationModule extends InstrumentationModule {
}
@Override
public boolean isIndyModule() {
// injects helpers to access package-private members
return false;
public String getModuleGroup() {
// relies on netty and needs access to common netty instrumentation classes
return "netty";
}
@Override
public List<String> injectedClassNames() {
// these are injected so that they can access package-private members
return Arrays.asList(
"com.twitter.finagle.ChannelTransportHelpers",
"io.netty.channel.OpenTelemetryChannelInitializerDelegate");
}
@Override

View File

@ -42,9 +42,10 @@ public class H2StreamChannelInitInstrumentation implements TypeInstrumentation {
public static class InitServerAdvice {
@Advice.OnMethodExit
public static void handleExit(
@Advice.Return(readOnly = false) ChannelInitializer<Channel> initializer) {
initializer = Helpers.wrapServer(initializer);
@Advice.AssignReturned.ToReturned
public static ChannelInitializer<Channel> handleExit(
@Advice.Return ChannelInitializer<Channel> initializer) {
return Helpers.wrapServer(initializer);
}
}
@ -52,9 +53,10 @@ public class H2StreamChannelInitInstrumentation implements TypeInstrumentation {
public static class InitClientAdvice {
@Advice.OnMethodExit
public static void handleExit(
@Advice.Return(readOnly = false) ChannelInitializer<Channel> initializer) {
initializer = Helpers.wrapClient(initializer);
@Advice.AssignReturned.ToReturned
public static ChannelInitializer<Channel> handleExit(
@Advice.Return ChannelInitializer<Channel> initializer) {
return Helpers.wrapClient(initializer);
}
}
}

View File

@ -14,6 +14,7 @@ import io.opentelemetry.javaagent.bootstrap.executors.ContextPropagatingRunnable
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@ -35,13 +36,14 @@ public class LocalSchedulerActivationInstrumentation implements TypeInstrumentat
public static class WrapRunnableAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void wrap(@Advice.Argument(value = 0, readOnly = false) Runnable task) {
@Advice.AssignReturned.ToArguments(@ToArgument(0))
public static Runnable wrap(@Advice.Argument(value = 0) Runnable task) {
if (task == null) {
return;
return null;
}
Context context = Java8BytecodeBridge.currentContext();
task = ContextPropagatingRunnable.propagateContext(task, context);
return ContextPropagatingRunnable.propagateContext(task, context);
}
}
}

View File

@ -12,6 +12,7 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import scala.Function1;
@ -34,13 +35,13 @@ public class PromiseMonitoredInstrumentation implements TypeInstrumentation {
public static class WrapFunctionAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void wrap(
@Advice.Argument(value = 1, readOnly = false) Function1<?, ?> function1) {
@Advice.AssignReturned.ToArguments(@ToArgument(1))
public static Function1<?, ?> wrap(@Advice.Argument(value = 1) Function1<?, ?> function1) {
if (function1 == null) {
return;
return null;
}
function1 = Function1Wrapper.wrap(function1);
return Function1Wrapper.wrap(function1);
}
}
}

View File

@ -10,15 +10,22 @@ import static java.util.Arrays.asList;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.List;
@AutoService(InstrumentationModule.class)
public class TwitterUtilCoreInstrumentationModule extends InstrumentationModule {
public class TwitterUtilCoreInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public TwitterUtilCoreInstrumentationModule() {
super("twitter-util-core");
}
@Override
public String getModuleGroup() {
return "netty";
}
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return asList(

View File

@ -56,55 +56,54 @@ public class NettyChannelInstrumentation implements TypeInstrumentation {
public static class ChannelConnectAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.This Channel channel,
@Advice.Argument(0) SocketAddress remoteAddress,
@Advice.Local("otelParentContext") Context parentContext,
@Advice.Local("otelRequest") NettyConnectionRequest request,
@Advice.Local("otelTimer") Timer timer) {
public static NettyScope onEnter(
@Advice.This Channel channel, @Advice.Argument(0) SocketAddress remoteAddress) {
parentContext = Java8BytecodeBridge.currentContext();
Context parentContext = Java8BytecodeBridge.currentContext();
Span span = Java8BytecodeBridge.spanFromContext(parentContext);
if (!span.getSpanContext().isValid()) {
return;
return null;
}
VirtualField<Channel, NettyConnectionContext> virtualField =
VirtualField.find(Channel.class, NettyConnectionContext.class);
if (virtualField.get(channel) != null) {
return;
return null;
}
virtualField.set(channel, new NettyConnectionContext(parentContext));
request = NettyConnectionRequest.connect(remoteAddress);
timer = Timer.start();
NettyConnectionRequest request = NettyConnectionRequest.connect(remoteAddress);
Timer timer = Timer.start();
return new NettyScope(parentContext, request, timer);
}
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExit(
@Advice.Return ChannelFuture channelFuture,
@Advice.Thrown Throwable error,
@Advice.Local("otelParentContext") Context parentContext,
@Advice.Local("otelRequest") NettyConnectionRequest request,
@Advice.Local("otelTimer") Timer timer) {
@Advice.Enter NettyScope nettyScope) {
if (request == null) {
if (nettyScope == null) {
return;
}
if (error != null) {
if (connectionInstrumenter().shouldStart(parentContext, request)) {
if (connectionInstrumenter()
.shouldStart(nettyScope.getParentContext(), nettyScope.getRequest())) {
InstrumenterUtil.startAndEnd(
connectionInstrumenter(),
parentContext,
request,
nettyScope.getParentContext(),
nettyScope.getRequest(),
null,
error,
timer.startTime(),
timer.now());
nettyScope.getTimer().startTime(),
nettyScope.getTimer().now());
}
} else {
channelFuture.addListener(new ConnectionListener(parentContext, request, timer));
channelFuture.addListener(
new ConnectionListener(
nettyScope.getParentContext(), nettyScope.getRequest(), nettyScope.getTimer()));
}
}
}

View File

@ -51,25 +51,25 @@ public class NettyChannelPipelineInstrumentation implements TypeInstrumentation
public static class ChannelPipelineAdd2ArgsAdvice {
@Advice.OnMethodEnter
public static void checkDepth(
@Advice.This ChannelPipeline pipeline,
@Advice.Argument(1) ChannelHandler handler,
@Advice.Local("otelCallDepth") CallDepth callDepth) {
public static CallDepth checkDepth(
@Advice.This ChannelPipeline pipeline, @Advice.Argument(1) ChannelHandler handler) {
// Pipelines are created once as a factory and then copied multiple times using the same add
// methods as we are hooking. If our handler has already been added we need to remove it so we
// don't end up with duplicates (this throws an exception)
if (pipeline.get(handler.getClass().getName()) != null) {
pipeline.remove(handler.getClass().getName());
}
callDepth = CallDepth.forClass(ChannelPipeline.class);
CallDepth callDepth = CallDepth.forClass(ChannelPipeline.class);
callDepth.getAndIncrement();
return callDepth;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void addHandler(
@Advice.This ChannelPipeline pipeline,
@Advice.Argument(1) ChannelHandler handler,
@Advice.Local("otelCallDepth") CallDepth callDepth) {
@Advice.Enter CallDepth callDepth) {
if (callDepth.decrementAndGet() > 0) {
return;
}
@ -82,29 +82,28 @@ public class NettyChannelPipelineInstrumentation implements TypeInstrumentation
public static class ChannelPipelineAdd3ArgsAdvice {
@Advice.OnMethodEnter
public static void checkDepth(
@Advice.This ChannelPipeline pipeline,
@Advice.Argument(2) ChannelHandler handler,
@Advice.Local("otelCallDepth") CallDepth callDepth) {
public static CallDepth checkDepth(
@Advice.This ChannelPipeline pipeline, @Advice.Argument(2) ChannelHandler handler) {
// Pipelines are created once as a factory and then copied multiple times using the same add
// methods as we are hooking. If our handler has already been added we need to remove it so we
// don't end up with duplicates (this throws an exception)
if (pipeline.get(handler.getClass().getName()) != null) {
pipeline.remove(handler.getClass().getName());
}
callDepth = CallDepth.forClass(ChannelPipeline.class);
CallDepth callDepth = CallDepth.forClass(ChannelPipeline.class);
callDepth.getAndIncrement();
return callDepth;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void addHandler(
@Advice.This ChannelPipeline pipeline,
@Advice.Argument(2) ChannelHandler handler,
@Advice.Local("otelCallDepth") CallDepth callDepth) {
@Advice.Enter CallDepth callDepth) {
if (callDepth.decrementAndGet() > 0) {
return;
}
ChannelPipelineUtil.wrapHandler(pipeline, handler);
}
}

View File

@ -11,11 +11,13 @@ import static java.util.Arrays.asList;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.List;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public class NettyInstrumentationModule extends InstrumentationModule {
public class NettyInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public NettyInstrumentationModule() {
super("netty", "netty-3.8");
}
@ -25,6 +27,11 @@ public class NettyInstrumentationModule extends InstrumentationModule {
return hasClassesNamed("org.jboss.netty.handler.codec.http.HttpMessage");
}
@Override
public String getModuleGroup() {
return "netty";
}
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return asList(

View File

@ -0,0 +1,36 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.netty.v3_8;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest;
import io.opentelemetry.instrumentation.netty.common.internal.Timer;
/** Container used to carry state between enter and exit advices */
public class NettyScope {
private final Context parentContext;
private final NettyConnectionRequest request;
private final Timer timer;
public NettyScope(Context parentContext, NettyConnectionRequest request, Timer timer) {
this.parentContext = parentContext;
this.request = request;
this.timer = timer;
}
public Context getParentContext() {
return parentContext;
}
public NettyConnectionRequest getRequest() {
return request;
}
public Timer getTimer() {
return timer;
}
}

View File

@ -22,6 +22,7 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.Iterator;
import java.util.Map;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@ -154,11 +155,13 @@ public abstract class AbstractNettyChannelPipelineInstrumentation implements Typ
public static class RemoveLastAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void removeHandler(
@Advice.This ChannelPipeline pipeline,
@Advice.Return(readOnly = false) ChannelHandler handler) {
@Advice.AssignReturned.ToReturned
public static ChannelHandler removeHandler(
@Advice.This ChannelPipeline pipeline, @Advice.Return ChannelHandler returnHandler) {
VirtualField<ChannelHandler, ChannelHandler> virtualField =
VirtualField.find(ChannelHandler.class, ChannelHandler.class);
// TODO remove this extra variable when migrating to "indy only" instrumentation.
ChannelHandler handler = returnHandler;
ChannelHandler ourHandler = virtualField.get(handler);
if (ourHandler != null) {
// Context is null when our handler has already been removed. This happens when calling
@ -176,6 +179,7 @@ public abstract class AbstractNettyChannelPipelineInstrumentation implements Typ
handler = pipeline.removeLast();
}
}
return handler;
}
}
@ -183,9 +187,14 @@ public abstract class AbstractNettyChannelPipelineInstrumentation implements Typ
public static class AddAfterAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void addAfterHandler(
@Advice.This ChannelPipeline pipeline,
@Advice.Argument(value = 1, readOnly = false) String name) {
@Advice.AssignReturned.ToArguments(@ToArgument(1))
public static String addAfterHandler(
@Advice.This ChannelPipeline pipeline, @Advice.Argument(value = 1) String nameArg) {
// TODO remove this extra variable when migrating to "indy only" instrumentation.
// using an intermediate variable is required to keep the advice work with "inlined" and
// "indy" this is probably a minor side-effect of using @Advice.AssignReturned.ToArguments
// with and inlined advice.
String name = nameArg;
ChannelHandler handler = pipeline.get(name);
if (handler != null) {
VirtualField<ChannelHandler, ChannelHandler> virtualField =
@ -195,6 +204,7 @@ public abstract class AbstractNettyChannelPipelineInstrumentation implements Typ
name = ourHandler.getClass().getName();
}
}
return name;
}
}

View File

@ -19,6 +19,7 @@ import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@ -58,22 +59,30 @@ public class NettyFutureInstrumentation implements TypeInstrumentation {
public static class AddListenerAdvice {
@Advice.OnMethodEnter
public static void wrapListener(
@Advice.Argument(value = 0, readOnly = false)
GenericFutureListener<? extends Future<?>> listener) {
@Advice.AssignReturned.ToArguments(@ToArgument(0))
public static GenericFutureListener<? extends Future<?>> wrapListener(
@Advice.Argument(value = 0) GenericFutureListener<? extends Future<?>> listenerArg) {
// TODO remove this extra variable when migrating to "indy only" instrumentation.
GenericFutureListener<? extends Future<?>> listener = listenerArg;
if (FutureListenerWrappers.shouldWrap(listener)) {
listener = FutureListenerWrappers.wrap(Java8BytecodeBridge.currentContext(), listener);
}
return listener;
}
}
@SuppressWarnings("unused")
public static class AddListenersAdvice {
// here the AsScalar allows to assign the value of the returned array to the argument value,
// otherwise it's considered to be an Object[] that contains the arguments/return value/thrown
// exception assignments that bytebuddy has to do after the advice is invoked.
@Advice.OnMethodEnter
public static void wrapListener(
@Advice.Argument(value = 0, readOnly = false)
GenericFutureListener<? extends Future<?>>[] listeners) {
@Advice.AssignReturned.AsScalar
@Advice.AssignReturned.ToArguments(@ToArgument(0))
public static Object[] wrapListener(
@Advice.Argument(value = 0) GenericFutureListener<? extends Future<?>>[] listeners) {
Context context = Java8BytecodeBridge.currentContext();
@SuppressWarnings({"unchecked", "rawtypes"})
@ -86,7 +95,7 @@ public class NettyFutureInstrumentation implements TypeInstrumentation {
wrappedListeners[i] = listeners[i];
}
}
listeners = wrappedListeners;
return wrappedListeners;
}
}
@ -94,20 +103,24 @@ public class NettyFutureInstrumentation implements TypeInstrumentation {
public static class RemoveListenerAdvice {
@Advice.OnMethodEnter
public static void wrapListener(
@Advice.Argument(value = 0, readOnly = false)
GenericFutureListener<? extends Future<?>> listener) {
listener = FutureListenerWrappers.getWrapper(listener);
@Advice.AssignReturned.ToArguments(@ToArgument(0))
public static GenericFutureListener<? extends Future<?>> wrapListener(
@Advice.Argument(value = 0) GenericFutureListener<? extends Future<?>> listener) {
return FutureListenerWrappers.getWrapper(listener);
}
}
@SuppressWarnings("unused")
public static class RemoveListenersAdvice {
// here the AsScalar allows to assign the value of the returned array to the argument value,
// otherwise it's considered to be an Object[] that contains the arguments/return value/thrown
// exception assignments that bytebuddy has to do after the advice is invoked.
@Advice.OnMethodEnter
public static void wrapListener(
@Advice.Argument(value = 0, readOnly = false)
GenericFutureListener<? extends Future<?>>[] listeners) {
@Advice.AssignReturned.AsScalar
@Advice.AssignReturned.ToArguments(@ToArgument(0))
public static Object[] wrapListener(
@Advice.Argument(value = 0) GenericFutureListener<? extends Future<?>>[] listeners) {
@SuppressWarnings({"unchecked", "rawtypes"})
GenericFutureListener<? extends Future<?>>[] wrappedListeners =
@ -115,7 +128,7 @@ public class NettyFutureInstrumentation implements TypeInstrumentation {
for (int i = 0; i < listeners.length; ++i) {
wrappedListeners[i] = FutureListenerWrappers.getWrapper(listeners[i]);
}
listeners = wrappedListeners;
return wrappedListeners;
}
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.netty.v4.common;
import io.netty.channel.ChannelPromise;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest;
import io.opentelemetry.instrumentation.netty.v4.common.internal.client.ConnectionCompleteListener;
import io.opentelemetry.instrumentation.netty.v4.common.internal.client.NettyConnectionInstrumenter;
/** Container used to carry state between enter and exit advices */
public class NettyScope {
private final Context context;
private final NettyConnectionRequest request;
private final Scope scope;
private NettyScope(Context context, NettyConnectionRequest request, Scope scope) {
this.context = context;
this.request = request;
this.scope = scope;
}
public static NettyScope startNew(
NettyConnectionInstrumenter instrumenter,
Context parentContext,
NettyConnectionRequest request) {
Context context = instrumenter.start(parentContext, request);
return new NettyScope(context, request, context.makeCurrent());
}
public static void end(
NettyScope nettyScope,
NettyConnectionInstrumenter instrumenter,
ChannelPromise channelPromise,
Throwable throwable) {
if (nettyScope == null) {
return;
}
nettyScope.scope.close();
if (throwable != null) {
instrumenter.end(nettyScope.context, nettyScope.request, null, throwable);
} else {
channelPromise.addListener(
new ConnectionCompleteListener(instrumenter, nettyScope.context, nettyScope.request));
}
}
}

View File

@ -11,12 +11,11 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import io.netty.channel.ChannelPromise;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest;
import io.opentelemetry.instrumentation.netty.v4.common.internal.client.ConnectionCompleteListener;
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.netty.v4.common.NettyScope;
import java.net.SocketAddress;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
@ -40,42 +39,24 @@ public class BootstrapInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused")
public static class ConnectAdvice {
@Advice.OnMethodEnter
public static void startConnect(
@Advice.Argument(2) SocketAddress remoteAddress,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelRequest") NettyConnectionRequest request,
@Advice.Local("otelScope") Scope scope) {
public static NettyScope startConnect(@Advice.Argument(2) SocketAddress remoteAddress) {
Context parentContext = Java8BytecodeBridge.currentContext();
request = NettyConnectionRequest.connect(remoteAddress);
NettyConnectionRequest request = NettyConnectionRequest.connect(remoteAddress);
if (!connectionInstrumenter().shouldStart(parentContext, request)) {
return;
return null;
}
context = connectionInstrumenter().start(parentContext, request);
scope = context.makeCurrent();
return NettyScope.startNew(connectionInstrumenter(), parentContext, request);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void endConnect(
@Advice.Thrown Throwable throwable,
@Advice.Argument(4) ChannelPromise channelPromise,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelRequest") NettyConnectionRequest request,
@Advice.Local("otelScope") Scope scope) {
@Advice.Enter NettyScope enterScope) {
if (scope == null) {
return;
}
scope.close();
if (throwable != null) {
connectionInstrumenter().end(context, request, null, throwable);
} else {
channelPromise.addListener(
new ConnectionCompleteListener(connectionInstrumenter(), context, request));
}
NettyScope.end(enterScope, connectionInstrumenter(), channelPromise, throwable);
}
}
}

View File

@ -54,16 +54,18 @@ public class NettyChannelPipelineInstrumentation
public static class ChannelPipelineAddAdvice {
@Advice.OnMethodEnter
public static void trackCallDepth(@Advice.Local("otelCallDepth") CallDepth callDepth) {
callDepth = CallDepth.forClass(ChannelPipeline.class);
public static CallDepth trackCallDepth() {
CallDepth callDepth = CallDepth.forClass(ChannelPipeline.class);
callDepth.getAndIncrement();
return callDepth;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void addHandler(
@Advice.This ChannelPipeline pipeline,
@Advice.Argument(2) ChannelHandler handler,
@Advice.Local("otelCallDepth") CallDepth callDepth) {
@Advice.Enter CallDepth callDepth) {
if (callDepth.decrementAndGet() > 0) {
return;
}

View File

@ -12,12 +12,14 @@ import static net.bytebuddy.matcher.ElementMatchers.not;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import io.opentelemetry.javaagent.instrumentation.netty.v4.common.NettyFutureInstrumentation;
import java.util.List;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public class NettyInstrumentationModule extends InstrumentationModule {
public class NettyInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public NettyInstrumentationModule() {
super("netty", "netty-4.0");
}
@ -31,6 +33,11 @@ public class NettyInstrumentationModule extends InstrumentationModule {
not(hasClassesNamed("io.netty.handler.codec.http.CombinedHttpHeaders")));
}
@Override
public String getModuleGroup() {
return "netty";
}
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return asList(

View File

@ -16,14 +16,14 @@ 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.instrumentation.netty.common.internal.NettyConnectionRequest;
import io.opentelemetry.instrumentation.netty.v4.common.internal.client.ConnectionCompleteListener;
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.netty.v4.common.NettyScope;
import java.net.SocketAddress;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@ -66,50 +66,34 @@ public class BootstrapInstrumentation implements TypeInstrumentation {
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);
@Advice.AssignReturned.ToArguments(@ToArgument(0))
public static AddressResolverGroup<?> onEnter(
@Advice.Argument(value = 0) AddressResolverGroup<?> resolver) {
return 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") NettyConnectionRequest request,
@Advice.Local("otelScope") Scope scope) {
public static NettyScope startConnect(@Advice.Argument(0) SocketAddress remoteAddress) {
Context parentContext = Java8BytecodeBridge.currentContext();
request = NettyConnectionRequest.connect(remoteAddress);
NettyConnectionRequest request = NettyConnectionRequest.connect(remoteAddress);
if (!connectionInstrumenter().shouldStart(parentContext, request)) {
return;
return null;
}
context = connectionInstrumenter().start(parentContext, request);
scope = context.makeCurrent();
return NettyScope.startNew(connectionInstrumenter(), parentContext, request);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void endConnect(
@Advice.Thrown Throwable throwable,
@Advice.Argument(2) ChannelPromise channelPromise,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelRequest") NettyConnectionRequest request,
@Advice.Local("otelScope") Scope scope) {
@Advice.Enter NettyScope enterScope) {
if (scope == null) {
return;
}
scope.close();
if (throwable != null) {
connectionInstrumenter().end(context, request, null, throwable);
} else {
channelPromise.addListener(
new ConnectionCompleteListener(connectionInstrumenter(), context, request));
}
NettyScope.end(enterScope, connectionInstrumenter(), channelPromise, throwable);
}
}
}

View File

@ -52,9 +52,7 @@ public class NettyChannelPipelineInstrumentation
public static class ChannelPipelineAddAdvice {
@Advice.OnMethodEnter
public static void trackCallDepth(
@Advice.Argument(2) ChannelHandler handler,
@Advice.Local("otelCallDepth") CallDepth callDepth) {
public static CallDepth trackCallDepth(@Advice.Argument(2) ChannelHandler handler) {
// Previously we used one unique call depth tracker for all handlers, using
// ChannelPipeline.class as a key.
// The problem with this approach is that it does not work with netty's
@ -64,8 +62,9 @@ public class NettyChannelPipelineInstrumentation
// Using the specific handler key instead of the generic ChannelPipeline.class will help us
// both to handle such cases and avoid adding our additional handlers in case of internal
// calls of `addLast` to other method overloads with a compatible signature.
callDepth = CallDepth.forClass(handler.getClass());
CallDepth callDepth = CallDepth.forClass(handler.getClass());
callDepth.getAndIncrement();
return callDepth;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
@ -73,7 +72,8 @@ public class NettyChannelPipelineInstrumentation
@Advice.This ChannelPipeline pipeline,
@Advice.Argument(1) String handlerName,
@Advice.Argument(2) ChannelHandler handler,
@Advice.Local("otelCallDepth") CallDepth callDepth) {
@Advice.Enter CallDepth callDepth) {
if (callDepth.decrementAndGet() > 0) {
return;
}

View File

@ -11,12 +11,14 @@ import static java.util.Arrays.asList;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import io.opentelemetry.javaagent.instrumentation.netty.v4.common.NettyFutureInstrumentation;
import java.util.List;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public class NettyInstrumentationModule extends InstrumentationModule {
public class NettyInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public NettyInstrumentationModule() {
super("netty", "netty-4.1");
}
@ -29,10 +31,8 @@ public class NettyInstrumentationModule extends InstrumentationModule {
}
@Override
public boolean isIndyModule() {
// netty instrumentation classes are used in other instrumentations which causes class cast
// exceptions
return false;
public String getModuleGroup() {
return "netty";
}
@Override

View File

@ -10,22 +10,20 @@ import static java.util.Arrays.asList;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.List;
@AutoService(InstrumentationModule.class)
public class RatpackInstrumentationModule extends InstrumentationModule {
public class RatpackInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public RatpackInstrumentationModule() {
super("ratpack", "ratpack-1.4");
}
@Override
public boolean isIndyModule() {
// java.lang.ClassCastException: class
// io.opentelemetry.javaagent.shaded.instrumentation.netty.v4_1.internal.AutoValue_ServerContext
// cannot be cast to class
// io.opentelemetry.javaagent.shaded.instrumentation.netty.v4_1.internal.ServerContext
// (io.opentelemetry.javaagent.shaded.instrumentation.netty.v4_1.internal.AutoValue_ServerContext is in unnamed module of loader 'app'; io.opentelemetry.javaagent.shaded.instrumentation.netty.v4_1.internal.ServerContext is in unnamed module of loader io.opentelemetry.javaagent.tooling.instrumentation.indy.InstrumentationModuleClassLoader @7f088b5c)
return false;
public String getModuleGroup() {
// relies on netty
return "netty";
}
@Override

View File

@ -10,10 +10,12 @@ import static java.util.Arrays.asList;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.List;
@AutoService(InstrumentationModule.class)
public class GatewayInstrumentationModule extends InstrumentationModule {
public class GatewayInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public GatewayInstrumentationModule() {
super("spring-cloud-gateway");
@ -24,6 +26,12 @@ public class GatewayInstrumentationModule extends InstrumentationModule {
return asList(new HandlerAdapterInstrumentation());
}
@Override
public String getModuleGroup() {
// relies on netty
return "netty";
}
@Override
public int order() {
// Later than Spring Webflux.

View File

@ -8,20 +8,22 @@ package io.opentelemetry.javaagent.instrumentation.spring.webflux.v5_0.server.re
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.Arrays;
import java.util.List;
@AutoService(InstrumentationModule.class)
public class ReactorNettyInstrumentationModule extends InstrumentationModule {
public class ReactorNettyInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public ReactorNettyInstrumentationModule() {
super("reactor-netty", "reactor-netty-server");
}
@Override
public boolean isIndyModule() {
// uses classes from netty instrumentation that are now in a different class loader
return false;
public String getModuleGroup() {
// relies on netty
return "netty";
}
@Override