From 4e03d5e6d73ac32093960a5cdf3de5863ad6aede Mon Sep 17 00:00:00 2001 From: Kanro Date: Sat, 4 Jan 2020 22:51:59 +0800 Subject: [PATCH] Add kotlin coroutine plugin (#4165) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Kotlin coroutine plugin write in Java Co-authored-by: 吴晟 Wu Sheng --- .github/workflows/plugins-test.yaml | 4 +- .../trace/component/ComponentsDefine.java | 2 + .../kotlin-coroutine-plugin/pom.xml | 47 ++++++ .../coroutine/DispatcherInterceptor.java | 42 +++++ .../kotlin/coroutine/TracingRunnable.java | 83 ++++++++++ .../define/DispatcherInstrumentation.java | 70 ++++++++ .../src/main/resources/skywalking-plugin.def | 17 ++ apm-sniffer/optional-plugins/pom.xml | 1 + .../setup/service-agent/java-agent/README.md | 1 + .../java-agent/Supported-list.md | 2 + .../Kotlin-Coroutine-plugin.md | 28 ++++ .../kotlin-coroutine-scenario/bin/startup.sh | 21 +++ .../config/expectedData.yaml | 132 +++++++++++++++ .../configuration.yml | 23 +++ .../kotlin-coroutine-scenario/pom.xml | 156 ++++++++++++++++++ .../src/main/assembly/assembly.xml | 41 +++++ .../testcase/kotlin/coroutine/Application.kt | 29 ++++ .../coroutine/controller/DemoController.kt | 65 ++++++++ .../coroutine/respository/DemoRepository.kt | 58 +++++++ .../kotlin/coroutine/service/DemoService.kt | 35 ++++ .../kotlin/coroutine/util/Connection.kt | 30 ++++ .../src/main/resources/application.yaml | 23 +++ .../src/main/resources/log4j2.xml | 30 ++++ .../support-version.list | 19 +++ 24 files changed, 958 insertions(+), 1 deletion(-) create mode 100644 apm-sniffer/optional-plugins/kotlin-coroutine-plugin/pom.xml create mode 100644 apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatcherInterceptor.java create mode 100644 apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/TracingRunnable.java create mode 100644 apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/define/DispatcherInstrumentation.java create mode 100644 apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/resources/skywalking-plugin.def create mode 100644 docs/en/setup/service-agent/java-agent/agent-optional-plugins/Kotlin-Coroutine-plugin.md create mode 100644 test/plugin/scenarios/kotlin-coroutine-scenario/bin/startup.sh create mode 100644 test/plugin/scenarios/kotlin-coroutine-scenario/config/expectedData.yaml create mode 100644 test/plugin/scenarios/kotlin-coroutine-scenario/configuration.yml create mode 100644 test/plugin/scenarios/kotlin-coroutine-scenario/pom.xml create mode 100644 test/plugin/scenarios/kotlin-coroutine-scenario/src/main/assembly/assembly.xml create mode 100644 test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/Application.kt create mode 100644 test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/controller/DemoController.kt create mode 100644 test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/respository/DemoRepository.kt create mode 100644 test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/service/DemoService.kt create mode 100644 test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/util/Connection.kt create mode 100644 test/plugin/scenarios/kotlin-coroutine-scenario/src/main/resources/application.yaml create mode 100644 test/plugin/scenarios/kotlin-coroutine-scenario/src/main/resources/log4j2.xml create mode 100644 test/plugin/scenarios/kotlin-coroutine-scenario/support-version.list diff --git a/.github/workflows/plugins-test.yaml b/.github/workflows/plugins-test.yaml index da878234a..48317e4ad 100644 --- a/.github/workflows/plugins-test.yaml +++ b/.github/workflows/plugins-test.yaml @@ -182,7 +182,7 @@ jobs: - name: Run spring 3.0.x (8) run: bash test/plugin/run.sh spring-3.0.x-scenario - SpringAsync_gRPC: + SpringAsync_gRPC_KotlinCoroutine: runs-on: ubuntu-18.04 timeout-minutes: 90 strategy: @@ -208,6 +208,8 @@ jobs: run: bash test/plugin/run.sh spring-async-scenario - name: Run grpc 1.6.0-1.25.0 (22) run: bash test/plugin/run.sh grpc-scenario + - name: Run kotlin coroutine 1.0.1-1.3.3 (4) + run: bash test/plugin/run.sh kotlin-coroutine-scenario Ehcache_Undertow_Jedis: runs-on: ubuntu-18.04 diff --git a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java index 1ec2dec08..d628c18b4 100755 --- a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java +++ b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java @@ -151,4 +151,6 @@ public class ComponentsDefine { public static final OfficialComponent ARMERIA = new OfficialComponent(79, "Armeria"); public static final OfficialComponent JDK_THREADING = new OfficialComponent(80, "JdkThreading"); + + public static final OfficialComponent KT_COROUTINE = new OfficialComponent(81, "KotlinCoroutine"); } diff --git a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/pom.xml b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/pom.xml new file mode 100644 index 000000000..85217cf80 --- /dev/null +++ b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/pom.xml @@ -0,0 +1,47 @@ + + + + + 4.0.0 + + org.apache.skywalking + optional-plugins + 7.0.0-SNAPSHOT + + + apm-kotlin-coroutine-plugin + kotlin-coroutine-plugin + jar + + + UTF-8 + 1.3.61 + 1.3.3 + + + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + ${kotlinx.coroutine.version} + provided + + + diff --git a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatcherInterceptor.java b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatcherInterceptor.java new file mode 100644 index 000000000..27eb41e2b --- /dev/null +++ b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatcherInterceptor.java @@ -0,0 +1,42 @@ +/* + * 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.kotlin.coroutine; + +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 java.lang.reflect.Method; + +public class DispatcherInterceptor implements InstanceMethodsAroundInterceptor { + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) { + // Wrapping runnable with current context snapshot + allArguments[1] = TracingRunnable.wrapOrNot((Runnable) allArguments[1]); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) { + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + } +} diff --git a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/TracingRunnable.java b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/TracingRunnable.java new file mode 100644 index 000000000..ef8f2a47f --- /dev/null +++ b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/TracingRunnable.java @@ -0,0 +1,83 @@ +/* + * 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.kotlin.coroutine; + +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.network.trace.component.ComponentsDefine; + +/** + * {@link Runnable} wrapper with trace context snapshot, it will create span + * with context snapshot around {@link Runnable} runs. + *

+ * A class implementation will be cheaper cost than lambda with captured + * variables implementation. + */ +class TracingRunnable implements Runnable { + private static final String COROUTINE = "/Kotlin/Coroutine"; + + private ContextSnapshot snapshot; + private Runnable delegate; + + private TracingRunnable(ContextSnapshot snapshot, Runnable delegate) { + this.snapshot = snapshot; + this.delegate = delegate; + } + + /** + * Wrap {@link Runnable} by {@link TracingRunnable} if active trace context + * existed. + * + * @param delegate {@link Runnable} to wrap. + * + * @return Wrapped {@link TracingRunnable} or original {@link Runnable} if + * trace context not existed. + */ + public static Runnable wrapOrNot(Runnable delegate) { + // Just wrap continuation with active trace context + if (ContextManager.isActive()) { + return new TracingRunnable(ContextManager.capture(), delegate); + } else { + return delegate; + } + } + + @Override + public void run() { + if (ContextManager.isActive() && snapshot.isFromCurrent()) { + // Thread not switched, skip restore snapshot. + delegate.run(); + return; + } + + // Create local coroutine span + AbstractSpan span = ContextManager.createLocalSpan(COROUTINE); + span.setComponent(ComponentsDefine.KT_COROUTINE); + + // Recover with snapshot + ContextManager.continued(snapshot); + + try { + delegate.run(); + } finally { + ContextManager.stopSpan(span); + } + } +} diff --git a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/define/DispatcherInstrumentation.java b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/define/DispatcherInstrumentation.java new file mode 100644 index 000000000..7c37eb317 --- /dev/null +++ b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/define/DispatcherInstrumentation.java @@ -0,0 +1,70 @@ +/* + * 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.kotlin.coroutine.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 org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; + +public class DispatcherInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + public static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.kotlin.coroutine.DispatcherInterceptor"; + public static final String ENHANCE_CLASS = "kotlinx.coroutines.CoroutineDispatcher"; + public static final String ENHANCE_METHOD_DISPATCH = "dispatch"; + + @Override + protected ClassMatch enhanceClass() { + return HierarchyMatch.byHierarchyMatch(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(ENHANCE_METHOD_DISPATCH) + .and(takesArgumentWithType(0, "kotlin.coroutines.CoroutineContext")) + .and(takesArgumentWithType(1, "java.lang.Runnable")); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPTOR_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return true; + } + } + }; + } +} diff --git a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 000000000..a9d7b1bc8 --- /dev/null +++ b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/resources/skywalking-plugin.def @@ -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. + +kotlin-coroutine=org.apache.skywalking.apm.plugin.kotlin.coroutine.define.DispatcherInstrumentation \ No newline at end of file diff --git a/apm-sniffer/optional-plugins/pom.xml b/apm-sniffer/optional-plugins/pom.xml index c8534f92a..a7c62dc93 100644 --- a/apm-sniffer/optional-plugins/pom.xml +++ b/apm-sniffer/optional-plugins/pom.xml @@ -46,6 +46,7 @@ gson-2.8.x-plugin zookeeper-3.4.x-plugin customize-enhance-plugin + kotlin-coroutine-plugin diff --git a/docs/en/setup/service-agent/java-agent/README.md b/docs/en/setup/service-agent/java-agent/README.md index 2b66f6999..0c0b30537 100755 --- a/docs/en/setup/service-agent/java-agent/README.md +++ b/docs/en/setup/service-agent/java-agent/README.md @@ -135,6 +135,7 @@ Now, we have the following known optional plugins. * [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.1.x in optional plugin folder. Please only active this plugin when you install agent in Spring Gateway. spring-cloud-gateway-2.x-plugin and spring-webflux-5.x-plugin are both required. * 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. ## Bootstrap class plugins All bootstrap plugins are optional, due to unexpected risk. Bootstrap plugins are provided in `bootstrap-plugins` folder. diff --git a/docs/en/setup/service-agent/java-agent/Supported-list.md b/docs/en/setup/service-agent/java-agent/Supported-list.md index 4e6a1e825..263e126f2 100644 --- a/docs/en/setup/service-agent/java-agent/Supported-list.md +++ b/docs/en/setup/service-agent/java-agent/Supported-list.md @@ -86,6 +86,8 @@ * [Spring @Async](https://github.com/spring-projects/spring-framework) 4.x and 5.x * Cache * [Ehcache](https://www.ehcache.org/) 2.x +* Kotlin + * [Coroutine](https://kotlinlang.org/docs/reference/coroutines-overview.html) 1.0.1 -> 1.3.x (Optional²) ¹Due to license incompatibilities/restrictions these plugins are hosted and released in 3rd part repository, diff --git a/docs/en/setup/service-agent/java-agent/agent-optional-plugins/Kotlin-Coroutine-plugin.md b/docs/en/setup/service-agent/java-agent/agent-optional-plugins/Kotlin-Coroutine-plugin.md new file mode 100644 index 000000000..27c200193 --- /dev/null +++ b/docs/en/setup/service-agent/java-agent/agent-optional-plugins/Kotlin-Coroutine-plugin.md @@ -0,0 +1,28 @@ +# Skywalking with Kotlin coroutine +This Plugin provides an auto instrument support plugin for Kotlin coroutine based on context snapshot. + +## Description +SkyWalking provide tracing context propagation inside thread. In order to support Kotlin Coroutine, we provide this additional plugin. + +## Implementation principle +As we know, Kotlin coroutine switches the execution thread by `CoroutineDispatcher`. + +01. Create a snapshot of the current context before dispatch the continuation. +02. Then create a coroutine span after thread switched, mark the span continued with the snapshot. +03. Every new span which created in the new thread will be a child of this coroutine span. So we can link those span together in a tracing. +04. After the original runnable executed, we need to stop the coroutine span for cleaning thread state. + +## Some screenshots +### Run without the plugin +We run a Kotlin coroutine based gRPC server without this coroutine plugin. +You can find, the one call (client -> server1 -> server2) has been split two tracing paths. + +01. Server1 without exit span and server2 tracing path. +![Without kotlin plugin1](http://skywalking.apache.org/screenshots/7.0.0/kotlin/coroutine/without-coroutine-plugin-server1.jpg) +02. Server2 tracing path. +![Without kotlin plugin2](http://skywalking.apache.org/screenshots/7.0.0/kotlin/coroutine/without-coroutine-plugin-server2.jpg) + +### Run with the plugin +Without changing codes manually, just install the plugin. We can find the spans be connected together. We can get all info of one client call. + +![With kotlin plugin](http://skywalking.apache.org/screenshots/7.0.0/kotlin/coroutine/run-with-coroutine-plugin.jpg) diff --git a/test/plugin/scenarios/kotlin-coroutine-scenario/bin/startup.sh b/test/plugin/scenarios/kotlin-coroutine-scenario/bin/startup.sh new file mode 100644 index 000000000..4978b078c --- /dev/null +++ b/test/plugin/scenarios/kotlin-coroutine-scenario/bin/startup.sh @@ -0,0 +1,21 @@ +#!/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} ${home}/../libs/kotlin-coroutine-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/kotlin-coroutine-scenario/config/expectedData.yaml b/test/plugin/scenarios/kotlin-coroutine-scenario/config/expectedData.yaml new file mode 100644 index 000000000..d46e352c7 --- /dev/null +++ b/test/plugin/scenarios/kotlin-coroutine-scenario/config/expectedData.yaml @@ -0,0 +1,132 @@ +# 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. +registryItems: + applications: + - {kotlin-coroutine-scenario: 2} + instances: + - {kotlin-coroutine-scenario: 1} + operationNames: + - kotlin-coroutine-scenario: [/kotlin-coroutine-scenario/case/h2] + heartbeat: [] +segmentItems: + - applicationCode: kotlin-coroutine-scenario + segmentSize: gt 1 + segments: + - segmentId: not null + spans: + - operationName: H2/JDBI/PreparedStatement/executeQuery + operationId: 0 + parentSpanId: 0 + spanId: 1 + spanLayer: Database + startTime: nq 0 + endTime: nq 0 + componentId: 32 + componentName: '' + isError: false + spanType: Exit + peer: localhost:-1 + peerId: 0 + tags: + - {key: db.type, value: 'sql'} + - {key: db.instance, value: 'demo'} + - {key: db.statement, value: 'SELECT * FROM PERSON WHERE ID = ?'} + - operationName: H2/JDBI/Connection/commit + operationId: 0 + parentSpanId: 0 + spanId: 2 + spanLayer: Database + startTime: nq 0 + endTime: nq 0 + componentId: 32 + componentName: '' + isError: false + spanType: Exit + peer: localhost:-1 + peerId: 0 + tags: + - {key: db.type, value: 'sql'} + - {key: db.instance, value: 'demo'} + - {key: db.statement, value: ''} + - operationName: /Kotlin/Coroutine + operationId: 0 + parentSpanId: -1 + spanId: 0 + startTime: nq 0 + endTime: nq 0 + componentId: 81 + componentName: '' + isError: false + spanType: Local + peer: '' + peerId: 0 + refs: + - {parentEndpointId: 0, parentEndpoint: /kotlin-coroutine-scenario/case/h2, + networkAddressId: 0, entryEndpointId: 0, refType: CrossThread, parentSpanId: 0, + parentTraceSegmentId: "${kotlin-coroutine-scenario[1]}", parentServiceInstanceId: 1, + networkAddress: '', entryEndpoint: /kotlin-coroutine-scenario/case/h2, entryServiceInstanceId: 1} + - segmentId: not null + spans: + - operationName: H2/JDBI/PreparedStatement/executeQuery + operationId: 0 + parentSpanId: 0 + spanId: 1 + spanLayer: Database + startTime: nq 0 + endTime: nq 0 + componentId: 32 + componentName: '' + isError: false + spanType: Exit + peer: localhost:-1 + peerId: 0 + tags: + - {key: db.type, value: 'sql'} + - {key: db.instance, value: 'demo'} + - {key: db.statement, value: 'SELECT * FROM PERSON WHERE ID = ?'} + - operationName: H2/JDBI/Connection/commit + operationId: 0 + parentSpanId: 0 + spanId: 2 + spanLayer: Database + startTime: nq 0 + endTime: nq 0 + componentId: 32 + componentName: '' + isError: false + spanType: Exit + peer: localhost:-1 + peerId: 0 + tags: + - {key: db.type, value: 'sql'} + - {key: db.instance, value: 'demo'} + - {key: db.statement, value: ''} + - operationName: /kotlin-coroutine-scenario/case/h2 + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 1 + componentName: '' + isError: false + spanType: Entry + peer: '' + peerId: 0 + tags: + - {key: url, value: 'http://localhost:8080/kotlin-coroutine-scenario/case/h2'} + - {key: http.method, value: GET} \ No newline at end of file diff --git a/test/plugin/scenarios/kotlin-coroutine-scenario/configuration.yml b/test/plugin/scenarios/kotlin-coroutine-scenario/configuration.yml new file mode 100644 index 000000000..157de2fc0 --- /dev/null +++ b/test/plugin/scenarios/kotlin-coroutine-scenario/configuration.yml @@ -0,0 +1,23 @@ +# 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/kotlin-coroutine-scenario/case/h2 +healthCheck: http://localhost:8080/kotlin-coroutine-scenario/case/healthCheck +startScript: ./bin/startup.sh +framework: kt-coroutine +runningMode: with_optional +withPlugins: apm-kotlin-coroutine-plugin-*.jar \ No newline at end of file diff --git a/test/plugin/scenarios/kotlin-coroutine-scenario/pom.xml b/test/plugin/scenarios/kotlin-coroutine-scenario/pom.xml new file mode 100644 index 000000000..b589e2e2b --- /dev/null +++ b/test/plugin/scenarios/kotlin-coroutine-scenario/pom.xml @@ -0,0 +1,156 @@ + + + + + org.apache.skywalking.apm.testcase + kotlin-coroutine-scenario + 1.0.0 + jar + + 4.0.0 + + + UTF-8 + 1.8 + 1.3.3 + + 2.1.6.RELEASE + 1.3.61 + 1.3.3 + + + skywalking-kotlin-coroutine-scenario + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot-version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + jul-to-slf4j + org.slf4j + + + + + com.h2database + h2 + 1.4.196 + + + commons-dbcp + commons-dbcp + 1.4 + + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + ${test.framework.version} + + + + + kotlin-coroutine-scenario + ${project.basedir}/src/main/kotlin + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot-version} + + + + repackage + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + + compile + + compile + + + + + test-compile + + test-compile + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/assembly/assembly.xml new file mode 100644 index 000000000..5024a751c --- /dev/null +++ b/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ${project.build.directory}/kotlin-coroutine-scenario.jar + ./libs + 0775 + + + diff --git a/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/Application.kt b/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/Application.kt new file mode 100644 index 000000000..6e8c567d9 --- /dev/null +++ b/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/Application.kt @@ -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.testcase.kotlin.coroutine + +import org.springframework.boot.SpringApplication +import org.springframework.boot.autoconfigure.SpringBootApplication + +@SpringBootApplication +open class Application + +fun main(args: Array) { + SpringApplication.run(Application::class.java, *args) +} \ No newline at end of file diff --git a/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/controller/DemoController.kt b/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/controller/DemoController.kt new file mode 100644 index 000000000..a50b851c1 --- /dev/null +++ b/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/controller/DemoController.kt @@ -0,0 +1,65 @@ +/* + * 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.testcase.kotlin.coroutine.controller + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import org.apache.skywalking.apm.testcase.kotlin.coroutine.service.DemoService +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Controller +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.ResponseBody + +@Controller +@RequestMapping("/case") +class DemoController { + @Autowired + private lateinit var service: DemoService + + @ResponseBody + @RequestMapping("/healthCheck") + fun healthCheck(): String { + return "Success" + } + + @ResponseBody + @RequestMapping("/h2") + fun h2ThreadSwitchedCase(): String { + val threadId = Thread.currentThread().id + + runBlocking { + // Run blocking with default context will not switch thread + if (threadId != Thread.currentThread().id) { + throw IllegalStateException("Prerequisite failed") + } + + service.work() + } + + runBlocking(Dispatchers.IO) { + // Run blocking with IO dispatcher will switch to IO thread + if (threadId == Thread.currentThread().id) { + throw IllegalStateException("Prerequisite failed") + } + + service.work() + } + return "Success" + } +} \ No newline at end of file diff --git a/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/respository/DemoRepository.kt b/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/respository/DemoRepository.kt new file mode 100644 index 000000000..641a1e4d5 --- /dev/null +++ b/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/respository/DemoRepository.kt @@ -0,0 +1,58 @@ +/* + * 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.testcase.kotlin.coroutine.respository + +import org.apache.commons.dbcp.BasicDataSource +import org.apache.skywalking.apm.testcase.kotlin.coroutine.util.use +import org.springframework.stereotype.Service +import javax.sql.DataSource + +@Service +class DemoRepository { + private val datasource: DataSource = BasicDataSource().apply { + driverClassName = "org.h2.Driver" + url = String.format("jdbc:h2:mem:demo") + username = "" + password = "" + } + + init { + datasource.connection.use { + prepareStatement("CREATE TABLE PERSON(ID INT auto_increment primary key, NAME varchar(255))").execute() + prepareStatement("INSERT INTO PERSON (NAME) VALUES (?)").apply { + setString(1, "kanro") + execute() + } + } + } + + fun getPersonName(id: Int): String? { + return datasource.connection.use { + prepareStatement("SELECT * FROM PERSON WHERE ID = ?").run { + setInt(1, id) + val result = executeQuery() + if (result.next()) { + result.getString("NAME") + } else { + null + } + } + } + } +} \ No newline at end of file diff --git a/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/service/DemoService.kt b/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/service/DemoService.kt new file mode 100644 index 000000000..fdf0deddd --- /dev/null +++ b/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/service/DemoService.kt @@ -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.testcase.kotlin.coroutine.service + +import org.apache.skywalking.apm.testcase.kotlin.coroutine.respository.DemoRepository +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Service + +@Service +class DemoService { + @Autowired + private lateinit var repository: DemoRepository + + fun work() { + if (repository.getPersonName(1) == null) { + throw IllegalStateException() + } + } +} \ No newline at end of file diff --git a/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/util/Connection.kt b/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/util/Connection.kt new file mode 100644 index 000000000..4e5ef78be --- /dev/null +++ b/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/kotlin/org/apache/skywalking/apm/testcase/kotlin/coroutine/util/Connection.kt @@ -0,0 +1,30 @@ +/* + * 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.testcase.kotlin.coroutine.util + +import java.sql.Connection + +inline fun Connection.use(block: Connection.() -> T): T { + try { + return block() + } finally { + commit() + close() + } +} \ No newline at end of file diff --git a/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/resources/application.yaml new file mode 100644 index 000000000..445b4d0d4 --- /dev/null +++ b/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/resources/application.yaml @@ -0,0 +1,23 @@ +# +# 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 + servlet: + context-path: /kotlin-coroutine-scenario +logging: + config: classpath:log4j2.xml diff --git a/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/resources/log4j2.xml b/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/resources/log4j2.xml new file mode 100644 index 000000000..9849ed5a8 --- /dev/null +++ b/test/plugin/scenarios/kotlin-coroutine-scenario/src/main/resources/log4j2.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/kotlin-coroutine-scenario/support-version.list b/test/plugin/scenarios/kotlin-coroutine-scenario/support-version.list new file mode 100644 index 000000000..36039cea9 --- /dev/null +++ b/test/plugin/scenarios/kotlin-coroutine-scenario/support-version.list @@ -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 +# "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. +1.3.3 +1.2.2 +1.1.1 +1.0.1 \ No newline at end of file