From b732ff35a7fd0d99ef1e5f4b9a66170fa812f6cd Mon Sep 17 00:00:00 2001 From: Lu Jiajing Date: Sat, 22 May 2021 17:06:13 +0800 Subject: [PATCH] Fix ClassCast issue for RequestHolder/ResponseHolder (#6973) --- .github/workflows/plugins-test.0.yaml | 1 + CHANGES.md | 1 + .../core/context/ContextManagerBenchmark.java | 97 +++++++++ .../v3/HandlerMethodInvokerInterceptor.java | 5 +- .../spring/mvc/v4/SpringTestCaseHelper.java | 6 +- .../spring/mvc/v5/GetBeanInterceptor.java | 6 +- .../spring/mvc/v5/InvokeInterceptor.java | 62 +++--- .../AbstractSpring5Instrumentation.java | 4 +- ...bstractSpring5ReactiveInstrumentation.java | 4 +- ...tractSpring5ReactiveInstrumentationV2.java | 29 +++ ...InvocableHandlerMethodInstrumentation.java | 13 +- .../plugin/spring/mvc/commons/Constants.java | 2 + .../commons/JavaxServletRequestHolder.java | 56 ------ .../mvc/commons/ReactiveRequestHolder.java | 66 ------- .../mvc/commons/ReactiveResponseHolder.java | 45 ----- .../spring/mvc/commons/RequestUtil.java | 110 +++++++++++ .../spring/mvc/commons/ResponseHolder.java | 22 --- .../AbstractMethodInterceptor.java | 186 ++++++++++-------- .../interceptor/GetBeanInterceptor.java | 5 +- .../InvokeForRequestInterceptor.java | 5 +- .../InvokeHandlerMethodInterceptor.java | 13 +- .../bin/startup.sh | 24 +++ .../config/expectedData.yaml | 103 ++++++++++ .../configuration.yml | 20 ++ .../pom.xml | 124 ++++++++++++ .../src/main/assembly/assembly.xml | 49 +++++ .../sc/springmvcreactive/Application.java | 20 +- .../controller/Controller.java | 60 ++++++ .../service/TestService.java | 25 ++- .../src/main/resources/application.yml | 18 ++ .../support-version.list | 20 ++ .../springmvc-reactive-scenario/pom.xml | 2 +- 32 files changed, 837 insertions(+), 366 deletions(-) create mode 100644 apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/context/ContextManagerBenchmark.java create mode 100644 apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/reactive/AbstractSpring5ReactiveInstrumentationV2.java delete mode 100644 apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/JavaxServletRequestHolder.java delete mode 100644 apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ReactiveRequestHolder.java delete mode 100644 apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ReactiveResponseHolder.java create mode 100644 apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestUtil.java delete mode 100644 apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ResponseHolder.java create mode 100644 test/plugin/scenarios/springmvc-reactive-devtools-scenario/bin/startup.sh create mode 100644 test/plugin/scenarios/springmvc-reactive-devtools-scenario/config/expectedData.yaml create mode 100644 test/plugin/scenarios/springmvc-reactive-devtools-scenario/configuration.yml create mode 100644 test/plugin/scenarios/springmvc-reactive-devtools-scenario/pom.xml create mode 100644 test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/assembly/assembly.xml rename apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestHolder.java => test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/Application.java (70%) create mode 100644 test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/controller/Controller.java rename apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/JavaxServletResponseHolder.java => test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/service/TestService.java (54%) create mode 100644 test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/resources/application.yml create mode 100644 test/plugin/scenarios/springmvc-reactive-devtools-scenario/support-version.list diff --git a/.github/workflows/plugins-test.0.yaml b/.github/workflows/plugins-test.0.yaml index b6272775af..43cf87588b 100644 --- a/.github/workflows/plugins-test.0.yaml +++ b/.github/workflows/plugins-test.0.yaml @@ -69,6 +69,7 @@ jobs: - gson-scenario - elasticjob-3.x-scenario - springmvc-reactive-scenario + - springmvc-reactive-devtools-scenario steps: - uses: actions/checkout@v2 with: diff --git a/CHANGES.md b/CHANGES.md index 36d368503d..7eb1499af2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -30,6 +30,7 @@ Release Notes. * Fix the conversion problem of float type in ConfigInitializer. * Fixed part of the dynamic configuration of ConfigurationDiscoveryService that does not take effect under certain circumstances. * Introduce method interceptor API v2 +* Fix ClassCast issue for RequestHolder/ResponseHolder. #### OAP-Backend * BugFix: filter invalid Envoy access logs whose socket address is empty. diff --git a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/context/ContextManagerBenchmark.java b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/context/ContextManagerBenchmark.java new file mode 100644 index 0000000000..8810129a14 --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/context/ContextManagerBenchmark.java @@ -0,0 +1,97 @@ +/* + * 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.agent.core.context; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.profile.GCProfiler; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public class ContextManagerBenchmark { + @Benchmark + @Fork(value = 1, warmups = 1) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + @BenchmarkMode(Mode.SampleTime) + public void getKeyFromThreadLocal(Blackhole bh) { + bh.consume(ContextManager.getRuntimeContext().get("KEY")); + } + + @Benchmark + @Fork(value = 1, warmups = 1) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + @BenchmarkMode(Mode.SampleTime) + public void isAssignableFrom(Blackhole bh) { + bh.consume(Map.class.isAssignableFrom(HashMap.class)); + } + + public static void main(String[] args) throws Exception { + Options opt = new OptionsBuilder().include(ContextManagerBenchmark.class.getSimpleName()) + .addProfiler(GCProfiler.class) + .build(); + new Runner(opt).run(); + } + + /** + * # JMH version: 1.21 + * # VM version: JDK 1.8.0_292, OpenJDK 64-Bit Server VM, 25.292-b10 + * # VM invoker: /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/bin/java + * # VM options: -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=62459:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 + * # Warmup: 5 iterations, 10 s each + * # Measurement: 5 iterations, 10 s each + * # Timeout: 10 min per iteration + * # Threads: 1 thread, will synchronize iterations + * # Benchmark mode: Sampling time + * + * Benchmark Mode Cnt Score Error Units + * ContextManagerBenchmark.getKeyFromThreadLocal sample 1332415 48.066 ± 0.765 ns/op + * ContextManagerBenchmark.getKeyFromThreadLocal:getKeyFromThreadLocal·p0.00 sample 11.000 ns/op + * ContextManagerBenchmark.getKeyFromThreadLocal:getKeyFromThreadLocal·p0.50 sample 42.000 ns/op + * ContextManagerBenchmark.getKeyFromThreadLocal:getKeyFromThreadLocal·p0.90 sample 45.000 ns/op + * ContextManagerBenchmark.getKeyFromThreadLocal:getKeyFromThreadLocal·p0.95 sample 47.000 ns/op + * ContextManagerBenchmark.getKeyFromThreadLocal:getKeyFromThreadLocal·p0.99 sample 93.000 ns/op + * ContextManagerBenchmark.getKeyFromThreadLocal:getKeyFromThreadLocal·p0.999 sample 652.000 ns/op + * ContextManagerBenchmark.getKeyFromThreadLocal:getKeyFromThreadLocal·p0.9999 sample 11236.403 ns/op + * ContextManagerBenchmark.getKeyFromThreadLocal:getKeyFromThreadLocal·p1.00 sample 144384.000 ns/op + * ContextManagerBenchmark.getKeyFromThreadLocal:·gc.alloc.rate sample 5 0.022 ± 0.010 MB/sec + * ContextManagerBenchmark.getKeyFromThreadLocal:·gc.alloc.rate.norm sample 5 ≈ 10⁻⁴ B/op + * ContextManagerBenchmark.getKeyFromThreadLocal:·gc.count sample 5 ≈ 0 counts + * ContextManagerBenchmark.isAssignableFrom sample 1182629 45.894 ± 0.939 ns/op + * ContextManagerBenchmark.isAssignableFrom:isAssignableFrom·p0.00 sample 7.000 ns/op + * ContextManagerBenchmark.isAssignableFrom:isAssignableFrom·p0.50 sample 39.000 ns/op + * ContextManagerBenchmark.isAssignableFrom:isAssignableFrom·p0.90 sample 43.000 ns/op + * ContextManagerBenchmark.isAssignableFrom:isAssignableFrom·p0.95 sample 46.000 ns/op + * ContextManagerBenchmark.isAssignableFrom:isAssignableFrom·p0.99 sample 89.000 ns/op + * ContextManagerBenchmark.isAssignableFrom:isAssignableFrom·p0.999 sample 606.000 ns/op + * ContextManagerBenchmark.isAssignableFrom:isAssignableFrom·p0.9999 sample 15904.000 ns/op + * ContextManagerBenchmark.isAssignableFrom:isAssignableFrom·p1.00 sample 138240.000 ns/op + * ContextManagerBenchmark.isAssignableFrom:·gc.alloc.rate sample 5 0.021 ± 0.007 MB/sec + * ContextManagerBenchmark.isAssignableFrom:·gc.alloc.rate.norm sample 5 ≈ 10⁻⁴ B/op + * ContextManagerBenchmark.isAssignableFrom:·gc.count sample 5 ≈ 0 counts + */ +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v3/HandlerMethodInvokerInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v3/HandlerMethodInvokerInterceptor.java index dd60587145..2123338734 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v3/HandlerMethodInvokerInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v3/HandlerMethodInvokerInterceptor.java @@ -19,12 +19,10 @@ package org.apache.skywalking.apm.plugin.spring.mvc.v3; import java.lang.reflect.Method; -import javax.servlet.http.HttpServletResponse; import org.apache.skywalking.apm.agent.core.context.ContextManager; 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.mvc.commons.JavaxServletResponseHolder; import org.springframework.web.context.request.NativeWebRequest; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT; @@ -40,8 +38,7 @@ public class HandlerMethodInvokerInterceptor implements InstanceMethodsAroundInt Object handler = allArguments[1]; if (handler instanceof EnhancedInstance) { ContextManager.getRuntimeContext() - .put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, new JavaxServletResponseHolder( - (HttpServletResponse) ((NativeWebRequest) allArguments[2]).getNativeResponse())); + .put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, ((NativeWebRequest) allArguments[2]).getNativeResponse()); } } diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/mvc/v4/SpringTestCaseHelper.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/mvc/v4/SpringTestCaseHelper.java index 2447d20dda..ffcee2a6fc 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/mvc/v4/SpringTestCaseHelper.java +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/mvc/v4/SpringTestCaseHelper.java @@ -22,16 +22,14 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.skywalking.apm.agent.core.context.ContextManager; import org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants; -import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletRequestHolder; -import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletResponseHolder; public final class SpringTestCaseHelper { public final static void createCaseHandler(HttpServletRequest request, HttpServletResponse response, CaseHandler a) throws Throwable { ContextManager.createLocalSpan("For-Test"); - ContextManager.getRuntimeContext().put(Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT, new JavaxServletRequestHolder(request)); - ContextManager.getRuntimeContext().put(Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT, new JavaxServletResponseHolder(response)); + ContextManager.getRuntimeContext().put(Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT, request); + ContextManager.getRuntimeContext().put(Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT, response); a.handleCase(); ContextManager.stopSpan(); ContextManager.getRuntimeContext().remove(Constants.CONTROLLER_METHOD_STACK_DEPTH); diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/GetBeanInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/GetBeanInterceptor.java index b2014c7aba..b063656c1c 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/GetBeanInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/GetBeanInterceptor.java @@ -22,8 +22,6 @@ import org.apache.skywalking.apm.agent.core.context.ContextManager; 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.mvc.commons.JavaxServletRequestHolder; -import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletResponseHolder; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -50,12 +48,12 @@ public class GetBeanInterceptor implements InstanceMethodsAroundInterceptor { ContextManager.getRuntimeContext() .put( REQUEST_KEY_IN_RUNTIME_CONTEXT, - new JavaxServletRequestHolder(requestAttributes.getRequest()) + requestAttributes.getRequest() ); ContextManager.getRuntimeContext() .put( RESPONSE_KEY_IN_RUNTIME_CONTEXT, - new JavaxServletResponseHolder(requestAttributes.getResponse()) + requestAttributes.getResponse() ); } } diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/InvokeInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/InvokeInterceptor.java index 0b13d2dbe2..50eb1ea7a3 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/InvokeInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/InvokeInterceptor.java @@ -17,48 +17,56 @@ package org.apache.skywalking.apm.plugin.spring.mvc.v5; -import java.lang.reflect.Method; import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.RuntimeContext; 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.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.mvc.commons.ReactiveRequestHolder; -import org.apache.skywalking.apm.plugin.spring.mvc.commons.ReactiveResponseHolder; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.InstanceMethodsAroundInterceptorV2; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext; import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; +import java.lang.reflect.Method; + +import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.REACTIVE_ASYNC_SPAN_IN_RUNTIME_CONTEXT; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT; -public class InvokeInterceptor implements InstanceMethodsAroundInterceptor { +public class InvokeInterceptor implements InstanceMethodsAroundInterceptorV2 { @Override - public void beforeMethod(final EnhancedInstance objInst, - final Method method, - final Object[] allArguments, - final Class[] argumentsTypes, - final MethodInterceptResult result) throws Throwable { + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInvocationContext context) throws Throwable { ServerWebExchange exchange = (ServerWebExchange) allArguments[0]; - final ReactiveResponseHolder responseHolder = new ReactiveResponseHolder(exchange.getResponse()); - ContextManager.getRuntimeContext() - .put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, responseHolder); - ContextManager.getRuntimeContext() - .put(REQUEST_KEY_IN_RUNTIME_CONTEXT, new ReactiveRequestHolder(exchange.getRequest())); - objInst.setSkyWalkingDynamicField(responseHolder); + final ServerHttpResponse response = exchange.getResponse(); + /** + * First, we put the slot in the RuntimeContext, + * as well as the MethodInvocationContext (MIC), + * so that we can access it in the {@link org.apache.skywalking.apm.plugin.spring.mvc.v5.InvokeInterceptor#afterMethod} + * Then we fetch the slot from {@link org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor.AbstractMethodInterceptor#afterMethod} + * and fulfill the slot with the real AbstractSpan. + * Afterwards, we can safely remove the RuntimeContext. + * Finally, when the lambda executes in the {@link reactor.core.publisher.Mono#doFinally}, + * ref of span could be acquired from MIC. + */ + AbstractSpan[] ref = new AbstractSpan[1]; + RuntimeContext runtimeContext = ContextManager.getRuntimeContext(); + runtimeContext.put(REACTIVE_ASYNC_SPAN_IN_RUNTIME_CONTEXT, ref); + runtimeContext.put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, response); + runtimeContext.put(REQUEST_KEY_IN_RUNTIME_CONTEXT, exchange.getRequest()); + context.setContext(ref); } @Override - public Object afterMethod(final EnhancedInstance objInst, - final Method method, - final Object[] allArguments, - final Class[] argumentsTypes, - final Object ret) throws Throwable { + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret, MethodInvocationContext context) throws Throwable { ServerWebExchange exchange = (ServerWebExchange) allArguments[0]; return ((Mono) ret).doFinally(s -> { - ReactiveResponseHolder responseHolder = (ReactiveResponseHolder) objInst.getSkyWalkingDynamicField(); - AbstractSpan span = responseHolder.getSpan(); + Object ctx = context.getContext(); + if (ctx == null) { + return; + } + AbstractSpan span = ((AbstractSpan[]) ctx)[0]; if (span == null) { return; } @@ -72,11 +80,7 @@ public class InvokeInterceptor implements InstanceMethodsAroundInterceptor { } @Override - public void handleMethodException(final EnhancedInstance objInst, - final Method method, - final Object[] allArguments, - final Class[] argumentsTypes, - final Throwable t) { + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t, MethodInvocationContext context) { } } diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/AbstractSpring5Instrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/AbstractSpring5Instrumentation.java index 1f1ee13318..28d4de352e 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/AbstractSpring5Instrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/AbstractSpring5Instrumentation.java @@ -20,10 +20,10 @@ package org.apache.skywalking.apm.plugin.spring.mvc.v5.define; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; public abstract class AbstractSpring5Instrumentation extends ClassInstanceMethodsEnhancePluginDefine { - public static final String WITHNESS_CLASSES = "org.springframework.web.servlet.resource.HttpResource"; + public static final String WITNESS_CLASSES = "org.springframework.web.servlet.resource.HttpResource"; @Override protected final String[] witnessClasses() { - return new String[] {WITHNESS_CLASSES}; + return new String[] {WITNESS_CLASSES}; } } diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/reactive/AbstractSpring5ReactiveInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/reactive/AbstractSpring5ReactiveInstrumentation.java index 8d37b84d67..5753debefa 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/reactive/AbstractSpring5ReactiveInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/reactive/AbstractSpring5ReactiveInstrumentation.java @@ -20,10 +20,10 @@ package org.apache.skywalking.apm.plugin.spring.mvc.v5.define.reactive; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; public abstract class AbstractSpring5ReactiveInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { - public static final String WITHNESS_CLASSES = "org.springframework.web.reactive.result.method.InvocableHandlerMethod"; + public static final String WITNESS_CLASSES = "org.springframework.web.reactive.result.method.InvocableHandlerMethod"; @Override protected final String[] witnessClasses() { - return new String[] {WITHNESS_CLASSES}; + return new String[] {WITNESS_CLASSES}; } } diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/reactive/AbstractSpring5ReactiveInstrumentationV2.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/reactive/AbstractSpring5ReactiveInstrumentationV2.java new file mode 100644 index 0000000000..52adbb77f1 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/reactive/AbstractSpring5ReactiveInstrumentationV2.java @@ -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 org.apache.skywalking.apm.plugin.spring.mvc.v5.define.reactive; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.ClassInstanceMethodsEnhancePluginDefineV2; + +public abstract class AbstractSpring5ReactiveInstrumentationV2 extends ClassInstanceMethodsEnhancePluginDefineV2 { + public static final String WITNESS_CLASSES = "org.springframework.web.reactive.result.method.InvocableHandlerMethod"; + + @Override + protected final String[] witnessClasses() { + return new String[] {WITNESS_CLASSES}; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/reactive/InvocableHandlerMethodInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/reactive/InvocableHandlerMethodInstrumentation.java index 27884ed3d4..b0a8bdb8ee 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/reactive/InvocableHandlerMethodInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/reactive/InvocableHandlerMethodInstrumentation.java @@ -20,15 +20,14 @@ package org.apache.skywalking.apm.plugin.spring.mvc.v5.define.reactive; 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.interceptor.v2.InstanceMethodsInterceptV2Point; 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; -public class InvocableHandlerMethodInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { +public class InvocableHandlerMethodInstrumentation extends AbstractSpring5ReactiveInstrumentationV2 { @Override protected ClassMatch enhanceClass() { return byName("org.springframework.web.reactive.result.method.InvocableHandlerMethod"); @@ -40,9 +39,9 @@ public class InvocableHandlerMethodInstrumentation extends ClassInstanceMethodsE } @Override - public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { - return new InstanceMethodsInterceptPoint[] { - new InstanceMethodsInterceptPoint() { + public InstanceMethodsInterceptV2Point[] getInstanceMethodsInterceptV2Points() { + return new InstanceMethodsInterceptV2Point[] { + new InstanceMethodsInterceptV2Point() { @Override public ElementMatcher getMethodsMatcher() { return named("invoke").and( @@ -50,7 +49,7 @@ public class InvocableHandlerMethodInstrumentation extends ClassInstanceMethodsE } @Override - public String getMethodsInterceptor() { + public String getMethodsInterceptorV2() { return "org.apache.skywalking.apm.plugin.spring.mvc.v5.InvokeInterceptor"; } diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/Constants.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/Constants.java index 6341b067c3..85a389fae8 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/Constants.java +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/Constants.java @@ -34,6 +34,8 @@ public class Constants { public static final String RESPONSE_KEY_IN_RUNTIME_CONTEXT = "SW_RESPONSE"; + public static final String REACTIVE_ASYNC_SPAN_IN_RUNTIME_CONTEXT = "SW_REACTIVE_RESPONSE_ASYNC_SPAN"; + public static final String FORWARD_REQUEST_FLAG = "SW_FORWARD_REQUEST_FLAG"; public static final String WEBFLUX_REQUEST_KEY = "SW_WEBFLUX_REQUEST_KEY"; diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/JavaxServletRequestHolder.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/JavaxServletRequestHolder.java deleted file mode 100644 index 8eb5705ba3..0000000000 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/JavaxServletRequestHolder.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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.mvc.commons; - -import java.util.Enumeration; -import java.util.Map; -import javax.servlet.http.HttpServletRequest; - -public class JavaxServletRequestHolder implements RequestHolder { - - private final HttpServletRequest request; - - public JavaxServletRequestHolder(HttpServletRequest request) { - this.request = request; - } - - @Override - public String getHeader(final String headerName) { - return request.getHeader(headerName); - } - - @Override - public Enumeration getHeaders(final String headerName) { - return request.getHeaders(headerName); - } - - @Override - public String requestURL() { - return request.getRequestURL().toString(); - } - - @Override - public String requestMethod() { - return request.getMethod(); - } - - @Override - public Map getParameterMap() { - return request.getParameterMap(); - } -} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ReactiveRequestHolder.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ReactiveRequestHolder.java deleted file mode 100644 index b5e1876ae0..0000000000 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ReactiveRequestHolder.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.mvc.commons; - -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.springframework.http.server.reactive.ServerHttpRequest; - -public class ReactiveRequestHolder implements RequestHolder { - private final ServerHttpRequest serverHttpRequest; - - public ReactiveRequestHolder(final ServerHttpRequest serverHttpRequest) { - this.serverHttpRequest = serverHttpRequest; - } - - @Override - public String getHeader(final String headerName) { - return this.serverHttpRequest.getHeaders().getFirst(headerName); - } - - @Override - public Enumeration getHeaders(final String headerName) { - List values = this.serverHttpRequest.getHeaders().get(headerName); - if (values == null) { - return Collections.enumeration(Collections.EMPTY_LIST); - } - return Collections.enumeration(values); - } - - @Override - public String requestURL() { - return this.serverHttpRequest.getURI().toString(); - } - - @Override - public String requestMethod() { - return this.serverHttpRequest.getMethodValue(); - } - - @Override - public Map getParameterMap() { - Map parameterMap = new HashMap<>(this.serverHttpRequest.getQueryParams().size()); - this.serverHttpRequest.getQueryParams().forEach((key, value) -> { - parameterMap.put(key, value.toArray(new String[0])); - }); - return parameterMap; - } -} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ReactiveResponseHolder.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ReactiveResponseHolder.java deleted file mode 100644 index 995114cf90..0000000000 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ReactiveResponseHolder.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.mvc.commons; - -import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; -import org.springframework.http.server.reactive.ServerHttpResponse; - -public class ReactiveResponseHolder implements ResponseHolder { - - private final ServerHttpResponse response; - - private AbstractSpan span; - - public ReactiveResponseHolder(final ServerHttpResponse response) { - this.response = response; - } - - public void setSpan(AbstractSpan span) { - this.span = span; - } - - public AbstractSpan getSpan() { - return this.span; - } - - @Override - public int statusCode() { - return response.getStatusCode().value(); - } -} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestUtil.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestUtil.java new file mode 100644 index 0000000000..640e04ce85 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestUtil.java @@ -0,0 +1,110 @@ +/* + * 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.mvc.commons; + +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.util.CollectionUtil; +import org.apache.skywalking.apm.util.StringUtil; +import org.springframework.http.server.reactive.ServerHttpRequest; + +import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RequestUtil { + public static void collectHttpParam(HttpServletRequest request, AbstractSpan span) { + final Map parameterMap = request.getParameterMap(); + if (parameterMap != null && !parameterMap.isEmpty()) { + String tagValue = CollectionUtil.toString(parameterMap); + tagValue = SpringMVCPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD > 0 ? + StringUtil.cut(tagValue, SpringMVCPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD) : tagValue; + Tags.HTTP.PARAMS.set(span, tagValue); + } + } + + public static void collectHttpParam(ServerHttpRequest request, AbstractSpan span) { + Map parameterMap = new HashMap<>(request.getQueryParams().size()); + request.getQueryParams().forEach((key, value) -> { + parameterMap.put(key, value.toArray(new String[0])); + }); + if (!parameterMap.isEmpty()) { + String tagValue = CollectionUtil.toString(parameterMap); + tagValue = SpringMVCPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD > 0 ? + StringUtil.cut(tagValue, SpringMVCPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD) : tagValue; + Tags.HTTP.PARAMS.set(span, tagValue); + } + } + + public static void collectHttpHeaders(HttpServletRequest request, AbstractSpan span) { + final List headersList = new ArrayList<>(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.size()); + SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.stream() + .filter( + headerName -> request.getHeaders(headerName) != null) + .forEach(headerName -> { + Enumeration headerValues = request.getHeaders( + headerName); + List valueList = Collections.list( + headerValues); + if (!CollectionUtil.isEmpty(valueList)) { + String headerValue = valueList.toString(); + headersList.add(headerName + "=" + headerValue); + } + }); + + collectHttpHeaders(headersList, span); + } + + public static void collectHttpHeaders(ServerHttpRequest request, AbstractSpan span) { + final List headersList = new ArrayList<>(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.size()); + SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.stream() + .filter(headerName -> getHeaders(request, headerName).hasMoreElements()) + .forEach(headerName -> { + Enumeration headerValues = getHeaders(request, headerName); + List valueList = Collections.list( + headerValues); + if (!CollectionUtil.isEmpty(valueList)) { + String headerValue = valueList.toString(); + headersList.add(headerName + "=" + headerValue); + } + }); + + collectHttpHeaders(headersList, span); + } + + private static void collectHttpHeaders(final List headersList, final AbstractSpan span) { + if (headersList != null && !headersList.isEmpty()) { + String tagValue = String.join("\n", headersList); + tagValue = SpringMVCPluginConfig.Plugin.Http.HTTP_HEADERS_LENGTH_THRESHOLD > 0 ? + StringUtil.cut(tagValue, SpringMVCPluginConfig.Plugin.Http.HTTP_HEADERS_LENGTH_THRESHOLD) : tagValue; + Tags.HTTP.HEADERS.set(span, tagValue); + } + } + + public static Enumeration getHeaders(final ServerHttpRequest request, final String headerName) { + List values = request.getHeaders().get(headerName); + if (values == null) { + return Collections.enumeration(Collections.emptyList()); + } + return Collections.enumeration(values); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ResponseHolder.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ResponseHolder.java deleted file mode 100644 index b46c4f2362..0000000000 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ResponseHolder.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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.mvc.commons; - -public interface ResponseHolder { - int statusCode(); -} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/AbstractMethodInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/AbstractMethodInterceptor.java index bcfcb6d490..c6121a65f1 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/AbstractMethodInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/AbstractMethodInterceptor.java @@ -18,16 +18,10 @@ package org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; 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.RuntimeContext; 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; @@ -38,16 +32,20 @@ import org.apache.skywalking.apm.agent.core.util.CollectionUtil; import org.apache.skywalking.apm.agent.core.util.MethodUtil; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.spring.mvc.commons.EnhanceRequireObjectCache; -import org.apache.skywalking.apm.plugin.spring.mvc.commons.RequestHolder; -import org.apache.skywalking.apm.plugin.spring.mvc.commons.ResponseHolder; -import org.apache.skywalking.apm.plugin.spring.mvc.commons.ReactiveResponseHolder; +import org.apache.skywalking.apm.plugin.spring.mvc.commons.RequestUtil; import org.apache.skywalking.apm.plugin.spring.mvc.commons.SpringMVCPluginConfig; import org.apache.skywalking.apm.plugin.spring.mvc.commons.exception.IllegalMethodStackDepthException; import org.apache.skywalking.apm.plugin.spring.mvc.commons.exception.ServletResponseNotFoundException; -import org.apache.skywalking.apm.util.StringUtil; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.lang.reflect.Method; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.CONTROLLER_METHOD_STACK_DEPTH; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.FORWARD_REQUEST_FLAG; +import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.REACTIVE_ASYNC_SPAN_IN_RUNTIME_CONTEXT; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT; @@ -60,9 +58,17 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround private static final String SERVLET_RESPONSE_CLASS = "javax.servlet.http.HttpServletResponse"; private static final String GET_STATUS_METHOD = "getStatus"; + private static boolean IN_SERVLET_CONTAINER; + static { IS_SERVLET_GET_STATUS_METHOD_EXIST = MethodUtil.isMethodExist( - AbstractMethodInterceptor.class.getClassLoader(), SERVLET_RESPONSE_CLASS, GET_STATUS_METHOD); + AbstractMethodInterceptor.class.getClassLoader(), SERVLET_RESPONSE_CLASS, GET_STATUS_METHOD); + try { + Class.forName(SERVLET_RESPONSE_CLASS, true, AbstractMethodInterceptor.class.getClassLoader()); + IN_SERVLET_CONTAINER = true; + } catch (Exception ignore) { + IN_SERVLET_CONTAINER = false; + } } public abstract String getRequestURL(Method method); @@ -96,31 +102,58 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround operationName = getAcceptedMethodTypes(method) + requestURL; } - RequestHolder request = (RequestHolder) ContextManager.getRuntimeContext() - .get(REQUEST_KEY_IN_RUNTIME_CONTEXT); + Object request = ContextManager.getRuntimeContext().get(REQUEST_KEY_IN_RUNTIME_CONTEXT); + if (request != null) { StackDepth stackDepth = (StackDepth) ContextManager.getRuntimeContext().get(CONTROLLER_METHOD_STACK_DEPTH); if (stackDepth == null) { - ContextCarrier contextCarrier = new ContextCarrier(); - CarrierItem next = contextCarrier.items(); - while (next.hasNext()) { - next = next.next(); - next.setHeadValue(request.getHeader(next.getHeadKey())); - } - - AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier); - Tags.URL.set(span, request.requestURL()); - Tags.HTTP.METHOD.set(span, request.requestMethod()); - span.setComponent(ComponentsDefine.SPRING_MVC_ANNOTATION); - SpanLayer.asHttp(span); - - if (SpringMVCPluginConfig.Plugin.SpringMVC.COLLECT_HTTP_PARAMS) { - collectHttpParam(request, span); - } - - if (!CollectionUtil.isEmpty(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS)) { - collectHttpHeaders(request, span); + final ContextCarrier contextCarrier = new ContextCarrier(); + + if (IN_SERVLET_CONTAINER && HttpServletRequest.class.isAssignableFrom(request.getClass())) { + final HttpServletRequest httpServletRequest = (HttpServletRequest) request; + CarrierItem next = contextCarrier.items(); + while (next.hasNext()) { + next = next.next(); + next.setHeadValue(httpServletRequest.getHeader(next.getHeadKey())); + } + + AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier); + Tags.URL.set(span, httpServletRequest.getRequestURL().toString()); + Tags.HTTP.METHOD.set(span, httpServletRequest.getMethod()); + span.setComponent(ComponentsDefine.SPRING_MVC_ANNOTATION); + SpanLayer.asHttp(span); + + if (SpringMVCPluginConfig.Plugin.SpringMVC.COLLECT_HTTP_PARAMS) { + RequestUtil.collectHttpParam(httpServletRequest, span); + } + + if (!CollectionUtil.isEmpty(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS)) { + RequestUtil.collectHttpHeaders(httpServletRequest, span); + } + } else if (ServerHttpRequest.class.isAssignableFrom(request.getClass())) { + final ServerHttpRequest serverHttpRequest = (ServerHttpRequest) request; + CarrierItem next = contextCarrier.items(); + while (next.hasNext()) { + next = next.next(); + next.setHeadValue(serverHttpRequest.getHeaders().getFirst(next.getHeadKey())); + } + + AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier); + Tags.URL.set(span, serverHttpRequest.getURI().toString()); + Tags.HTTP.METHOD.set(span, serverHttpRequest.getMethodValue()); + span.setComponent(ComponentsDefine.SPRING_MVC_ANNOTATION); + SpanLayer.asHttp(span); + + if (SpringMVCPluginConfig.Plugin.SpringMVC.COLLECT_HTTP_PARAMS) { + RequestUtil.collectHttpParam(serverHttpRequest, span); + } + + if (!CollectionUtil.isEmpty(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS)) { + RequestUtil.collectHttpHeaders(serverHttpRequest, span); + } + } else { + throw new IllegalStateException("this line should not be reached"); } stackDepth = new StackDepth(); @@ -136,8 +169,8 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround private String buildOperationName(Object invoker, Method method) { StringBuilder operationName = new StringBuilder(invoker.getClass().getName()).append(".") - .append(method.getName()) - .append("("); + .append(method.getName()) + .append("("); for (Class type : method.getParameterTypes()) { operationName.append(type.getName()).append(","); } @@ -152,7 +185,8 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround @Override public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { - Boolean forwardRequestFlag = (Boolean) ContextManager.getRuntimeContext().get(FORWARD_REQUEST_FLAG); + final RuntimeContext runtimeContext = ContextManager.getRuntimeContext(); + Boolean forwardRequestFlag = (Boolean) runtimeContext.get(FORWARD_REQUEST_FLAG); /** * Spring MVC plugin do nothing if current request is forward request. * Ref: https://github.com/apache/skywalking/pull/1325 @@ -161,11 +195,10 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround return ret; } - RequestHolder request = (RequestHolder) ContextManager.getRuntimeContext() - .get(REQUEST_KEY_IN_RUNTIME_CONTEXT); + Object request = runtimeContext.get(REQUEST_KEY_IN_RUNTIME_CONTEXT); if (request != null) { - StackDepth stackDepth = (StackDepth) ContextManager.getRuntimeContext().get(CONTROLLER_METHOD_STACK_DEPTH); + StackDepth stackDepth = (StackDepth) runtimeContext.get(CONTROLLER_METHOD_STACK_DEPTH); if (stackDepth == null) { throw new IllegalMethodStackDepthException(); } else { @@ -175,30 +208,43 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround AbstractSpan span = ContextManager.activeSpan(); if (stackDepth.depth() == 0) { - ResponseHolder response = (ResponseHolder) ContextManager.getRuntimeContext() - .get( - RESPONSE_KEY_IN_RUNTIME_CONTEXT); + Object response = runtimeContext.get(RESPONSE_KEY_IN_RUNTIME_CONTEXT); if (response == null) { throw new ServletResponseNotFoundException(); } - if (IS_SERVLET_GET_STATUS_METHOD_EXIST && response.statusCode() >= 400) { - span.errorOccurred(); - Tags.STATUS_CODE.set(span, Integer.toString(response.statusCode())); + Integer statusCode = null; + + if (IS_SERVLET_GET_STATUS_METHOD_EXIST && HttpServletResponse.class.isAssignableFrom(response.getClass())) { + statusCode = ((HttpServletResponse) response).getStatus(); + } else if (ServerHttpResponse.class.isAssignableFrom(response.getClass())) { + if (IS_SERVLET_GET_STATUS_METHOD_EXIST) { + statusCode = ((ServerHttpResponse) response).getRawStatusCode(); + } + Object context = runtimeContext.get(REACTIVE_ASYNC_SPAN_IN_RUNTIME_CONTEXT); + if (context != null) { + ((AbstractSpan[]) context)[0] = span.prepareForAsync(); + } } - if (response instanceof ReactiveResponseHolder) { - ReactiveResponseHolder reactiveResponse = (ReactiveResponseHolder) response; - AbstractSpan async = span.prepareForAsync(); - reactiveResponse.setSpan(async); + + if (statusCode != null && statusCode >= 400) { + span.errorOccurred(); + Tags.STATUS_CODE.set(span, Integer.toString(statusCode)); } - ContextManager.getRuntimeContext().remove(REQUEST_KEY_IN_RUNTIME_CONTEXT); - ContextManager.getRuntimeContext().remove(RESPONSE_KEY_IN_RUNTIME_CONTEXT); - ContextManager.getRuntimeContext().remove(CONTROLLER_METHOD_STACK_DEPTH); + + runtimeContext.remove(REACTIVE_ASYNC_SPAN_IN_RUNTIME_CONTEXT); + runtimeContext.remove(REQUEST_KEY_IN_RUNTIME_CONTEXT); + runtimeContext.remove(RESPONSE_KEY_IN_RUNTIME_CONTEXT); + runtimeContext.remove(CONTROLLER_METHOD_STACK_DEPTH); } // Active HTTP parameter collection automatically in the profiling context. if (!SpringMVCPluginConfig.Plugin.SpringMVC.COLLECT_HTTP_PARAMS && span.isProfiling()) { - collectHttpParam(request, span); + if (HttpServletRequest.class.isAssignableFrom(request.getClass())) { + RequestUtil.collectHttpParam((HttpServletRequest) request, span); + } else if (ServerHttpRequest.class.isAssignableFrom(request.getClass())) { + RequestUtil.collectHttpParam((ServerHttpRequest) request, span); + } } ContextManager.stopSpan(); @@ -212,38 +258,4 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround Class[] argumentsTypes, Throwable t) { ContextManager.activeSpan().log(t); } - - private void collectHttpParam(RequestHolder request, AbstractSpan span) { - final Map parameterMap = request.getParameterMap(); - if (parameterMap != null && !parameterMap.isEmpty()) { - String tagValue = CollectionUtil.toString(parameterMap); - tagValue = SpringMVCPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD > 0 ? - StringUtil.cut(tagValue, SpringMVCPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD) : tagValue; - Tags.HTTP.PARAMS.set(span, tagValue); - } - } - - private void collectHttpHeaders(RequestHolder request, AbstractSpan span) { - final List headersList = new ArrayList<>(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.size()); - SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.stream() - .filter( - headerName -> request.getHeaders(headerName) != null) - .forEach(headerName -> { - Enumeration headerValues = request.getHeaders( - headerName); - List valueList = Collections.list( - headerValues); - if (!CollectionUtil.isEmpty(valueList)) { - String headerValue = valueList.toString(); - headersList.add(headerName + "=" + headerValue); - } - }); - - if (!headersList.isEmpty()) { - String tagValue = headersList.stream().collect(Collectors.joining("\n")); - tagValue = SpringMVCPluginConfig.Plugin.Http.HTTP_HEADERS_LENGTH_THRESHOLD > 0 ? - StringUtil.cut(tagValue, SpringMVCPluginConfig.Plugin.Http.HTTP_HEADERS_LENGTH_THRESHOLD) : tagValue; - Tags.HTTP.HEADERS.set(span, tagValue); - } - } } diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/GetBeanInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/GetBeanInterceptor.java index 3fd2d61755..6cc6c62334 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/GetBeanInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/GetBeanInterceptor.java @@ -23,7 +23,6 @@ import org.apache.skywalking.apm.agent.core.context.ContextManager; 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.mvc.commons.JavaxServletRequestHolder; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -45,8 +44,8 @@ public class GetBeanInterceptor implements InstanceMethodsAroundInterceptor { Object ret) throws Throwable { if (ret instanceof EnhancedInstance) { ContextManager.getRuntimeContext() - .put(REQUEST_KEY_IN_RUNTIME_CONTEXT, new JavaxServletRequestHolder(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) - .getRequest())); + .put(REQUEST_KEY_IN_RUNTIME_CONTEXT, ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) + .getRequest()); } return ret; } diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/InvokeForRequestInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/InvokeForRequestInterceptor.java index 4360048f62..9d9b915b41 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/InvokeForRequestInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/InvokeForRequestInterceptor.java @@ -19,12 +19,10 @@ package org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor; import java.lang.reflect.Method; -import javax.servlet.http.HttpServletResponse; import org.apache.skywalking.apm.agent.core.context.ContextManager; 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.mvc.commons.JavaxServletResponseHolder; import org.springframework.web.context.request.NativeWebRequest; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT; @@ -38,8 +36,7 @@ public class InvokeForRequestInterceptor implements InstanceMethodsAroundInterce public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { ContextManager.getRuntimeContext() - .put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, new JavaxServletResponseHolder( - (HttpServletResponse) ((NativeWebRequest) allArguments[0]).getNativeResponse())); + .put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, ((NativeWebRequest) allArguments[0]).getNativeResponse()); } @Override diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/InvokeHandlerMethodInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/InvokeHandlerMethodInterceptor.java index 9d7df037d5..bc6e2f9adf 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/InvokeHandlerMethodInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/InvokeHandlerMethodInterceptor.java @@ -18,15 +18,12 @@ package org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor; -import java.lang.reflect.Method; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.apache.skywalking.apm.agent.core.context.ContextManager; 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.mvc.commons.JavaxServletRequestHolder; -import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletResponseHolder; + +import java.lang.reflect.Method; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT; @@ -36,10 +33,8 @@ public class InvokeHandlerMethodInterceptor implements InstanceMethodsAroundInte public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { if (allArguments[2] instanceof EnhancedInstance) { - ContextManager.getRuntimeContext().put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, new JavaxServletResponseHolder( - (HttpServletResponse) allArguments[1])); - ContextManager.getRuntimeContext().put(REQUEST_KEY_IN_RUNTIME_CONTEXT, new JavaxServletRequestHolder( - (HttpServletRequest) allArguments[0])); + ContextManager.getRuntimeContext().put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, allArguments[1]); + ContextManager.getRuntimeContext().put(REQUEST_KEY_IN_RUNTIME_CONTEXT, allArguments[0]); } } diff --git a/test/plugin/scenarios/springmvc-reactive-devtools-scenario/bin/startup.sh b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/bin/startup.sh new file mode 100644 index 0000000000..db12139edc --- /dev/null +++ b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/bin/startup.sh @@ -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. + +set -ex + +home="$(cd "$(dirname $0)"; pwd)" + +classpath="$(printf %s: $home/../target/3rd-lib/*.jar)${home}/../target/classes" +java ${agent_opts} -classpath "$classpath" test.apache.skywalking.apm.testcase.sc.springmvcreactive.Application & diff --git a/test/plugin/scenarios/springmvc-reactive-devtools-scenario/config/expectedData.yaml b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/config/expectedData.yaml new file mode 100644 index 0000000000..95dd650093 --- /dev/null +++ b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/config/expectedData.yaml @@ -0,0 +1,103 @@ +# 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: springmvc-reactive-devtools-scenario + segmentSize: ge 2 + segments: + - segmentId: not null + spans: + - operationName: H2/JDBI/PreparedStatement/executeQuery + operationId: 0 + parentSpanId: 0 + spanId: 1 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 32 + isError: false + spanType: Exit + peer: not null + tags: + - {key: db.type, value: not null} + - {key: db.instance, value: test} + - {key: db.statement, value: not null} + skipAnalysis: 'false' + - operationName: '/testcase/error' + operationId: 0 + parentSpanId: 0 + spanId: 2 + spanLayer: Http + startTime: not null + endTime: not null + isError: false + componentId: not null + tags: + - {key: url, value: not null} + - {key: http.method, value: GET} + skipAnalysis: 'false' + - operationName: 'future/get:/testcase/error' + operationId: 0 + parentSpanId: 0 + spanId: 3 + spanLayer: not null + startTime: not null + endTime: not null + isError: true + componentId: not null + logs: + - logEvent: + - {key: event, value: error} + - {key: error.kind, value: not null} + - {key: message, value: not null} + - {key: stack, value: not null} + skipAnalysis: 'false' + - operationName: '{GET}/testcase/{test}' + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 14 + isError: false + tags: + - {key: url, value: not null} + - {key: http.method, value: GET} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: '{GET}/testcase/error' + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 14 + spanType: Entry + isError: true + tags: + - {key: url, value: not null} + - {key: http.method, value: GET} + - {key: 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} + skipAnalysis: 'false' \ No newline at end of file diff --git a/test/plugin/scenarios/springmvc-reactive-devtools-scenario/configuration.yml b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/configuration.yml new file mode 100644 index 0000000000..12cb454c0f --- /dev/null +++ b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/configuration.yml @@ -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. + +type: jvm +entryService: http://localhost:8080/testcase/test +healthCheck: http://localhost:8080/testcase/healthCheck +startScript: ./bin/startup.sh diff --git a/test/plugin/scenarios/springmvc-reactive-devtools-scenario/pom.xml b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/pom.xml new file mode 100644 index 0000000000..05badf7d7a --- /dev/null +++ b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/pom.xml @@ -0,0 +1,124 @@ + + + + 4.0.0 + + org.apache.skywalking + springmvc-reactive-devtools-scenario + jar + 5.0.0 + + skywalking-springmvc-reactive-devtools-scenario + + + UTF-8 + 2.1.1.RELEASE + ${test.framework.version} + + + + + org.springframework.boot + spring-boot-starter-webflux + ${test.framework.version} + + + org.springframework.boot + spring-boot-devtools + true + ${test.framework.version} + + + com.h2database + h2 + 1.4.200 + + + + + springmvc-reactive-devtools-scenario + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dependencies + package + + copy-dependencies + + + jar + jar + runtime + + ${project.build.directory}/3rd-lib/ + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.0 + + 1.8 + 1.8 + ${project.build.sourceEncoding} + + + + org.springframework.boot + spring-boot-maven-plugin + ${test.framework.version} + + + + repackage + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + + + diff --git a/test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..5e8d436ab6 --- /dev/null +++ b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,49 @@ + + + + + zip + + + + + ./bin + 0775 + + + ${project.build.directory}/classes + 0775 + + + ${project.build.directory}/3rd-lib + 0775 + + + + + + ${project.build.directory}/springmvc-reactive-devtools-scenario.jar + ./libs + 0775 + + + diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestHolder.java b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/Application.java similarity index 70% rename from apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestHolder.java rename to test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/Application.java index 8f4c933d65..4d6eae3582 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestHolder.java +++ b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/Application.java @@ -15,19 +15,15 @@ * limitations under the License. */ -package org.apache.skywalking.apm.plugin.spring.mvc.commons; +package test.apache.skywalking.apm.testcase.sc.springmvcreactive; -import java.util.Enumeration; -import java.util.Map; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; -public interface RequestHolder { - String getHeader(String headerName); +@SpringBootApplication +public class Application { - Enumeration getHeaders(String headerName); - - String requestURL(); - - String requestMethod(); - - Map getParameterMap(); + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } } diff --git a/test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/controller/Controller.java b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/controller/Controller.java new file mode 100644 index 0000000000..0117cad6b7 --- /dev/null +++ b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/controller/Controller.java @@ -0,0 +1,60 @@ +/* + * 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.springmvcreactive.controller; + +import java.sql.SQLException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.util.concurrent.ListenableFuture; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.AsyncRestTemplate; +import reactor.core.publisher.Mono; +import test.apache.skywalking.apm.testcase.sc.springmvcreactive.service.TestService; + +@RestController +public class Controller { + + @Autowired + private TestService testService; + + @RequestMapping("/testcase/healthCheck") + public String healthCheck() { + return "healthCheck"; + } + + @GetMapping("/testcase/{test}") + public Mono hello(@RequestBody(required = false) String body, @PathVariable("test") String test) throws SQLException { + testService.executeSQL(); + ListenableFuture> forEntity = new AsyncRestTemplate().getForEntity("http://localhost:8080/testcase/error", String.class); + try { + forEntity.get(); + } catch (Exception e) { + } + return Mono.just("Hello World"); + } + + @GetMapping("/testcase/error") + public Mono error() { + throw new RuntimeException("this is Error"); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/JavaxServletResponseHolder.java b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/service/TestService.java similarity index 54% rename from apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/JavaxServletResponseHolder.java rename to test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/service/TestService.java index 7fc0144562..aff108f3b0 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/JavaxServletResponseHolder.java +++ b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/service/TestService.java @@ -15,21 +15,28 @@ * limitations under the License. */ -package org.apache.skywalking.apm.plugin.spring.mvc.commons; +package test.apache.skywalking.apm.testcase.sc.springmvcreactive.service; -import javax.servlet.http.HttpServletResponse; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import javax.annotation.PostConstruct; +import org.springframework.stereotype.Service; -public class JavaxServletResponseHolder implements ResponseHolder { +@Service +public class TestService { - private final HttpServletResponse response; + private Connection connection; - public JavaxServletResponseHolder(final HttpServletResponse response) { - this.response = response; + @PostConstruct + private void setUp() throws SQLException { + connection = DriverManager.getConnection("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", "sa", ""); } - @Override - public int statusCode() { - return response.getStatus(); + public void executeSQL() throws SQLException { + PreparedStatement preparedStatement = connection.prepareStatement("SELECT 1 = 1"); + preparedStatement.executeQuery(); } } diff --git a/test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/resources/application.yml b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/resources/application.yml new file mode 100644 index 0000000000..e916bcda18 --- /dev/null +++ b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/src/main/resources/application.yml @@ -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. +# + +server.port: 8080 \ No newline at end of file diff --git a/test/plugin/scenarios/springmvc-reactive-devtools-scenario/support-version.list b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/support-version.list new file mode 100644 index 0000000000..c5820daf80 --- /dev/null +++ b/test/plugin/scenarios/springmvc-reactive-devtools-scenario/support-version.list @@ -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. + +# 2.0.0-2.1.0 are supported, but due to the status code bug(https://github.com/spring-projects/spring-framework/issues/21901) +# we don’t test them + +2.1.7.RELEASE diff --git a/test/plugin/scenarios/springmvc-reactive-scenario/pom.xml b/test/plugin/scenarios/springmvc-reactive-scenario/pom.xml index aa3c075cd4..9459d98a64 100644 --- a/test/plugin/scenarios/springmvc-reactive-scenario/pom.xml +++ b/test/plugin/scenarios/springmvc-reactive-scenario/pom.xml @@ -63,7 +63,7 @@ org.springframework.boot spring-boot-maven-plugin - 1.5.9.RELEASE + ${test.framework.version} -- GitLab