From 1d2ede12cfee23bcb740f530cd7e69a075191a61 Mon Sep 17 00:00:00 2001 From: YuI <605354966@qq.com> Date: Tue, 22 Mar 2022 03:06:06 +0800 Subject: [PATCH] Add support for apache shenyu (#5629) --- .../shenyu/javaagent/build.gradle.kts | 20 ++++++ .../shenyu/ShenYuCommonPluginAdvice.java | 59 ++++++++++++++++ .../ShenYuDoDubboInvokerInstrumentation.java | 31 +++++++++ .../ShenYuDoExecuteInstrumentation.java | 31 +++++++++ .../shenyu/ShenYuExecuteInstrumentation.java | 32 +++++++++ .../shenyu/ShenYuInstrumentationModule.java | 27 ++++++++ .../shenyu/ShenYuPluginUtils.java | 68 +++++++++++++++++++ .../shenyu/ShenYuSingletons.java | 44 ++++++++++++ .../shenyu/ShenYuSpanNameExtractor.java | 17 +++++ settings.gradle.kts | 1 + 10 files changed, 330 insertions(+) create mode 100644 instrumentation/shenyu/javaagent/build.gradle.kts create mode 100644 instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuCommonPluginAdvice.java create mode 100644 instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuDoDubboInvokerInstrumentation.java create mode 100644 instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuDoExecuteInstrumentation.java create mode 100644 instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuExecuteInstrumentation.java create mode 100644 instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuInstrumentationModule.java create mode 100644 instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuPluginUtils.java create mode 100644 instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuSingletons.java create mode 100644 instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuSpanNameExtractor.java diff --git a/instrumentation/shenyu/javaagent/build.gradle.kts b/instrumentation/shenyu/javaagent/build.gradle.kts new file mode 100644 index 000000000000..0f6cffc640e5 --- /dev/null +++ b/instrumentation/shenyu/javaagent/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +muzzle { + pass { + group.set("org.apache.shenyu") + module.set("shenyu") + versions.set("[2.4.3,)") + assertInverse.set(true) + } +} + +dependencies { + compileOnly("org.springframework:spring-webflux:5.0.0.RELEASE") +} + +tasks.withType().configureEach { + jvmArgs("-Dotel.instrumentation.shenyu.experimental-span-attributes=true") +} \ No newline at end of file diff --git a/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuCommonPluginAdvice.java b/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuCommonPluginAdvice.java new file mode 100644 index 000000000000..b562055b9354 --- /dev/null +++ b/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuCommonPluginAdvice.java @@ -0,0 +1,59 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.shenyu; + +import static io.opentelemetry.javaagent.instrumentation.shenyu.ShenYuPluginUtils.end; +import static io.opentelemetry.javaagent.instrumentation.shenyu.ShenYuPluginUtils.registerSpan; +import static io.opentelemetry.javaagent.instrumentation.shenyu.ShenYuSingletons.httpRouteGetter; +import static io.opentelemetry.javaagent.instrumentation.shenyu.ShenYuSingletons.instrumenter; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteHolder; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteSource; +import net.bytebuddy.asm.Advice; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +public class ShenYuCommonPluginAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.Argument(0) ServerWebExchange exchange, + @Advice.This Object self, + @Advice.Local("otelContext") Context context, + @Advice.Local("otelScope") Scope scope) { + Context parentContext = Context.current(); + + HttpRouteHolder.updateHttpRoute( + parentContext, HttpRouteSource.CONTROLLER, httpRouteGetter(), exchange); + + if (!instrumenter().shouldStart(parentContext, self)) { + return; + } + + context = instrumenter().start(parentContext, self); + scope = context.makeCurrent(); + + registerSpan(exchange, context, self); + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void onExit(@Advice.Argument(0) ServerWebExchange exchange, + @Advice.Return(readOnly = false) Mono mono, + @Advice.Thrown Throwable exception, + @Advice.Local("otelContext") Context context, + @Advice.Local("otelScope") Scope scope) { + + if (scope != null) { + scope.close(); + } + + if (mono != null) { + mono = end(mono, exchange); + } + } + +} diff --git a/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuDoDubboInvokerInstrumentation.java b/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuDoDubboInvokerInstrumentation.java new file mode 100644 index 000000000000..29696d500015 --- /dev/null +++ b/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuDoDubboInvokerInstrumentation.java @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.shenyu; + +import static net.bytebuddy.matcher.ElementMatchers.hasSuperClass; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class ShenYuDoDubboInvokerInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return hasSuperClass(named("org.apache.shenyu.plugin.dubbo.common.AbstractDubboPlugin")); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isMethod().and(named("doDubboInvoker")), + ShenYuCommonPluginAdvice.class.getName()); + } + +} \ No newline at end of file diff --git a/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuDoExecuteInstrumentation.java b/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuDoExecuteInstrumentation.java new file mode 100644 index 000000000000..4444de0708f8 --- /dev/null +++ b/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuDoExecuteInstrumentation.java @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.shenyu; + +import static net.bytebuddy.matcher.ElementMatchers.hasSuperClass; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class ShenYuDoExecuteInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return hasSuperClass(named("org.apache.shenyu.plugin.base.AbstractShenyuPlugin")); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isMethod().and(named("doExecute")), + ShenYuCommonPluginAdvice.class.getName()); + } + +} \ No newline at end of file diff --git a/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuExecuteInstrumentation.java b/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuExecuteInstrumentation.java new file mode 100644 index 000000000000..ff342ab63f10 --- /dev/null +++ b/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuExecuteInstrumentation.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.shenyu; + +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; + +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class ShenYuExecuteInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return namedOneOf("org.apache.shenyu.plugin.global.GlobalPlugin", + "org.apache.shenyu.plugin.response.ResponsePlugin"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isMethod().and(named("execute")), + ShenYuCommonPluginAdvice.class.getName()); + } + +} \ No newline at end of file diff --git a/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuInstrumentationModule.java b/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuInstrumentationModule.java new file mode 100644 index 000000000000..589ed19d89d0 --- /dev/null +++ b/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuInstrumentationModule.java @@ -0,0 +1,27 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.shenyu; + +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 java.util.List; + +@AutoService(InstrumentationModule.class) +public class ShenYuInstrumentationModule extends InstrumentationModule { + + public ShenYuInstrumentationModule() { + super("shenyu"); + } + + @Override + public List typeInstrumentations() { + return asList(new ShenYuExecuteInstrumentation(), new ShenYuDoExecuteInstrumentation()); + } + +} diff --git a/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuPluginUtils.java b/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuPluginUtils.java new file mode 100644 index 000000000000..611dc7a72856 --- /dev/null +++ b/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuPluginUtils.java @@ -0,0 +1,68 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.shenyu; + +import static io.opentelemetry.javaagent.instrumentation.shenyu.ShenYuSingletons.instrumenter; + +import io.opentelemetry.context.Context; +import java.util.Deque; +import java.util.LinkedList; +import javax.annotation.Nullable; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +public class ShenYuPluginUtils { + + public static final String ON_SPAN_END = ShenYuPluginUtils.class.getName() + ".Context"; + + public static void registerSpan(ServerWebExchange exchange, Context context, Object plugin) { + ShenYuPlugin shenYuPlugin = (ShenYuPlugin) exchange.getAttributes() + .getOrDefault(ON_SPAN_END, new ShenYuPlugin()); + + shenYuPlugin.getOnSpanEndDeque().addLast(t -> instrumenter().end(context, plugin, null, t)); + + exchange.getAttributes() + .put(ON_SPAN_END, shenYuPlugin); + } + + public static Mono end(Mono mono, ServerWebExchange exchange) { + return mono.doOnError(throwable -> end(exchange, throwable)) + .doOnSuccess(t -> end(exchange, null)) + .doOnCancel(() -> end(exchange, null)); + } + + private static void end(ServerWebExchange exchange, @Nullable Throwable throwable) { + ShenYuPlugin shenYuPlugin = (ShenYuPlugin) exchange.getAttributes().get(ON_SPAN_END); + Deque onSpanEndDeque = shenYuPlugin.getOnSpanEndDeque(); + + OnSpanEnd onSpanEnd = onSpanEndDeque.pollLast(); + if (onSpanEnd != null) { + onSpanEnd.end(throwable); + } + + } + + @FunctionalInterface + interface OnSpanEnd { + void end(Throwable throwable); + } + + /** + * Many Plugins in ShenYu, so record these in Deque + */ + protected static class ShenYuPlugin { + private final Deque onSpanEndDeque; + + public ShenYuPlugin() { + onSpanEndDeque = new LinkedList<>(); + } + + public Deque getOnSpanEndDeque() { + return onSpanEndDeque; + } + } + +} diff --git a/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuSingletons.java b/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuSingletons.java new file mode 100644 index 000000000000..15df9dd4b9bf --- /dev/null +++ b/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuSingletons.java @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.shenyu; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteGetter; +import org.springframework.web.reactive.HandlerMapping; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.util.pattern.PathPattern; + +public class ShenYuSingletons { + + private static final String INSTRUMENTATION_NAME = "org.apache.shenyu"; + + private static final Instrumenter INSTRUMENTER; + + static { + InstrumenterBuilder builder = + Instrumenter.builder( + GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, new ShenYuSpanNameExtractor()); + + INSTRUMENTER = builder.newInstrumenter(); + } + + public static Instrumenter instrumenter() { + return INSTRUMENTER; + } + + public static HttpRouteGetter httpRouteGetter() { + return (context, exchange) -> { + PathPattern bestPattern = + exchange.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); + return bestPattern == null ? null : bestPattern.getPatternString(); + }; + } + + private ShenYuSingletons() {} + +} diff --git a/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuSpanNameExtractor.java b/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuSpanNameExtractor.java new file mode 100644 index 000000000000..6910674fc070 --- /dev/null +++ b/instrumentation/shenyu/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/shenyu/ShenYuSpanNameExtractor.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.shenyu; + +import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; + +public class ShenYuSpanNameExtractor implements SpanNameExtractor { + + @Override + public String extract(Object plugin) { + return plugin.getClass().getSimpleName(); + } + +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 5d1dc952c4a2..95dd2a3f007e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -373,6 +373,7 @@ include(":instrumentation:servlet:servlet-javax-common:javaagent") include(":instrumentation:servlet:servlet-2.2:javaagent") include(":instrumentation:servlet:servlet-3.0:javaagent") include(":instrumentation:servlet:servlet-5.0:javaagent") +include(":instrumentation:shenyu:javaagent") include(":instrumentation:spark-2.3:javaagent") include(":instrumentation:spring:spring-batch-3.0:javaagent") include(":instrumentation:spring:spring-core-2.0:javaagent")