Skip to content

Commit 065a6f3

Browse files
authored
Merge pull request #35 from newrelic/support_1.9
updated to support Kotlin Coroutines 1.9
2 parents bd8baa3 + 93cb817 commit 065a6f3

26 files changed

+1264
-0
lines changed

Kotlin-Coroutines_1.9/build.gradle

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
2+
// Build.gradle generated for instrumentation module Kotlin-Coroutines_1.2
3+
4+
apply plugin: 'java'
5+
6+
targetCompatibility = JavaVersion.VERSION_1_9
7+
8+
dependencies {
9+
implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: '1.9.0'
10+
11+
// New Relic Java Agent dependencies
12+
implementation 'com.newrelic.agent.java:newrelic-agent:6.0.0'
13+
implementation 'com.newrelic.agent.java:newrelic-api:6.0.0'
14+
implementation fileTree(include: ['*.jar'], dir: '../libs')
15+
}
16+
17+
jar {
18+
manifest {
19+
attributes 'Implementation-Title': 'com.newrelic.instrumentation.labs.Kotlin-Coroutines_1.9'
20+
attributes 'Implementation-Vendor': 'New Relic Labs'
21+
attributes 'Implementation-Vendor-Id': 'com.newrelic.labs'
22+
attributes 'Implementation-Version': 2.0
23+
}
24+
}
25+
26+
verifyInstrumentation {
27+
passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core:[1.9.0,)]'
28+
passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:[1.9.0,)'
29+
excludeRegex '.*SNAPSHOT'
30+
excludeRegex '.*alpha'
31+
excludeRegex '.*-eap-.*'
32+
excludeRegex '.*-native-.*'
33+
excludeRegex '.*-M[0-9]'
34+
excludeRegex '.*-rc'
35+
excludeRegex '.*-RC'
36+
excludeRegex '.*-RC[0-9]'
37+
excludeRegex '.*-Beta'
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines_17;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.logging.Level;
6+
7+
import com.newrelic.api.agent.Config;
8+
import com.newrelic.api.agent.NewRelic;
9+
10+
public class DispatchedTaskIgnores {
11+
12+
private static List<String> ignoredTasks = new ArrayList<>();
13+
private static final String DISPATCHEDIGNORECONFIG = "Coroutines.ignores.dispatched";
14+
15+
static {
16+
Config config = NewRelic.getAgent().getConfig();
17+
String ignores = config.getValue(DISPATCHEDIGNORECONFIG);
18+
configure(ignores);
19+
20+
}
21+
22+
23+
public static boolean ignoreDispatchedTask(String contString) {
24+
return ignoredTasks.contains(contString);
25+
}
26+
27+
public static void addIgnore(String ignore) {
28+
if(!ignoredTasks.contains(ignore)) {
29+
ignoredTasks.add(ignore);
30+
NewRelic.getAgent().getLogger().log(Level.FINE, "Will ignore DispatchedTasks with continuation string {0}", ignore);
31+
}
32+
}
33+
34+
public static void reset() {
35+
ignoredTasks.clear();
36+
}
37+
38+
protected static void configure(String result) {
39+
if(result == null || result.isEmpty()) return;
40+
String[] ignores = result.split(",");
41+
for(String ignore : ignores) {
42+
addIgnore(ignore);
43+
}
44+
}
45+
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines_17;
2+
3+
import com.newrelic.agent.bridge.AgentBridge;
4+
import com.newrelic.api.agent.NewRelic;
5+
import com.newrelic.api.agent.Token;
6+
import com.newrelic.api.agent.Trace;
7+
8+
import kotlin.coroutines.Continuation;
9+
import kotlin.coroutines.CoroutineContext;
10+
11+
public class NRContinuationWrapper<T> implements Continuation<T> {
12+
13+
private Continuation<T> delegate = null;
14+
private String name = null;
15+
private static boolean isTransformed = false;
16+
17+
public NRContinuationWrapper(Continuation<T> d, String n) {
18+
delegate = d;
19+
name = n;
20+
if(!isTransformed) {
21+
AgentBridge.instrumentation.retransformUninstrumentedClass(getClass());
22+
isTransformed = true;
23+
}
24+
}
25+
26+
@Override
27+
public CoroutineContext getContext() {
28+
return delegate.getContext();
29+
}
30+
31+
@Override
32+
@Trace(async=true)
33+
public void resumeWith(Object p0) {
34+
String contString = Utils.getContinuationString(delegate);
35+
if(contString != null && !contString.isEmpty()) {
36+
NewRelic.getAgent().getTracedMethod().setMetricName("Custom","ContinuationWrapper","resumeWith",contString);
37+
} else if(name != null) {
38+
NewRelic.getAgent().getTracedMethod().setMetricName("Custom","ContinuationWrapper","resumeWith",name);
39+
} else {
40+
NewRelic.getAgent().getTracedMethod().setMetricName("Custom","ContinuationWrapper","resumeWith",p0.getClass().getName());
41+
}
42+
Token t = Utils.getToken(getContext());
43+
if(t != null) {
44+
t.link();
45+
}
46+
if(delegate != null) {
47+
delegate.resumeWith(p0);
48+
}
49+
}
50+
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines_17;
2+
3+
import com.newrelic.api.agent.Token;
4+
5+
import kotlin.coroutines.AbstractCoroutineContextElement;
6+
import kotlin.coroutines.CoroutineContext;
7+
8+
public class NRCoroutineToken extends AbstractCoroutineContextElement
9+
{
10+
public static Key key = new Key();
11+
12+
public NRCoroutineToken(Token t) {
13+
super(key);
14+
token = t;
15+
}
16+
17+
private Token token = null;
18+
19+
public static final class Key implements CoroutineContext.Key<NRCoroutineToken> {
20+
private Key() {}
21+
}
22+
23+
public Token getToken() {
24+
return token;
25+
}
26+
27+
@Override
28+
public int hashCode() {
29+
return token.hashCode();
30+
}
31+
32+
@Override
33+
public boolean equals(Object obj) {
34+
if(this != obj ) {
35+
if(obj instanceof NRCoroutineToken) {
36+
NRCoroutineToken t = (NRCoroutineToken)obj;
37+
return t.token == token;
38+
}
39+
} else {
40+
return true;
41+
}
42+
return false;
43+
}
44+
45+
@Override
46+
public String toString() {
47+
return "NRCoroutineToken";
48+
}
49+
50+
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines_17;
2+
3+
import com.newrelic.agent.bridge.AgentBridge;
4+
5+
import kotlin.coroutines.Continuation;
6+
import kotlin.coroutines.jvm.internal.SuspendFunction;
7+
import kotlin.jvm.functions.Function1;
8+
9+
public class NRFunction1Wrapper<P1, R> implements Function1<P1, R> {
10+
11+
private Function1<P1, R> delegate = null;
12+
private static boolean isTransformed = false;
13+
14+
public NRFunction1Wrapper(Function1<P1,R> d) {
15+
delegate = d;
16+
if(!isTransformed) {
17+
AgentBridge.instrumentation.retransformUninstrumentedClass(getClass());
18+
isTransformed = true;
19+
}
20+
}
21+
22+
@SuppressWarnings({ "rawtypes", "unchecked" })
23+
@Override
24+
public R invoke(P1 p1) {
25+
if(p1 instanceof Continuation && !(p1 instanceof SuspendFunction)) {
26+
// wrap if needed
27+
if(!(p1 instanceof NRContinuationWrapper)) {
28+
String cont_string = Utils.getContinuationString((Continuation)p1);
29+
NRContinuationWrapper wrapper = new NRContinuationWrapper<>((Continuation)p1, cont_string);
30+
p1 = (P1) wrapper;
31+
}
32+
}
33+
return delegate != null ? delegate.invoke(p1) : null;
34+
}
35+
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines_17;
2+
3+
import com.newrelic.agent.bridge.AgentBridge;
4+
5+
import kotlin.coroutines.Continuation;
6+
import kotlin.coroutines.jvm.internal.SuspendFunction;
7+
import kotlin.jvm.functions.Function2;
8+
9+
public class NRFunction2Wrapper<P1, P2, R> implements Function2<P1, P2, R> {
10+
11+
private Function2<P1, P2, R> delegate = null;
12+
private static boolean isTransformed = false;
13+
14+
public NRFunction2Wrapper(Function2<P1, P2, R> d) {
15+
delegate = d;
16+
if(!isTransformed) {
17+
isTransformed = true;
18+
AgentBridge.instrumentation.retransformUninstrumentedClass(getClass());
19+
}
20+
}
21+
22+
@SuppressWarnings({ "rawtypes", "unchecked" })
23+
@Override
24+
public R invoke(P1 p1, P2 p2) {
25+
26+
if(p2 instanceof Continuation && !(p2 instanceof SuspendFunction)) {
27+
// wrap if needed
28+
String cont_string = Utils.getContinuationString((Continuation)p2);
29+
if(!(p2 instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(cont_string)) {
30+
NRContinuationWrapper wrapper = new NRContinuationWrapper<>((Continuation)p2, cont_string);
31+
p2 = (P2) wrapper;
32+
}
33+
}
34+
if(delegate != null) {
35+
return delegate.invoke(p1, p2);
36+
}
37+
return null;
38+
}
39+
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines_17;
2+
3+
import com.newrelic.agent.bridge.AgentBridge;
4+
import com.newrelic.api.agent.NewRelic;
5+
import com.newrelic.api.agent.Token;
6+
import com.newrelic.api.agent.Trace;
7+
8+
import kotlin.coroutines.Continuation;
9+
import kotlinx.coroutines.DispatchedTask;
10+
11+
public class NRRunnable implements Runnable {
12+
13+
private Runnable delegate = null;
14+
private Token token = null;
15+
private static boolean isTransformed = false;
16+
17+
public NRRunnable(Runnable r,Token t) {
18+
if(!isTransformed) {
19+
AgentBridge.instrumentation.retransformUninstrumentedClass(getClass());
20+
isTransformed = true;
21+
}
22+
delegate = r;
23+
token = t;
24+
}
25+
26+
@Override
27+
@Trace(async = true)
28+
public void run() {
29+
boolean nameSet = false;
30+
if(delegate != null && delegate instanceof DispatchedTask) {
31+
DispatchedTask<?> task = (DispatchedTask<?>)delegate;
32+
Continuation<?> cont_delegate = task.getDelegate$kotlinx_coroutines_core();
33+
if(cont_delegate != null) {
34+
String cont_string = Utils.getContinuationString(cont_delegate);
35+
if(cont_string == null) cont_string = cont_delegate.getClass().getName();
36+
NewRelic.getAgent().getTracedMethod().setMetricName("Custom","DispatchedTask",Utils.getContinuationString(cont_delegate));
37+
nameSet = true;
38+
}
39+
}
40+
if(!nameSet) {
41+
String delegateType = delegate != null ? delegate.getClass().getName() : "null";
42+
NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","AsyncRunnableWrapper",delegateType);
43+
}
44+
if(token != null) {
45+
token.linkAndExpire();
46+
token = null;
47+
}
48+
if(delegate != null) {
49+
delegate.run();
50+
}
51+
}
52+
53+
}

0 commit comments

Comments
 (0)