Support for tracing webflux-6.x and gateway-4.x (#661)

This commit is contained in:
Chen Ziyan 2023-12-21 16:04:21 +08:00 committed by GitHub
parent c5d62cb97a
commit 7aec68263b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 2766 additions and 6 deletions

View File

@ -57,6 +57,7 @@ jobs:
case:
- spring-6.x-scenario
- resteasy-6.x-scenario
- gateway-4.x-scenario
steps:
- uses: actions/checkout@v2
with:

View File

@ -7,6 +7,8 @@ Release Notes.
* Fix NoSuchMethodError in mvc-annotation-commons and change deprecated method.
* fix forkjoinpool plugin in JDK11。
* Support for tracing spring-cloud-gateway 4.x in gateway-4.x-plugin.
#### Documentation

View File

@ -20,13 +20,16 @@ package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v3x.define;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
/**
* This abstract class defines the <code>witnessClasses()</code> method,
* and other plugin define classes need to inherit from this class
* This abstract class defines the <code>witnessClasses()</code> method, and other plugin define classes need to inherit
* from this class
*/
public abstract class AbstractGatewayV3EnhancePluginDefine extends ClassInstanceMethodsEnhancePluginDefine {
@Override
protected String[] witnessClasses() {
return new String[]{"org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties"};
return new String[] {
"org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties",
"org.springframework.web.client.AsyncRestTemplate"
};
}
}

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>optional-spring-cloud</artifactId>
<groupId>org.apache.skywalking</groupId>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apm-spring-cloud-gateway-4.x-plugin</artifactId>
<packaging>jar</packaging>
<url>http://maven.apache.org</url>
<properties>
<spring-cloud-starter-gateway.version>4.0.0</spring-cloud-starter-gateway.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>${spring-cloud-starter-gateway.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-spring-webflux-6.x-plugin</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>org.apache.skywalking:apm-spring-webflux-6.x-plugin</artifact>
<excludes>
<exclude>skywalking-plugin.def</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,43 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache;
import reactor.netty.http.client.HttpClientConfig;
/**
* Intercept the constructor and inject {@link EnhanceObjectCache}.
* <p>
* The first constructor argument is {@link reactor.netty.http.client.HttpClientConfig} class instance which can get the
* request uri string.
*/
public class HttpClientFinalizerConstructorInterceptor implements InstanceConstructorInterceptor {
@Override
public void onConstruct(EnhancedInstance objInst, Object[] allArguments) {
final HttpClientConfig httpClientConfig = (HttpClientConfig) allArguments[0];
if (httpClientConfig == null) {
return;
}
final EnhanceObjectCache enhanceObjectCache = new EnhanceObjectCache();
enhanceObjectCache.setUrl(httpClientConfig.uri());
objInst.setSkyWalkingDynamicField(enhanceObjectCache);
}
}

View File

@ -0,0 +1,107 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.apache.skywalking.apm.agent.core.context.tag.Tags;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.SignalType;
import reactor.netty.Connection;
import reactor.netty.http.client.HttpClientResponse;
import java.lang.reflect.Method;
import java.util.function.BiFunction;
/**
* This class intercept <code>responseConnection</code> method.
* <p>
* After downstream service response, finish the span in the {@link EnhanceObjectCache}.
*/
public class HttpClientFinalizerResponseConnectionInterceptor implements InstanceMethodsAroundInterceptor {
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) {
BiFunction<? super HttpClientResponse, ? super Connection, ? extends Publisher> finalReceiver = (BiFunction<? super HttpClientResponse, ? super Connection, ? extends Publisher>) allArguments[0];
EnhanceObjectCache cache = (EnhanceObjectCache) objInst.getSkyWalkingDynamicField();
allArguments[0] = (BiFunction<HttpClientResponse, Connection, Publisher>) (response, connection) -> {
Publisher publisher = finalReceiver.apply(response, connection);
if (cache == null) {
return publisher;
}
// receive the response.
if (cache.getSpan() != null) {
if (response.status().code() >= HttpResponseStatus.BAD_REQUEST.code()) {
cache.getSpan().errorOccurred();
}
Tags.HTTP_RESPONSE_STATUS_CODE.set(cache.getSpan(), response.status().code());
}
return publisher;
};
}
@Override
public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
Object ret) {
Flux<?> responseFlux = (Flux<?>) ret;
responseFlux = responseFlux
.doOnError(e -> {
EnhanceObjectCache cache = (EnhanceObjectCache) objInst.getSkyWalkingDynamicField();
if (cache == null) {
return;
}
if (cache.getSpan() != null) {
cache.getSpan().errorOccurred();
cache.getSpan().log(e);
}
})
.doFinally(signalType -> {
EnhanceObjectCache cache = (EnhanceObjectCache) objInst.getSkyWalkingDynamicField();
if (cache == null) {
return;
}
// do finally. Finish the span.
if (cache.getSpan() != null) {
if (signalType == SignalType.CANCEL) {
cache.getSpan().errorOccurred();
}
cache.getSpan().asyncFinish();
}
if (cache.getSpan1() != null) {
cache.getSpan1().asyncFinish();
}
});
return responseFlux;
}
@Override
public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
Class<?>[] argumentsTypes, Throwable t) {
}
}

View File

@ -0,0 +1,115 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x;
import org.apache.skywalking.apm.agent.core.context.CarrierItem;
import org.apache.skywalking.apm.agent.core.context.ContextCarrier;
import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.context.tag.Tags;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache;
import org.apache.skywalking.apm.util.StringUtil;
import org.reactivestreams.Publisher;
import reactor.netty.NettyOutbound;
import reactor.netty.http.client.HttpClientRequest;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.function.BiFunction;
import static org.apache.skywalking.apm.network.trace.component.ComponentsDefine.SPRING_CLOUD_GATEWAY;
/**
* This class intercept <code>send</code> method.
* <p>
* In before method, create a new BiFunction lambda expression for setting <code>ContextCarrier</code> to http header
* and replace the original lambda in argument
*/
public class HttpClientFinalizerSendInterceptor implements InstanceMethodsAroundInterceptor {
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) throws Throwable {
EnhanceObjectCache enhanceObjectCache = (EnhanceObjectCache) objInst.getSkyWalkingDynamicField();
if (enhanceObjectCache == null) {
return;
}
/*
In this plug-in, the HttpClientFinalizerSendInterceptor depends on the NettyRoutingFilterInterceptor
When the NettyRoutingFilterInterceptor is not executed, the HttpClientFinalizerSendInterceptor has no meaning to be executed independently
and using ContextManager.activeSpan() method would cause NPE as active span does not exist.
*/
if (!ContextManager.isActive()) {
return;
}
AbstractSpan span = ContextManager.activeSpan();
span.prepareForAsync();
if (StringUtil.isNotEmpty(enhanceObjectCache.getUrl())) {
URL url = new URL(enhanceObjectCache.getUrl());
ContextCarrier contextCarrier = new ContextCarrier();
AbstractSpan abstractSpan = ContextManager.createExitSpan(
"SpringCloudGateway/sendRequest", contextCarrier, getPeer(url));
Tags.URL.set(abstractSpan, enhanceObjectCache.getUrl());
abstractSpan.prepareForAsync();
abstractSpan.setComponent(SPRING_CLOUD_GATEWAY);
abstractSpan.setLayer(SpanLayer.HTTP);
ContextManager.stopSpan(abstractSpan);
BiFunction<? super HttpClientRequest, ? super NettyOutbound, ? extends Publisher<Void>> finalSender = (BiFunction<? super HttpClientRequest, ? super NettyOutbound, ? extends Publisher<Void>>) allArguments[0];
allArguments[0] = (BiFunction<HttpClientRequest, NettyOutbound, Publisher<Void>>) (request, outbound) -> {
Publisher publisher = finalSender.apply(request, outbound);
CarrierItem next = contextCarrier.items();
while (next.hasNext()) {
next = next.next();
request.requestHeaders().remove(next.getHeadKey());
request.requestHeaders().set(next.getHeadKey(), next.getHeadValue());
}
return publisher;
};
enhanceObjectCache.setCacheSpan(abstractSpan);
}
ContextManager.stopSpan(span);
enhanceObjectCache.setSpan1(span);
}
private String getPeer(URL url) {
return url.getHost() + ":" + url.getPort();
}
@Override
public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
Object ret) {
((EnhancedInstance) ret).setSkyWalkingDynamicField(objInst.getSkyWalkingDynamicField());
return ret;
}
@Override
public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
Class<?>[] argumentsTypes, Throwable t) {
}
}

View File

@ -0,0 +1,52 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache;
import java.lang.reflect.Method;
/**
* This class intercept <code>uri</code> method to get the url of downstream service
*/
public class HttpClientFinalizerUriInterceptor implements InstanceMethodsAroundInterceptor {
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) throws Throwable {
}
@Override
public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
Object ret) throws Throwable {
if (ret instanceof EnhancedInstance) {
EnhanceObjectCache enhanceObjectCache = (EnhanceObjectCache) ((EnhancedInstance) ret)
.getSkyWalkingDynamicField();
enhanceObjectCache.setUrl(String.valueOf(allArguments[0]));
}
return ret;
}
@Override
public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
Class<?>[] argumentsTypes, Throwable t) {
}
}

View File

@ -0,0 +1,92 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x;
import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.context.ContextSnapshot;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebExchangeDecorator;
import java.lang.reflect.Method;
import static org.apache.skywalking.apm.network.trace.component.ComponentsDefine.SPRING_CLOUD_GATEWAY;
/**
* This class intercept <code>filter</code> method.
* <p>
* <code>spring-webflux-5.x-plugin</code> will inject context snapshot into skywalking dynamic field, and this
* interceptor will continue the span in another thread.
* </p>
*/
public class NettyRoutingFilterInterceptor implements InstanceMethodsAroundInterceptor {
private static final String NETTY_ROUTING_FILTER_TRACED_ATTR = NettyRoutingFilterInterceptor.class.getName() + ".isTraced";
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) throws Throwable {
ServerWebExchange exchange = (ServerWebExchange) allArguments[0];
if (isTraced(exchange)) {
return;
}
setTracedStatus(exchange);
EnhancedInstance enhancedInstance = getInstance(allArguments[0]);
AbstractSpan span = ContextManager.createLocalSpan("SpringCloudGateway/RoutingFilter");
if (enhancedInstance != null && enhancedInstance.getSkyWalkingDynamicField() != null) {
ContextManager.continued((ContextSnapshot) enhancedInstance.getSkyWalkingDynamicField());
}
span.setComponent(SPRING_CLOUD_GATEWAY);
}
private static void setTracedStatus(ServerWebExchange exchange) {
exchange.getAttributes().put(NETTY_ROUTING_FILTER_TRACED_ATTR, true);
}
private static boolean isTraced(ServerWebExchange exchange) {
return exchange.getAttributeOrDefault(NETTY_ROUTING_FILTER_TRACED_ATTR, false);
}
private EnhancedInstance getInstance(Object o) {
EnhancedInstance instance = null;
if (o instanceof EnhancedInstance) {
instance = (EnhancedInstance) o;
} else if (o instanceof ServerWebExchangeDecorator) {
ServerWebExchange delegate = ((ServerWebExchangeDecorator) o).getDelegate();
return getInstance(delegate);
}
return instance;
}
@Override
public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
Object ret) throws Throwable {
return ret;
}
@Override
public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
Class<?>[] argumentsTypes, Throwable t) {
ContextManager.activeSpan().log(t);
}
}

View File

@ -0,0 +1,35 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
/**
* This abstract class defines the <code>witnessClasses()</code> method,
* and other plugin define classes need to inherit from this class
*/
public abstract class AbstractGatewayV4EnhancePluginDefine extends ClassInstanceMethodsEnhancePluginDefine {
/**
* @since 4.0.0
*/
@Override
protected String[] witnessClasses() {
return new String[]{"org.springframework.cloud.gateway.filter.factory.cache.LocalResponseCacheProperties"};
}
}

View File

@ -0,0 +1,35 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define;
/**
* This class is used for enable {@link org.apache.skywalking.apm.plugin.spring.webflux.v6.define.DispatcherHandlerInstrumentation}
* class when {@link org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties} class is exist
*/
public class DispatcherHandlerInstrumentation extends
org.apache.skywalking.apm.plugin.spring.webflux.v6.define.DispatcherHandlerInstrumentation {
/**
* @since 4.0.0
*/
@Override
protected String[] witnessClasses() {
return new String[]{"org.springframework.cloud.gateway.filter.factory.cache.LocalResponseCacheProperties"};
}
}

View File

@ -0,0 +1,54 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
/**
* enhance object cache
*/
public class EnhanceObjectCache {
private String url;
private AbstractSpan span;
private AbstractSpan span1;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public void setCacheSpan(AbstractSpan span) {
this.span = span;
}
public AbstractSpan getSpan() {
return span;
}
public AbstractSpan getSpan1() {
return span1;
}
public void setSpan1(final AbstractSpan span) {
span1 = span;
}
}

View File

@ -0,0 +1,121 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType;
import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName;
/**
* This class instrument <code>reactor.netty.http.client.HttpClientFinalizer</code> class.
* <p>
* This is the class that actually sends the request. By enhancing in different methods,
* we can get information such as uri and send trace context to the downstream service through http header.
* </p>
*/
public class HttpClientFinalizerInstrumentation extends AbstractGatewayV4EnhancePluginDefine {
private static final String INTERCEPT_CLASS_HTTP_CLIENT_FINALIZER = "reactor.netty.http.client.HttpClientFinalizer";
private static final String CLIENT_FINALIZER_CONSTRUCTOR_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.HttpClientFinalizerConstructorInterceptor";
private static final String CLIENT_FINALIZER_CONSTRUCTOR_ARGUMENT_TYPE = "reactor.netty.http.client.HttpClientConfig";
private static final String HTTP_CLIENT_FINALIZER_URI_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.HttpClientFinalizerUriInterceptor";
private static final String HTTP_CLIENT_FINALIZER_SEND_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.HttpClientFinalizerSendInterceptor";
private static final String HTTP_CLIENT_FINALIZER_RESPONSE_CONNECTION_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.HttpClientFinalizerResponseConnectionInterceptor";
@Override
protected ClassMatch enhanceClass() {
return byName(INTERCEPT_CLASS_HTTP_CLIENT_FINALIZER);
}
@Override
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
return new ConstructorInterceptPoint[]{
new ConstructorInterceptPoint() {
@Override
public ElementMatcher<MethodDescription> getConstructorMatcher() {
return takesArgumentWithType(0, CLIENT_FINALIZER_CONSTRUCTOR_ARGUMENT_TYPE);
}
@Override
public String getConstructorInterceptor() {
return CLIENT_FINALIZER_CONSTRUCTOR_INTERCEPTOR;
}
}
};
}
@Override
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
return new InstanceMethodsInterceptPoint[]{
new InstanceMethodsInterceptPoint() {
@Override
public ElementMatcher<MethodDescription> getMethodsMatcher() {
return named("uri");
}
@Override
public String getMethodsInterceptor() {
return HTTP_CLIENT_FINALIZER_URI_INTERCEPTOR;
}
@Override
public boolean isOverrideArgs() {
return false;
}
},
new InstanceMethodsInterceptPoint() {
@Override
public ElementMatcher<MethodDescription> getMethodsMatcher() {
return named("send").and(takesArgumentWithType(0, "java.util.function.BiFunction"));
}
@Override
public String getMethodsInterceptor() {
return HTTP_CLIENT_FINALIZER_SEND_INTERCEPTOR;
}
@Override
public boolean isOverrideArgs() {
return true;
}
},
new InstanceMethodsInterceptPoint() {
@Override
public ElementMatcher<MethodDescription> getMethodsMatcher() {
return named("responseConnection");
}
@Override
public String getMethodsInterceptor() {
return HTTP_CLIENT_FINALIZER_RESPONSE_CONNECTION_INTERCEPTOR;
}
@Override
public boolean isOverrideArgs() {
return true;
}
}
};
}
}

View File

@ -0,0 +1,75 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType;
import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName;
/**
* This class instrument {@link org.springframework.cloud.gateway.filter.NettyRoutingFilter} class.
* <p>
* Enhance <code>filter</code> method and get <code>ContextSnapshot</code> from {@link
* org.springframework.web.server.ServerWebExchange}. Create a local span to continue context snapshot created in {@link
* org.apache.skywalking.apm.plugin.spring.webflux.v6.DispatcherHandlerHandleMethodInterceptor} interceptor.
* </p>
*/
public class NettyRoutingFilterInstrumentation extends AbstractGatewayV4EnhancePluginDefine {
private static final String INTERCEPT_CLASS_NETTY_ROUTING_FILTER = "org.springframework.cloud.gateway.filter.NettyRoutingFilter";
private static final String NETTY_ROUTING_FILTER_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.NettyRoutingFilterInterceptor";
@Override
protected ClassMatch enhanceClass() {
return byName(INTERCEPT_CLASS_NETTY_ROUTING_FILTER);
}
@Override
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
return new ConstructorInterceptPoint[0];
}
@Override
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
return new InstanceMethodsInterceptPoint[]{
new InstanceMethodsInterceptPoint() {
@Override
public ElementMatcher<MethodDescription> getMethodsMatcher() {
return named("filter").and(
takesArgumentWithType(0, "org.springframework.web.server.ServerWebExchange"));
}
@Override
public String getMethodsInterceptor() {
return NETTY_ROUTING_FILTER_INTERCEPTOR;
}
@Override
public boolean isOverrideArgs() {
return true;
}
}
};
}
}

View File

@ -0,0 +1,35 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define;
/**
* This class is used for enable {@link org.apache.skywalking.apm.plugin.spring.webflux.v6.define.ServerWebExchangeInstrumentation}
* when {@link org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties} class is exist.
*/
public class ServerWebExchangeInstrumentation extends
org.apache.skywalking.apm.plugin.spring.webflux.v6.define.ServerWebExchangeInstrumentation {
/**
* @since 4.0.0
*/
@Override
protected String[] witnessClasses() {
return new String[]{"org.springframework.cloud.gateway.filter.factory.cache.LocalResponseCacheProperties"};
}
}

View File

@ -0,0 +1,20 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
spring-cloud-gateway-4.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.HttpClientFinalizerInstrumentation
spring-cloud-gateway-4.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.NettyRoutingFilterInstrumentation
spring-cloud-gateway-4.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.ServerWebExchangeInstrumentation
spring-cloud-gateway-4.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.DispatcherHandlerInstrumentation

View File

@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import reactor.netty.http.client.HttpClientConfig;
@RunWith(MockitoJUnitRunner.class)
public class HttpClientFinalizerConstructorInterceptorTest {
private static final String URI = "http://localhost:8080/get";
private final EnhancedInstance enhancedInstance = new EnhancedInstance() {
private EnhanceObjectCache enhanceObjectCache;
@Override
public Object getSkyWalkingDynamicField() {
return enhanceObjectCache;
}
@Override
public void setSkyWalkingDynamicField(Object value) {
this.enhanceObjectCache = (EnhanceObjectCache) value;
}
};
private HttpClientConfig httpClientConfig;
private HttpClientFinalizerConstructorInterceptor httpClientFinalizerConstructorInterceptor;
@Before
public void setUp() {
httpClientConfig = mock(HttpClientConfig.class);
when(httpClientConfig.uri()).thenReturn(URI);
httpClientFinalizerConstructorInterceptor = new HttpClientFinalizerConstructorInterceptor();
}
@Test
public void onConstruct() {
httpClientFinalizerConstructorInterceptor.onConstruct(enhancedInstance, new Object[]{httpClientConfig});
final EnhanceObjectCache enhanceCache = (EnhanceObjectCache) enhancedInstance.getSkyWalkingDynamicField();
assertNotNull(enhanceCache);
assertEquals(enhanceCache.getUrl(), URI);
}
}

View File

@ -0,0 +1,176 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.List;
import java.util.function.BiFunction;
import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan;
import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.agent.test.helper.SegmentHelper;
import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule;
import org.apache.skywalking.apm.agent.test.tools.SegmentStorage;
import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint;
import org.apache.skywalking.apm.agent.test.tools.SpanAssert;
import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner;
import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.reactivestreams.Publisher;
import io.netty.handler.codec.http.HttpResponseStatus;
import reactor.core.publisher.Flux;
import reactor.netty.Connection;
import reactor.netty.NettyOutbound;
import reactor.netty.http.client.HttpClientRequest;
import reactor.netty.http.client.HttpClientResponse;
@RunWith(TracingSegmentRunner.class)
public class HttpClientFinalizerInterceptorTest {
private final static String URI = "http://localhost:8080/get";
private final static String ENTRY_OPERATION_NAME = "/get";
private final HttpClientFinalizerSendInterceptor sendInterceptor = new HttpClientFinalizerSendInterceptor();
private final HttpClientFinalizerResponseConnectionInterceptor responseConnectionInterceptor = new HttpClientFinalizerResponseConnectionInterceptor();
private final BiFunction<? super HttpClientRequest, ? super NettyOutbound, ? extends Publisher<Void>> originalSendBiFunction = (httpClientRequest, nettyOutbound) -> (Publisher<Void>) s -> {
};
private final BiFunction<? super HttpClientResponse, ? super Connection, ? extends Publisher<Void>> originalResponseConnectionBiFunction = (httpClientResponse, connection) -> (Publisher<Void>) s -> {
};
private final EnhancedInstance enhancedInstance = new EnhancedInstance() {
private EnhanceObjectCache enhanceObjectCache;
@Override
public Object getSkyWalkingDynamicField() {
return enhanceObjectCache;
}
@Override
public void setSkyWalkingDynamicField(Object value) {
this.enhanceObjectCache = (EnhanceObjectCache) value;
}
};
@Rule
public AgentServiceRule serviceRule = new AgentServiceRule();
@Rule
public MockitoRule rule = MockitoJUnit.rule();
private HttpClientResponse mockResponse;
private HttpClientRequest mockRequest;
@SegmentStoragePoint
private SegmentStorage segmentStorage;
private AbstractSpan entrySpan;
@Before
public void setUp() throws Exception {
final EnhanceObjectCache enhanceObjectCache = new EnhanceObjectCache();
enhanceObjectCache.setUrl(URI);
enhancedInstance.setSkyWalkingDynamicField(enhanceObjectCache);
entrySpan = ContextManager.createEntrySpan(ENTRY_OPERATION_NAME, null);
entrySpan.setLayer(SpanLayer.HTTP);
entrySpan.setComponent(ComponentsDefine.SPRING_WEBFLUX);
mockRequest = new MockCliengRequest();
mockResponse = new MockClientResponse();
}
@Test
public void testWithDynamicFieldNull() throws Throwable {
enhancedInstance.setSkyWalkingDynamicField(null);
executeSendRequest();
final List<TraceSegment> traceSegments = segmentStorage.getTraceSegments();
assertEquals(traceSegments.size(), 0);
if (ContextManager.isActive()) {
ContextManager.stopSpan();
}
}
@Test
public void testWithEmptyUri() throws Throwable {
final EnhanceObjectCache objectCache = (EnhanceObjectCache) enhancedInstance.getSkyWalkingDynamicField();
objectCache.setUrl("");
executeSendRequest();
final List<TraceSegment> traceSegments = segmentStorage.getTraceSegments();
assertEquals(traceSegments.size(), 1);
final List<AbstractTracingSpan> spans = SegmentHelper.getSpans(traceSegments.get(0));
assertNotNull(spans);
assertEquals(spans.size(), 1);
assertUpstreamSpan(spans.get(0));
assertNotNull(objectCache.getSpan1());
assertEquals(objectCache.getSpan1().getSpanId(), entrySpan.getSpanId());
}
@Test
public void testWithUri() throws Throwable {
executeSendRequest();
final List<TraceSegment> traceSegments = segmentStorage.getTraceSegments();
assertEquals(traceSegments.size(), 1);
final EnhanceObjectCache objectCache = (EnhanceObjectCache) enhancedInstance
.getSkyWalkingDynamicField();
assertNotNull(objectCache.getSpan1());
assertNotNull(objectCache.getSpan());
assertEquals(objectCache.getSpan1().getSpanId(), entrySpan.getSpanId());
assertTrue(objectCache.getSpan().isExit());
assertEquals(objectCache.getSpan().getOperationName(), "SpringCloudGateway/sendRequest");
final List<AbstractTracingSpan> spans = SegmentHelper.getSpans(traceSegments.get(0));
assertNotNull(spans);
assertEquals(spans.size(), 2);
assertUpstreamSpan(spans.get(1));
assertDownstreamSpan(spans.get(0));
}
private void executeSendRequest() throws Throwable {
Object[] sendArguments = new Object[]{originalSendBiFunction};
sendInterceptor.beforeMethod(enhancedInstance, null, sendArguments, null, null);
sendInterceptor.afterMethod(enhancedInstance, null, new Object[0], null, enhancedInstance);
((BiFunction<? super HttpClientRequest, ? super NettyOutbound, ? extends Publisher<Void>>) sendArguments[0])
.apply(mockRequest, null);
Object[] responseConnectionArguments = new Object[]{originalResponseConnectionBiFunction};
responseConnectionInterceptor
.beforeMethod(enhancedInstance, null, responseConnectionArguments, null, null);
Flux flux = Flux.just(0);
flux = (Flux) responseConnectionInterceptor.afterMethod(enhancedInstance, null, new Object[0], null, flux);
((BiFunction<? super HttpClientResponse, ? super Connection, ? extends Publisher<Void>>) responseConnectionArguments[0])
.apply(mockResponse, null);
flux.blockFirst();
}
private void assertUpstreamSpan(AbstractSpan span) {
SpanAssert.assertLayer(span, SpanLayer.HTTP);
SpanAssert.assertComponent(span, ComponentsDefine.SPRING_WEBFLUX);
}
private void assertDownstreamSpan(AbstractSpan span) {
SpanAssert.assertLayer(span, SpanLayer.HTTP);
SpanAssert.assertComponent(span, ComponentsDefine.SPRING_CLOUD_GATEWAY);
SpanAssert.assertTagSize(span, 2);
SpanAssert.assertTag(span, 0, URI);
SpanAssert.assertTag(span, 1, String.valueOf(HttpResponseStatus.OK.code()));
}
}

View File

@ -0,0 +1,135 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.cookie.Cookie;
import reactor.netty.http.client.HttpClientRequest;
import reactor.util.context.Context;
import reactor.util.context.ContextView;
import java.time.Duration;
import java.util.Map;
import java.util.Set;
public class MockCliengRequest implements HttpClientRequest {
@Override
public HttpClientRequest addCookie(Cookie cookie) {
return null;
}
@Override
public HttpClientRequest addHeader(CharSequence charSequence, CharSequence charSequence1) {
return null;
}
@Override
public HttpClientRequest header(CharSequence charSequence, CharSequence charSequence1) {
return null;
}
@Override
public HttpClientRequest headers(HttpHeaders httpHeaders) {
return null;
}
@Override
public boolean isFollowRedirect() {
return false;
}
@Override
public HttpClientRequest responseTimeout(Duration duration) {
return null;
}
@Override
public Context currentContext() {
return null;
}
@Override
public ContextView currentContextView() {
return null;
}
@Override
public String[] redirectedFrom() {
return new String[0];
}
@Override
public HttpHeaders requestHeaders() {
return new DefaultHttpHeaders();
}
@Override
public String resourceUrl() {
return null;
}
@Override
public Map<CharSequence, Set<Cookie>> cookies() {
return null;
}
@Override
public boolean isKeepAlive() {
return false;
}
@Override
public boolean isWebsocket() {
return false;
}
@Override
public HttpMethod method() {
return null;
}
@Override
public String path() {
return HttpClientRequest.super.path();
}
@Override
public String fullPath() {
return null;
}
@Override
public String requestId() {
return null;
}
@Override
public String uri() {
return null;
}
@Override
public HttpVersion version() {
return null;
}
}

View File

@ -0,0 +1,120 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.cookie.Cookie;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClientResponse;
import reactor.util.context.Context;
import reactor.util.context.ContextView;
import java.util.Map;
import java.util.Set;
public class MockClientResponse implements HttpClientResponse {
@Override
public HttpHeaders responseHeaders() {
return null;
}
@Override
public HttpResponseStatus status() {
return HttpResponseStatus.OK;
}
@Override
public Mono<HttpHeaders> trailerHeaders() {
return null;
}
@Override
public Context currentContext() {
return null;
}
@Override
public ContextView currentContextView() {
return null;
}
@Override
public String[] redirectedFrom() {
return new String[0];
}
@Override
public HttpHeaders requestHeaders() {
return null;
}
@Override
public String resourceUrl() {
return null;
}
@Override
public Map<CharSequence, Set<Cookie>> cookies() {
return null;
}
@Override
public boolean isKeepAlive() {
return false;
}
@Override
public boolean isWebsocket() {
return false;
}
@Override
public HttpMethod method() {
return null;
}
@Override
public String path() {
return HttpClientResponse.super.path();
}
@Override
public String fullPath() {
return null;
}
@Override
public String requestId() {
return null;
}
@Override
public String uri() {
return null;
}
@Override
public HttpVersion version() {
return null;
}
}

View File

@ -0,0 +1,218 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x;
import java.security.Principal;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.context.ContextSnapshot;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan;
import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.agent.test.helper.SegmentHelper;
import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule;
import org.apache.skywalking.apm.agent.test.tools.SegmentStorage;
import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint;
import org.apache.skywalking.apm.agent.test.tools.SpanAssert;
import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner;
import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.springframework.context.ApplicationContext;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.http.codec.multipart.Part;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebSession;
import reactor.core.publisher.Mono;
@RunWith(TracingSegmentRunner.class)
public class NettyRoutingFilterInterceptorTest {
private static class ServerWebExchangeEnhancedInstance implements ServerWebExchange, EnhancedInstance {
private ContextSnapshot snapshot;
Map<String, Object> attributes = new HashMap<>();
@Override
public Object getSkyWalkingDynamicField() {
return snapshot;
}
@Override
public void setSkyWalkingDynamicField(Object value) {
this.snapshot = (ContextSnapshot) value;
}
@Override
public ServerHttpRequest getRequest() {
return null;
}
@Override
public ServerHttpResponse getResponse() {
return null;
}
@Override
public Map<String, Object> getAttributes() {
return attributes;
}
@Override
public Mono<WebSession> getSession() {
return null;
}
@Override
public <T extends Principal> Mono<T> getPrincipal() {
return null;
}
@Override
public Mono<MultiValueMap<String, String>> getFormData() {
return null;
}
@Override
public Mono<MultiValueMap<String, Part>> getMultipartData() {
return null;
}
@Override
public LocaleContext getLocaleContext() {
return null;
}
@Override
public ApplicationContext getApplicationContext() {
return null;
}
@Override
public boolean isNotModified() {
return false;
}
@Override
public boolean checkNotModified(Instant instant) {
return false;
}
@Override
public boolean checkNotModified(String s) {
return false;
}
@Override
public boolean checkNotModified(String s, Instant instant) {
return false;
}
@Override
public String transformUrl(String s) {
return null;
}
@Override
public void addUrlTransformer(Function<String, String> function) {
}
@Override
public String getLogPrefix() {
return null;
}
}
private final ServerWebExchangeEnhancedInstance enhancedInstance = new ServerWebExchangeEnhancedInstance();
private final NettyRoutingFilterInterceptor interceptor = new NettyRoutingFilterInterceptor();
@Rule
public AgentServiceRule serviceRule = new AgentServiceRule();
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@SegmentStoragePoint
private SegmentStorage segmentStorage;
@Before
public void setUp() throws Exception {
}
@Test
public void testWithNullDynamicField() throws Throwable {
interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null);
interceptor.afterMethod(null, null, null, null, null);
ContextManager.stopSpan();
final List<TraceSegment> traceSegments = segmentStorage.getTraceSegments();
Assert.assertEquals(traceSegments.size(), 1);
final List<AbstractTracingSpan> spans = SegmentHelper.getSpans(traceSegments.get(0));
Assert.assertNotNull(spans);
Assert.assertEquals(spans.size(), 1);
SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY);
}
@Test
public void testWithContextSnapshot() throws Throwable {
final AbstractSpan entrySpan = ContextManager.createEntrySpan("/get", null);
SpanLayer.asHttp(entrySpan);
entrySpan.setComponent(ComponentsDefine.SPRING_WEBFLUX);
enhancedInstance.setSkyWalkingDynamicField(ContextManager.capture());
interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null);
interceptor.afterMethod(null, null, null, null, null);
ContextManager.stopSpan();
ContextManager.stopSpan(entrySpan);
final List<TraceSegment> traceSegments = segmentStorage.getTraceSegments();
Assert.assertEquals(traceSegments.size(), 1);
final List<AbstractTracingSpan> spans = SegmentHelper.getSpans(traceSegments.get(0));
Assert.assertNotNull(spans);
Assert.assertEquals(spans.size(), 2);
SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY);
SpanAssert.assertComponent(spans.get(1), ComponentsDefine.SPRING_WEBFLUX);
SpanAssert.assertLayer(spans.get(1), SpanLayer.HTTP);
}
private static final String NETTY_ROUTING_FILTER_TRACED_ATTR =
NettyRoutingFilterInterceptor.class.getName() + ".isTraced";
@Test
public void testIsTraced() throws Throwable {
interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null);
interceptor.afterMethod(null, null, null, null, null);
Assert.assertEquals(enhancedInstance.getAttributes().get(NETTY_ROUTING_FILTER_TRACED_ATTR), true);
Assert.assertNotNull(ContextManager.activeSpan());
ContextManager.stopSpan();
interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null);
interceptor.afterMethod(null, null, null, null, null);
Assert.assertEquals(enhancedInstance.getAttributes().get(NETTY_ROUTING_FILTER_TRACED_ATTR), true);
}
}

View File

@ -31,6 +31,7 @@
<module>gateway-2.0.x-plugin</module>
<module>gateway-2.1.x-plugin</module>
<module>gateway-3.x-plugin</module>
<module>gateway-4.x-plugin</module>
</modules>
<packaging>pom</packaging>

View File

@ -35,6 +35,7 @@
<module>spring-webflux-5.x-plugin</module>
<module>resttemplate-6.x-plugin</module>
<module>mvc-annotation-6.x-plugin</module>
<module>spring-webflux-6.x-plugin</module>
</modules>
<properties>

View File

@ -18,8 +18,11 @@
package org.apache.skywalking.apm.plugin.spring.webflux.v5.define;
import java.util.Collections;
import java.util.List;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
@ -29,6 +32,9 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName;
public class DispatcherHandlerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
private static final String WEBFLUX_CONTEXT_WRITE_CLASS = "reactor.core.publisher.Mono";
private static final String WEBFLUX_CONTEXT_WRITE_METHOD = "subscriberContext";
@Override
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
return new ConstructorInterceptPoint[0];
@ -60,4 +66,10 @@ public class DispatcherHandlerInstrumentation extends ClassInstanceMethodsEnhanc
protected ClassMatch enhanceClass() {
return byName("org.springframework.web.reactive.DispatcherHandler");
}
@Override
protected List<WitnessMethod> witnessMethods() {
return Collections.singletonList(
new WitnessMethod(WEBFLUX_CONTEXT_WRITE_CLASS, named(WEBFLUX_CONTEXT_WRITE_METHOD)));
}
}

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>optional-spring-plugins</artifactId>
<groupId>org.apache.skywalking</groupId>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apm-spring-webflux-6.x-plugin</artifactId>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,146 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.skywalking.apm.plugin.spring.webflux.v6;
import org.apache.skywalking.apm.agent.core.context.CarrierItem;
import org.apache.skywalking.apm.agent.core.context.ContextCarrier;
import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.context.ContextSnapshot;
import org.apache.skywalking.apm.agent.core.context.tag.Tags;
import org.apache.skywalking.apm.agent.core.context.tag.Tags.HTTP;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatusCode;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebExchangeDecorator;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.util.pattern.PathPattern;
import reactor.core.publisher.Mono;
import java.lang.reflect.Method;
import java.util.List;
public class DispatcherHandlerHandleMethodInterceptor implements InstanceMethodsAroundInterceptor {
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) throws Throwable {
EnhancedInstance instance = getInstance(allArguments[0]);
ServerWebExchange exchange = (ServerWebExchange) allArguments[0];
ContextCarrier carrier = new ContextCarrier();
CarrierItem next = carrier.items();
HttpHeaders headers = exchange.getRequest().getHeaders();
while (next.hasNext()) {
next = next.next();
List<String> header = headers.get(next.getHeadKey());
if (header != null && header.size() > 0) {
next.setHeadValue(header.get(0));
}
}
AbstractSpan span = ContextManager.createEntrySpan(exchange.getRequest().getURI().getPath(), carrier);
if (instance != null && instance.getSkyWalkingDynamicField() != null) {
ContextManager.continued((ContextSnapshot) instance.getSkyWalkingDynamicField());
}
span.setComponent(ComponentsDefine.SPRING_WEBFLUX);
SpanLayer.asHttp(span);
Tags.URL.set(span, exchange.getRequest().getURI().toString());
HTTP.METHOD.set(span, exchange.getRequest().getMethod().name());
instance.setSkyWalkingDynamicField(ContextManager.capture());
span.prepareForAsync();
ContextManager.stopSpan(span);
exchange.getAttributes().put("SKYWALKING_SPAN", span);
}
private void maybeSetPattern(AbstractSpan span, ServerWebExchange exchange) {
if (span != null) {
PathPattern pathPattern = exchange.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
//check if the best matching pattern matches url in request
//to avoid operationName overwritten by fallback url
if (pathPattern != null && pathPattern.matches(exchange.getRequest().getPath().pathWithinApplication())) {
span.setOperationName(pathPattern.getPatternString());
}
}
}
@Override
public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
Object ret) throws Throwable {
ServerWebExchange exchange = (ServerWebExchange) allArguments[0];
AbstractSpan span = (AbstractSpan) exchange.getAttributes().get("SKYWALKING_SPAN");
Mono<Void> monoReturn = (Mono<Void>) ret;
// add skywalking context snapshot to reactor context.
EnhancedInstance instance = getInstance(allArguments[0]);
if (instance != null && instance.getSkyWalkingDynamicField() != null) {
monoReturn = monoReturn.contextWrite(
c -> c.put("SKYWALKING_CONTEXT_SNAPSHOT", instance.getSkyWalkingDynamicField()));
}
return monoReturn.doFinally(s -> {
if (span != null) {
maybeSetPattern(span, exchange);
try {
HttpStatusCode httpStatus = exchange.getResponse().getStatusCode();
// fix webflux-2.0.0-2.1.0 version have bug. httpStatus is null. not support
if (httpStatus != null) {
Tags.HTTP_RESPONSE_STATUS_CODE.set(span, httpStatus.value());
if (httpStatus.isError()) {
span.errorOccurred();
}
}
} finally {
span.asyncFinish();
}
}
});
}
@Override
public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
Class<?>[] argumentsTypes, Throwable t) {
}
public static EnhancedInstance getInstance(Object o) {
EnhancedInstance instance = null;
if (o instanceof DefaultServerWebExchange) {
instance = (EnhancedInstance) o;
} else if (o instanceof ServerWebExchangeDecorator) {
ServerWebExchange delegate = ((ServerWebExchangeDecorator) o).getDelegate();
return getInstance(delegate);
}
return instance;
}
}

View File

@ -0,0 +1,28 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.skywalking.apm.plugin.spring.webflux.v6;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
public class ServerWebExchangeConstructorInterceptor implements InstanceConstructorInterceptor {
@Override
public void onConstruct(EnhancedInstance objInst, Object[] allArguments) {
}
}

View File

@ -0,0 +1,81 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.skywalking.apm.plugin.spring.webflux.v6.define;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
import java.util.Collections;
import java.util.List;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName;
public class DispatcherHandlerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
private static final String WEBFLUX_CONTEXT_WRITE_CLASS = "reactor.core.publisher.Mono";
private static final String WEBFLUX_CONTEXT_WRITE_METHOD = "contextWrite";
private static final String WEBFLUX_METHOD_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.webflux.v6.DispatcherHandlerHandleMethodInterceptor";
private static final String WEBFLUX_ENHANCE_CLASS = "org.springframework.web.reactive.DispatcherHandler";
@Override
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
return new ConstructorInterceptPoint[0];
}
@Override
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
return new InstanceMethodsInterceptPoint[] {
new InstanceMethodsInterceptPoint() {
@Override
public ElementMatcher<MethodDescription> getMethodsMatcher() {
return named("handle");
}
@Override
public String getMethodsInterceptor() {
return WEBFLUX_METHOD_INTERCEPTOR;
}
@Override
public boolean isOverrideArgs() {
return false;
}
}
};
}
@Override
protected ClassMatch enhanceClass() {
return byName(WEBFLUX_ENHANCE_CLASS);
}
/**
* @since 6.0.0
*/
@Override
protected List<WitnessMethod> witnessMethods() {
return Collections.singletonList(
new WitnessMethod(WEBFLUX_CONTEXT_WRITE_CLASS, named(WEBFLUX_CONTEXT_WRITE_METHOD)));
}
}

View File

@ -0,0 +1,61 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.skywalking.apm.plugin.spring.webflux.v6.define;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
import static net.bytebuddy.matcher.ElementMatchers.any;
import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName;
public class ServerWebExchangeInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
private static final String WEBFLUX_CONSTRUCTOR_INTERCEPOR = "org.apache.skywalking.apm.plugin.spring.webflux.v6.ServerWebExchangeConstructorInterceptor";
private static final String WEBFLUX_ENHANCE_CLASS = "org.springframework.web.server.adapter.DefaultServerWebExchange";
@Override
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
return new ConstructorInterceptPoint[] {
new ConstructorInterceptPoint() {
@Override
public ElementMatcher<MethodDescription> getConstructorMatcher() {
return any();
}
@Override
public String getConstructorInterceptor() {
return WEBFLUX_CONSTRUCTOR_INTERCEPOR;
}
}
};
}
@Override
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
return new InstanceMethodsInterceptPoint[0];
}
@Override
protected ClassMatch enhanceClass() {
return byName(WEBFLUX_ENHANCE_CLASS);
}
}

View File

@ -0,0 +1,18 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
spring-webflux-6.x=org.apache.skywalking.apm.plugin.spring.webflux.v6.define.DispatcherHandlerInstrumentation
spring-webflux-6.x=org.apache.skywalking.apm.plugin.spring.webflux.v6.define.ServerWebExchangeInstrumentation

View File

@ -9,7 +9,7 @@ Now, we have the following known optional plugins.
* Plugin of Gson serialization lib in optional plugin folder.
* Plugin of Zookeeper 3.4.x in optional plugin folder. The reason of being optional plugin is, many business irrelevant traces are generated, which cause extra payload to agents and backends. At the same time, those traces may be just heartbeat(s).
* [Customize enhance](Customize-enhance-trace.md) Trace methods based on description files, rather than write plugin or change source codes.
* Plugin of Spring Cloud Gateway 2.x and 3.x in optional plugin folder. Please only activate this plugin when you install agent in Spring Gateway.
* Plugin of Spring Cloud Gateway 2.x and 3.x and 4.x in optional plugin folder. Please only activate this plugin when you install agent in Spring Gateway.
* Plugin of Spring Transaction in optional plugin folder. The reason of being optional plugin is, many local span are generated, which also spend more CPU, memory and network.
* [Plugin of Kotlin coroutine](agent-optional-plugins/Kotlin-Coroutine-plugin.md) provides the tracing across coroutines automatically. As it will add local spans to all across routines scenarios, Please assess the performance impact.
* Plugin of quartz-scheduler-2.x in the optional plugin folder. The reason for being an optional plugin is, many task scheduling systems are based on quartz-scheduler, this will cause duplicate tracing and link different sub-tasks as they share the same quartz level trigger, such as ElasticJob.

View File

@ -172,3 +172,5 @@
- jetty-server-11.x
- jetty-client-11.x
- websphere-liberty-23.x
- spring-cloud-gateway-4.x
- spring-webflux-6.x

View File

@ -15,7 +15,7 @@ metrics based on the tracing data.
* Resin 3 (Optional¹)
* Resin 4 (Optional¹)
* [Jetty Server](http://www.eclipse.org/jetty/) 9.x -> 11.x
* [Spring WebFlux](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html) 5.x (Optional¹)
* [Spring WebFlux](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html) 5.x (Optional¹) -> 6.x (Optional¹)
* [Undertow](http://undertow.io/) 1.3.0.Final -> 2.0.27.Final
* [RESTEasy](https://resteasy.github.io/) 3.1.0.Final -> 6.2.4.Final
* [Play Framework](https://www.playframework.com/) 2.6.x -> 2.8.x
@ -40,7 +40,7 @@ metrics based on the tracing data.
* [Hutool-http](https://www.hutool.cn/) client 5.x
* [Micronaut HTTP Client](https://github.com/micronaut-projects/micronaut-core) 3.2.x -> 3.6.x
* HTTP Gateway
* [Spring Cloud Gateway](https://spring.io/projects/spring-cloud-gateway) 2.0.2.RELEASE -> 3.x (Optional²)
* [Spring Cloud Gateway](https://spring.io/projects/spring-cloud-gateway) 2.0.2.RELEASE -> 4.1.x (Optional²)
* [Apache ShenYu](https://shenyu.apache.org) (Rich protocol support: `HTTP`,`Spring Cloud`,`gRPC`,`Dubbo`,`SOFARPC`,`Motan`,`Tars`) 2.4.x (Optional²)
* JDBC
* Mysql Driver 5.x, 6.x, 8.x

View File

@ -0,0 +1,155 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
segmentItems:
- serviceName: gateway-projectB-scenario
segmentSize: nq 0
segments:
- segmentId: not null
spans:
- operationName: /provider/timeout/error
parentSpanId: 0
spanId: 1
isError: true
spanType: Exit
tags:
- { key: url, value: not null }
- { key: http.method, value: GET }
- { key: http.status_code, value: '500' }
logs:
- logEvent:
- { key: event, value: error }
- { key: error.kind, value: not null }
- { key: message, value: not null }
- {key: stack, value: not null}
- logEvent:
- { key: event, value: error }
- { key: error.kind, value: not null }
- { key: message, value: not null }
- { key: stack, value: not null }
- operationName: GET:/provider/b/testcase
parentSpanId: -1
spanId: 0
spanLayer: Http
startTime: nq 0
endTime: nq 0
componentId: 14
isError: false
spanType: Entry
peer: ''
tags:
- {key: url, value: not null}
- {key: http.method, value: GET}
- {key: http.status_code, value: '200'}
refs:
- {parentEndpoint: SpringCloudGateway/RoutingFilter, networkAddress: 'localhost:18070',
refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not
null, parentService: not null, traceId: not null}
skipAnalysis: 'false'
- serviceName: gateway-projectA-scenario
segmentSize: nq 0
segments:
- segmentId: not null
spans:
- operationName: /provider/timeout/error
parentSpanId: -1
spanId: 0
spanLayer: Http
isError: true
spanType: Entry
tags:
- {key: url, value: 'http://localhost:8080/provider/timeout/error' }
- {key: http.method, value: GET}
- {key: http.status_code, value: '500'}
- segmentId: not null
spans:
- operationName: SpringCloudGateway/sendRequest
parentSpanId: 0
spanId: 1
spanLayer: Http
startTime: nq 0
endTime: nq 0
componentId: 61
isError: true
spanType: Exit
peer: 1.2.3.4:18070
skipAnalysis: false
tags:
- { key: url, value: not null }
logs:
- logEvent:
- { key: event, value: error }
- { key: error.kind, value: not null }
- { key: message, value: not null }
- { key: stack, value: not null}
- operationName: SpringCloudGateway/RoutingFilter
parentSpanId: -1
spanId: 0
startTime: nq 0
endTime: nq 0
componentId: 61
spanType: Local
refs:
- { parentEndpoint: '/provider/timeout/error', networkAddress: '', refType: CrossThread,
parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not
null, parentService: not null, traceId: not null }
skipAnalysis: 'false'
- segmentId: not null
spans:
- operationName: SpringCloudGateway/sendRequest
parentSpanId: 0
spanId: 1
spanLayer: Http
startTime: nq 0
endTime: nq 0
componentId: 61
isError: false
spanType: Exit
peer: 'localhost:18070'
tags:
- {key: url, value: not null}
- {key: http.status_code, value: '200'}
skipAnalysis: 'false'
- operationName: SpringCloudGateway/RoutingFilter
parentSpanId: -1
spanId: 0
startTime: nq 0
endTime: nq 0
componentId: 61
isError: false
spanType: Local
peer: ''
refs:
- {parentEndpoint: '/provider/b/testcase', networkAddress: '', refType: CrossThread,
parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not
null, parentService: not null, traceId: not null}
skipAnalysis: 'false'
- segmentId: not null
spans:
- operationName: /provider/b/testcase
parentSpanId: -1
spanId: 0
spanLayer: Http
startTime: nq 0
endTime: nq 0
componentId: 67
isError: false
spanType: Entry
peer: not null
tags:
- { key: url, value: 'http://localhost:8080/provider/b/testcase' }
- { key: http.method, value: GET }
- { key: http.status_code, value: '200' }
skipAnalysis: 'false'

View File

@ -0,0 +1,22 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
type: jvm
entryService: http://localhost:8080/provider/b/testcase
healthCheck: http://localhost:8080/provider/b/healthCheck
startScript: ./bin/startup.sh
runningMode: with_optional
withPlugins: apm-spring-cloud-gateway-4.x-plugin-*.jar

View File

@ -0,0 +1,24 @@
#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
home="$(cd "$(dirname $0)"; pwd)"
java -jar ${agent_opts} "-Dskywalking.agent.service_name=gateway-projectA-scenario" ${home}/../libs/gateway-projectA-scenario.jar &
sleep 1
java -jar ${agent_opts} "-Dskywalking.agent.service_name=gateway-projectB-scenario" ${home}/../libs/gateway-projectB-scenario.jar &

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.apache.skywalking</groupId>
<artifactId>gateway-4.x-scenario</artifactId>
<version>5.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gateway-dist</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>assemble</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
<outputDirectory>../target/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<directory>./bin</directory>
<fileMode>0775</fileMode>
</fileSet>
</fileSets>
<files>
<file>
<source>../gateway-projectA-scenario/target/gateway-projectA-scenario.jar</source>
<outputDirectory>./libs</outputDirectory>
<fileMode>0775</fileMode>
</file>
<file>
<source>../gateway-projectB-scenario/target/gateway-projectB-scenario.jar</source>
<outputDirectory>./libs</outputDirectory>
<fileMode>0775</fileMode>
</file>
</files>
</assembly>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.apache.skywalking</groupId>
<artifactId>gateway-4.x-scenario</artifactId>
<version>5.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gateway-projectA-scenario</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>${test.framework.version}</version>
</dependency>
</dependencies>
<build>
<finalName>gateway-projectA-scenario</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,31 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package test.apache.skywalking.apm.testcase.sc.gateway.projectA;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class ApiKeyResolver implements KeyResolver {
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getPath().value());
}
}

View File

@ -0,0 +1,29 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package test.apache.skywalking.apm.testcase.sc.gateway.projectA;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package test.apache.skywalking.apm.testcase.sc.gateway.projectA;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
public class Test1Filter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest buildRequest = exchange.getRequest().mutate().build();
return chain.filter(exchange.mutate().request(buildRequest).build());
}
@Override
public int getOrder() {
return 0;
}
}

View File

@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package test.apache.skywalking.apm.testcase.sc.gateway.projectA;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
public class Test2Filter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest buildRequest = exchange.getRequest().mutate().build();
return chain.filter(exchange.mutate().request(buildRequest).build());
}
@Override
public int getOrder() {
return 1;
}
}

View File

@ -0,0 +1,35 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package test.apache.skywalking.apm.testcase.sc.gateway.projectA;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TestFilterConfig {
@Bean
public Test1Filter test1Filter() {
return new Test1Filter();
}
@Bean
public Test2Filter test2Filter() {
return new Test2Filter();
}
}

View File

@ -0,0 +1,33 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
server:
port: 8080
spring:
cloud:
gateway:
httpclient:
connect-timeout: 2000
routes:
- id: provider_route
uri: http://localhost:18070
predicates:
- Path=/provider/b/*
- id: provider_timeout
uri: http://1.2.3.4:18070
predicates:
- Path=/provider/timeout/*

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>gateway-4.x-scenario</artifactId>
<groupId>org.apache.skywalking</groupId>
<version>5.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gateway-projectB-scenario</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
</dependencies>
<build>
<finalName>gateway-projectB-scenario</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.5.9.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,31 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package test.apache.skywalking.apm.testcase.sc.gateway.projectB;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(value = {"test.apache.skywalking.apm.testcase.sc.gateway.projectB.controller"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@ -0,0 +1,40 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package test.apache.skywalking.apm.testcase.sc.gateway.projectB.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class TestController {
@RequestMapping("/provider/b/testcase")
public String testcase() {
try {
new RestTemplate().getForEntity("http://localhost:8080/provider/timeout/error", String.class);
} catch (Exception e) {
}
return "1";
}
@RequestMapping("/provider/b/healthCheck")
public String healthCheck() {
return "Success";
}
}

View File

@ -0,0 +1,17 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
server.port=18070

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.skywalking</groupId>
<artifactId>gateway-4.x-scenario</artifactId>
<packaging>pom</packaging>
<version>5.0.0</version>
<modules>
<module>gateway-projectA-scenario</module>
<module>gateway-projectB-scenario</module>
<module>gateway-dist</module>
</modules>
<name>skywalking-gateway-4.x-scenario</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<compiler.version>1.8</compiler.version>
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
<test.framework.version>4.0.0</test.framework.version>
<docker.image.version>${test.framework.version}</docker.image.version>
</properties>
<build>
<finalName>gateway-4.x-scenario</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${compiler.version}</source>
<target>${compiler.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,19 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
4.0.0
4.0.8
4.1.0