Skip to content

Commit bca5c8e

Browse files
authored
Refactor kotlin coroutine plugin with CoroutineContext (#453)
1 parent 5250ecf commit bca5c8e

12 files changed

Lines changed: 205 additions & 494 deletions

CHANGES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ Release Notes.
1212
* Remove Powermock entirely from the test cases.
1313
* Fix H2 instrumentation point
1414
* Refactor pipeline in jedis-plugin.
15-
* Enhance kotlin coroutine plugin for stack tracing.
1615
* Add plugin to support ClickHouse JDBC driver (0.3.2.*).
16+
* Refactor kotlin coroutine plugin with CoroutineContext.
1717
* Fix OracleURLParser ignoring actual port when :SID is absent.
1818

1919
#### Documentation

apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatchedTaskExceptionInterceptor.java

Lines changed: 0 additions & 60 deletions
This file was deleted.

apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatchedTaskRunInterceptor.java

Lines changed: 0 additions & 73 deletions
This file was deleted.

apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatcherInterceptor.java

Lines changed: 0 additions & 56 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package org.apache.skywalking.apm.plugin.kotlin.coroutine;
20+
21+
import kotlin.coroutines.CoroutineContext;
22+
import org.apache.skywalking.apm.agent.core.context.ContextManager;
23+
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext;
24+
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.StaticMethodsAroundInterceptorV2;
25+
26+
import java.lang.reflect.Method;
27+
28+
public class NewCoroutineContextInterceptor implements StaticMethodsAroundInterceptorV2 {
29+
30+
@Override
31+
public void beforeMethod(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes, MethodInvocationContext context) {
32+
33+
}
34+
35+
@Override
36+
public Object afterMethod(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes, Object ret, MethodInvocationContext context) {
37+
if (ContextManager.isActive()) {
38+
// Create TracingCoroutineContext when have been in tracing context already.
39+
// Kotlin coroutine plugin does not automatically activate tracing.
40+
CoroutineContext coroutineContext = (CoroutineContext) ret;
41+
// Provide context snapshot for current tracing to TracingCoroutineContext.
42+
return coroutineContext.plus(new TracingCoroutineContext(ContextManager.capture()));
43+
}
44+
45+
return ret;
46+
}
47+
48+
@Override
49+
public void handleMethodException(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes, Throwable t, MethodInvocationContext context) {
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package org.apache.skywalking.apm.plugin.kotlin.coroutine;
20+
21+
import kotlin.coroutines.AbstractCoroutineContextElement;
22+
import kotlin.coroutines.CoroutineContext;
23+
import kotlinx.coroutines.ThreadContextElement;
24+
import org.apache.skywalking.apm.agent.core.context.ContextManager;
25+
import org.apache.skywalking.apm.agent.core.context.ContextSnapshot;
26+
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
27+
import org.apache.skywalking.apm.agent.core.logging.api.ILog;
28+
import org.apache.skywalking.apm.agent.core.logging.api.LogManager;
29+
import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
30+
import org.jetbrains.annotations.NotNull;
31+
32+
public class TracingCoroutineContext extends AbstractCoroutineContextElement implements ThreadContextElement<AbstractSpan> {
33+
private static class Key implements CoroutineContext.Key<TracingCoroutineContext> {
34+
public static final Key INSTANCE = new Key();
35+
36+
private Key() {
37+
}
38+
}
39+
40+
private static final String COROUTINE_OPERATION = "Kotlin/Coroutine";
41+
42+
private static final ILog LOG = LogManager.getLogger(TracingCoroutineContext.class);
43+
44+
private final ContextSnapshot snapshot;
45+
46+
public TracingCoroutineContext(ContextSnapshot snapshot) {
47+
super(Key.INSTANCE);
48+
this.snapshot = snapshot;
49+
}
50+
51+
@Override
52+
public void restoreThreadContext(@NotNull CoroutineContext coroutineContext, AbstractSpan span) {
53+
if (ContextManager.isActive() && span != null) {
54+
ContextManager.stopSpan(span);
55+
}
56+
}
57+
58+
@Override
59+
public AbstractSpan updateThreadContext(@NotNull CoroutineContext coroutineContext) {
60+
// Coroutine will be executed in a new thread, we need recover our tracing context in this thread.
61+
62+
// Snapshot is null means tracing is disabled in current coroutine.
63+
if (snapshot != null) {
64+
65+
// Tracing is enabled on the target dispatched thread.
66+
if (ContextManager.isActive()) {
67+
68+
// If the trace context is from the snapshot, it means that the thread has not been switched,
69+
// and there is no need to create a cross-thread span.
70+
// If not, it means that we are scheduled to a dirty thread, we log a warning and give up to
71+
// create cross-thread span.
72+
if (!snapshot.isFromCurrent()) {
73+
LOG.warn("Kotlin coroutine has been dispatched to a dirty thread which with active span: {}.", ContextManager.getGlobalTraceId());
74+
}
75+
return null;
76+
}
77+
78+
AbstractSpan span = ContextManager.createLocalSpan(COROUTINE_OPERATION);
79+
span.setComponent(ComponentsDefine.KT_COROUTINE);
80+
// Recover with snapshot
81+
ContextManager.continued(snapshot);
82+
return span;
83+
}
84+
85+
return null;
86+
}
87+
}

0 commit comments

Comments
 (0)