Skip to content

Commit

Permalink
fetch route in apache shenyu (#11260)
Browse files Browse the repository at this point in the history
Co-authored-by: Lauri Tulmin <ltulmin@splunk.com>
Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
  • Loading branch information
3 people committed May 17, 2024
1 parent d37c739 commit 9e48f24
Show file tree
Hide file tree
Showing 11 changed files with 392 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/supported-libraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ These are the supported libraries and frameworks:
| [Apache Dubbo](https://github.com/apache/dubbo/) | 2.7+ | [opentelemetry-apache-dubbo-2.7](../instrumentation/apache-dubbo-2.7/library-autoconfigure) | [RPC Client Spans], [RPC Server Spans] |
| [Apache HttpAsyncClient](https://hc.apache.org/index.html) | 4.1+ | N/A | [HTTP Client Spans], [HTTP Client Metrics] |
| [Apache HttpClient](https://hc.apache.org/index.html) | 2.0+ | [opentelemetry-apache-httpclient-4.3](../instrumentation/apache-httpclient/apache-httpclient-4.3/library),<br>[opentelemetry-apache-httpclient-5.2](../instrumentation/apache-httpclient/apache-httpclient-5.2/library) | [HTTP Client Spans], [HTTP Client Metrics] |
| [Apache Shenyu](https://shenyu.apache.org/) | 2.4+ | N/A | Provides `http.route` [2] |
| [Apache Kafka Producer/Consumer API](https://kafka.apache.org/documentation/#producerapi) | 0.11+ | [opentelemetry-kafka-clients-2.6](../instrumentation/kafka/kafka-clients/kafka-clients-2.6/library) | [Messaging Spans] |
| [Apache Kafka Streams API](https://kafka.apache.org/documentation/streams/) | 0.11+ | N/A | [Messaging Spans] |
| [Apache MyFaces](https://myfaces.apache.org/) | 1.2+ (not including 3.x yet) | N/A | Provides `http.route` [2], Controller Spans [3] |
Expand Down
5 changes: 5 additions & 0 deletions instrumentation/apache-shenyu-2.4/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Settings for the Apache Shenyu instrumentation

| System property | Type | Default | Description |
|---------------------------------------------------------------------| ------- | ------- |---------------------------------------------------------------------------------------------|
| `otel.instrumentation.apache-shenyu.experimental-span-attributes` | Boolean | `false` | Enable the capture of experimental span attributes. |
51 changes: 51 additions & 0 deletions instrumentation/apache-shenyu-2.4/javaagent/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
plugins {
id("otel.javaagent-instrumentation")
}

muzzle {
pass {
group.set("org.apache.shenyu")
module.set("shenyu-web")
versions.set("[2.4.0,)")
assertInverse.set(true)
}
}

dependencies {
compileOnly("org.apache.shenyu:shenyu-web:2.4.0")
compileOnly("com.google.auto.value:auto-value-annotations")
annotationProcessor("com.google.auto.value:auto-value")

testLibrary("org.springframework.boot:spring-boot-starter-test:2.0.0.RELEASE")

// based on apache shenyu 2.4.0 official example
testLibrary("org.apache.shenyu:shenyu-spring-boot-starter-gateway:2.4.0") {
exclude("org.codehaus.groovy", "groovy")
}
testImplementation("org.springframework.boot:spring-boot-starter-webflux:2.2.2.RELEASE") {
exclude("org.codehaus.groovy", "groovy")
}

// the latest version of apache shenyu uses spring-boot 2.7
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-test:2.7.+")

testInstrumentation(project(":instrumentation:netty:netty-4.1:javaagent"))
}

tasks.withType<Test>().configureEach {
jvmArgs("-Dotel.instrumentation.apache-shenyu.experimental-span-attributes=true")

// required on jdk17
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")

systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean)
}

configurations.testRuntimeClasspath {
resolutionStrategy {
// requires old logback (and therefore also old slf4j)
force("ch.qos.logback:logback-classic:1.2.11")
force("org.slf4j:slf4j-api:1.7.36")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.apacheshenyu.v2_4;

import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import java.util.Collections;
import java.util.List;

@AutoService(InstrumentationModule.class)
public class ApacheShenYuInstrumentationModule extends InstrumentationModule {
public ApacheShenYuInstrumentationModule() {
super("apache-shenyu", "apache-shenyu-2.4");
}

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return Collections.singletonList(new ContextBuilderInstrumentation());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.apacheshenyu.v2_4;

import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteGetter;
import org.apache.shenyu.common.dto.MetaData;

public final class ApacheShenYuSingletons {

private ApacheShenYuSingletons() {}

public static HttpServerRouteGetter<MetaData> httpRouteGetter() {
return (context, metaData) -> metaData.getPath();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.apacheshenyu.v2_4;

import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute;
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.shenyu.common.constant.Constants;
import org.apache.shenyu.common.dto.MetaData;
import org.springframework.web.server.ServerWebExchange;

public class ContextBuilderInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.apache.shenyu.plugin.global.DefaultShenyuContextBuilder");
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("build")
.and(takesArgument(0, named("org.springframework.web.server.ServerWebExchange")))
.and(isPublic()),
this.getClass().getName() + "$BuildAdvice");
}

@SuppressWarnings("unused")
public static class BuildAdvice {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(@Advice.Argument(0) ServerWebExchange exchange) {
Context context = Context.current();
MetaData metaData = exchange.getAttribute(Constants.META_DATA);
if (metaData == null) {
return;
}
HttpServerRoute.update(
context,
HttpServerRouteSource.NESTED_CONTROLLER,
ApacheShenYuSingletons.httpRouteGetter(),
metaData);
MetaDataHelper.extractAttributes(metaData, context);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.apacheshenyu.v2_4;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.LocalRootSpan;
import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;
import org.apache.shenyu.common.dto.MetaData;

public final class MetaDataHelper {

/** ID for apache shenyu metadata * */
private static final AttributeKey<String> META_ID_ATTRIBUTE =
AttributeKey.stringKey("apache-shenyu.meta.id");

/** App name for apache shenyu metadata * */
private static final AttributeKey<String> APP_NAME_ATTRIBUTE =
AttributeKey.stringKey("apache-shenyu.meta.app-name");

/** Context path for apache shenyu metadata * */
private static final AttributeKey<String> CONTEXT_PATH_ATTRIBUTE =
AttributeKey.stringKey("apache-shenyu.meta.context-path");

/** Path for apache shenyu metadata * */
private static final AttributeKey<String> PATH_ATTRIBUTE =
AttributeKey.stringKey("apache-shenyu.meta.path");

/** Rpc type for apache shenyu metadata * */
private static final AttributeKey<String> RPC_TYPE_ATTRIBUTE =
AttributeKey.stringKey("apache-shenyu.meta.rpc-type");

/** Service name for apache shenyu metadata * */
private static final AttributeKey<String> SERVICE_NAME_ATTRIBUTE =
AttributeKey.stringKey("apache-shenyu.meta.service-name");

/** Method name for apache shenyu metadata * */
private static final AttributeKey<String> METHOD_NAME_ATTRIBUTE =
AttributeKey.stringKey("apache-shenyu.meta.method-name");

/** Parameter types for apache shenyu metadata * */
private static final AttributeKey<String> PARAMETER_TYPES_ATTRIBUTE =
AttributeKey.stringKey("apache-shenyu.meta.param-types");

/** Rpc extension for apache shenyu metadata * */
private static final AttributeKey<String> RPC_EXT_ATTRIBUTE =
AttributeKey.stringKey("apache-shenyu.meta.rpc-ext");

/** Rpc extension for apache shenyu metadata * */
private static final AttributeKey<Boolean> META_ENABLED_ATTRIBUTE =
AttributeKey.booleanKey("apache-shenyu.meta.enabled");

private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES;

static {
CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES =
InstrumentationConfig.get()
.getBoolean("otel.instrumentation.apache-shenyu.experimental-span-attributes", false);
}

private MetaDataHelper() {}

public static void extractAttributes(MetaData metadata, Context context) {
if (metadata != null && CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) {
Span serverSpan = LocalRootSpan.fromContextOrNull(context);
if (serverSpan == null) {
return;
}
serverSpan.setAttribute(META_ID_ATTRIBUTE, metadata.getId());
serverSpan.setAttribute(APP_NAME_ATTRIBUTE, metadata.getAppName());
serverSpan.setAttribute(CONTEXT_PATH_ATTRIBUTE, metadata.getContextPath());
serverSpan.setAttribute(PATH_ATTRIBUTE, metadata.getPath());
serverSpan.setAttribute(RPC_TYPE_ATTRIBUTE, metadata.getRpcType());
serverSpan.setAttribute(SERVICE_NAME_ATTRIBUTE, metadata.getServiceName());
serverSpan.setAttribute(METHOD_NAME_ATTRIBUTE, metadata.getMethodName());
serverSpan.setAttribute(PARAMETER_TYPES_ATTRIBUTE, metadata.getParameterTypes());
serverSpan.setAttribute(RPC_EXT_ATTRIBUTE, metadata.getRpcExt());
serverSpan.setAttribute(META_ENABLED_ATTRIBUTE, metadata.getEnabled());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.apacheshenyu.v2_4;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;

@SpringBootApplication(exclude = {GsonAutoConfiguration.class})
public class ShenYuBootstrapApplication {}
Loading

0 comments on commit 9e48f24

Please sign in to comment.