All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.opentelemetry.javaagent.instrumentation.netty.v4.common.AbstractNettyChannelPipelineInstrumentation Maven / Gradle / Ivy

/*
 * Copyright The OpenTelemetry Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package io.opentelemetry.javaagent.instrumentation.netty.v4.common;

import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelPipeline;
import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.Iterator;
import java.util.Map;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

public abstract class AbstractNettyChannelPipelineInstrumentation implements TypeInstrumentation {

  @Override
  public ElementMatcher classLoaderOptimization() {
    return hasClassesNamed("io.netty.channel.ChannelPipeline");
  }

  @Override
  public ElementMatcher typeMatcher() {
    return implementsInterface(named("io.netty.channel.ChannelPipeline"));
  }

  @Override
  public void transform(TypeTransformer transformer) {
    transformer.applyAdviceToMethod(
        isMethod()
            .and(namedOneOf("remove", "replace"))
            .and(takesArgument(0, named("io.netty.channel.ChannelHandler"))),
        AbstractNettyChannelPipelineInstrumentation.class.getName() + "$RemoveAdvice");
    transformer.applyAdviceToMethod(
        isMethod().and(namedOneOf("remove", "replace")).and(takesArgument(0, String.class)),
        AbstractNettyChannelPipelineInstrumentation.class.getName() + "$RemoveByNameAdvice");
    transformer.applyAdviceToMethod(
        isMethod().and(namedOneOf("remove", "replace")).and(takesArgument(0, Class.class)),
        AbstractNettyChannelPipelineInstrumentation.class.getName() + "$RemoveByClassAdvice");
    transformer.applyAdviceToMethod(
        isMethod().and(named("removeFirst")).and(returns(named("io.netty.channel.ChannelHandler"))),
        AbstractNettyChannelPipelineInstrumentation.class.getName() + "$RemoveFirstAdvice");
    transformer.applyAdviceToMethod(
        isMethod().and(named("removeLast")).and(returns(named("io.netty.channel.ChannelHandler"))),
        AbstractNettyChannelPipelineInstrumentation.class.getName() + "$RemoveLastAdvice");
    transformer.applyAdviceToMethod(
        isMethod()
            .and(named("addAfter"))
            .and(takesArgument(1, String.class))
            .and(takesArguments(4)),
        AbstractNettyChannelPipelineInstrumentation.class.getName() + "$AddAfterAdvice");
    transformer.applyAdviceToMethod(
        isMethod().and(named("toMap")).and(takesArguments(0)).and(returns(Map.class)),
        AbstractNettyChannelPipelineInstrumentation.class.getName() + "$ToMapAdvice");
  }

  @SuppressWarnings("unused")
  public static class RemoveAdvice {

    @Advice.OnMethodEnter(suppress = Throwable.class)
    public static void removeHandler(
        @Advice.This ChannelPipeline pipeline, @Advice.Argument(0) ChannelHandler handler) {
      VirtualField virtualField =
          VirtualField.find(ChannelHandler.class, ChannelHandler.class);
      ChannelHandler ourHandler = virtualField.get(handler);
      if (ourHandler != null) {
        if (pipeline.context(ourHandler) != null) {
          pipeline.remove(ourHandler);
        }
        virtualField.set(handler, null);
      }
    }
  }

  @SuppressWarnings("unused")
  public static class RemoveByNameAdvice {

    @Advice.OnMethodEnter(suppress = Throwable.class)
    public static void removeHandler(
        @Advice.This ChannelPipeline pipeline, @Advice.Argument(0) String name) {
      ChannelHandler handler = pipeline.get(name);
      if (handler == null) {
        return;
      }

      VirtualField virtualField =
          VirtualField.find(ChannelHandler.class, ChannelHandler.class);
      ChannelHandler ourHandler = virtualField.get(handler);
      if (ourHandler != null) {
        if (pipeline.context(ourHandler) != null) {
          pipeline.remove(ourHandler);
        }
        virtualField.set(handler, null);
      }
    }
  }

  @SuppressWarnings("unused")
  public static class RemoveByClassAdvice {

    @Advice.OnMethodEnter(suppress = Throwable.class)
    public static void removeHandler(
        @Advice.This ChannelPipeline pipeline,
        @Advice.Argument(0) Class handlerClass) {
      ChannelHandler handler = pipeline.get(handlerClass);
      if (handler == null) {
        return;
      }

      VirtualField virtualField =
          VirtualField.find(ChannelHandler.class, ChannelHandler.class);
      ChannelHandler ourHandler = virtualField.get(handler);
      if (ourHandler != null) {
        if (pipeline.context(ourHandler) != null) {
          pipeline.remove(ourHandler);
        }
        virtualField.set(handler, null);
      }
    }
  }

  @SuppressWarnings("unused")
  public static class RemoveFirstAdvice {

    @Advice.OnMethodExit(suppress = Throwable.class)
    public static void removeHandler(
        @Advice.This ChannelPipeline pipeline, @Advice.Return ChannelHandler handler) {
      VirtualField virtualField =
          VirtualField.find(ChannelHandler.class, ChannelHandler.class);
      ChannelHandler ourHandler = virtualField.get(handler);
      if (ourHandler != null) {
        if (pipeline.context(ourHandler) != null) {
          pipeline.remove(ourHandler);
        }
        virtualField.set(handler, null);
      }
    }
  }

  @SuppressWarnings("unused")
  public static class RemoveLastAdvice {

    @Advice.OnMethodExit(suppress = Throwable.class)
    public static void removeHandler(
        @Advice.This ChannelPipeline pipeline,
        @Advice.Return(readOnly = false) ChannelHandler handler) {
      VirtualField virtualField =
          VirtualField.find(ChannelHandler.class, ChannelHandler.class);
      ChannelHandler ourHandler = virtualField.get(handler);
      if (ourHandler != null) {
        // Context is null when our handler has already been removed. This happens when calling
        // removeLast first removed our handler and we called removeLast again to remove the http
        // handler.
        if (pipeline.context(ourHandler) != null) {
          pipeline.remove(ourHandler);
        }
        virtualField.set(handler, null);
      } else {
        String handlerClassName = handler.getClass().getName();
        if (handlerClassName.endsWith("TracingHandler")
            && (handlerClassName.startsWith("io.opentelemetry.javaagent.instrumentation.netty.")
                || handlerClassName.startsWith("io.opentelemetry.instrumentation.netty."))) {
          handler = pipeline.removeLast();
        }
      }
    }
  }

  @SuppressWarnings("unused")
  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) {
      ChannelHandler handler = pipeline.get(name);
      if (handler != null) {
        VirtualField virtualField =
            VirtualField.find(ChannelHandler.class, ChannelHandler.class);
        ChannelHandler ourHandler = virtualField.get(handler);
        if (ourHandler != null) {
          name = ourHandler.getClass().getName();
        }
      }
    }
  }

  @SuppressWarnings("unused")
  public static class ToMapAdvice {

    @Advice.OnMethodExit(suppress = Throwable.class)
    public static void wrapIterator(@Advice.Return Map map) {
      VirtualField virtualField =
          VirtualField.find(ChannelHandler.class, ChannelHandler.class);
      for (Iterator iterator = map.values().iterator(); iterator.hasNext(); ) {
        ChannelHandler handler = iterator.next();
        String handlerClassName = handler.getClass().getName();
        if (handlerClassName.endsWith("TracingHandler")
            && (handlerClassName.startsWith("io.opentelemetry.javaagent.instrumentation.netty.")
                || handlerClassName.startsWith("io.opentelemetry.instrumentation.netty."))) {
          iterator.remove();
        }
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy