From 2adaf60209aea31626c230baacb2b7583138b2f7 Mon Sep 17 00:00:00 2001 From: Doug Hilpipre Date: Thu, 1 Aug 2024 15:13:28 -0500 Subject: [PATCH 1/3] added ignores for CoroutineScope to ignore selected Coroutines, needed for Ktor instrumentation --- .../kotlin/coroutines_14/Utils.java | 31 ++++- .../java/kotlinx/coroutines/BuildersKt.java | 110 +++++++++++------- .../kotlin/coroutines_15/Utils.java | 31 ++++- .../java/kotlinx/coroutines/BuildersKt.java | 110 +++++++++++------- .../coroutines_17/NRContinuationWrapper.java | 6 +- .../kotlin/coroutines_17/Utils.java | 29 +++++ .../java/kotlinx/coroutines/BuildersKt.java | 110 +++++++++++------- .../coroutines/DispatcherExecutor.java | 2 + 8 files changed, 307 insertions(+), 122 deletions(-) diff --git a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/Utils.java b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/Utils.java index b2e4ec0..52945da 100644 --- a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/Utils.java +++ b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/Utils.java @@ -16,12 +16,15 @@ import kotlin.coroutines.jvm.internal.BaseContinuationImpl; import kotlinx.coroutines.AbstractCoroutine; import kotlinx.coroutines.CoroutineName; +import kotlinx.coroutines.CoroutineScope; import kotlinx.coroutines.DispatchedTask; public class Utils implements AgentConfigListener { private static final List ignoredContinuations = new ArrayList(); + private static final List ignoredScopes = new ArrayList<>(); private static final String CONTIGNORECONFIG = "Coroutines.ignores.continuations"; + private static final String SCOPESIGNORECONFIG = "Coroutines.ignores.scopes"; private static final String DISPATCHEDIGNORECONFIG = "Coroutines.ignores.dispatched"; private static final String DELAYED_ENABLED_CONFIG = "Coroutines.delayed.enabled"; @@ -95,6 +98,32 @@ private static void loadConfig(Config config) { if (ignores != null && !ignores.isEmpty()) { DispatchedTaskIgnores.configure(ignores); } + ignores = config.getValue(SCOPESIGNORECONFIG); + if (ignores != null && !ignores.isEmpty()) { + ignoredScopes.clear(); + String[] ignoresList = ignores.split(","); + + for(String ignore : ignoresList) { + if (!ignoredScopes.contains(ignore)) { + ignoredScopes.add(ignore); + NewRelic.getAgent().getLogger().log(Level.FINE, "Will ignore CoroutineScopes named {0}", ignore); + } + } + } else if(!ignoredScopes.isEmpty()) { + ignoredScopes.clear(); + } + + } + + public static boolean ignoreScope(CoroutineScope scope) { + CoroutineContext ctx = scope.getCoroutineContext(); + String name = getCoroutineName(ctx); + String className = scope.getClass().getName(); + return ignoreScope(className) || ignoreScope(name); + } + + public static boolean ignoreScope(String coroutineScope) { + return ignoredScopes.contains(coroutineScope); } public static boolean ignoreContinuation(String cont_string) { @@ -193,4 +222,4 @@ public static String getContinuationString(Continuation continuation) { return null; } -} +} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/BuildersKt.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/BuildersKt.java index 3814d52..c7a319f 100644 --- a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/BuildersKt.java +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/BuildersKt.java @@ -1,6 +1,7 @@ package kotlinx.coroutines; import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; @@ -21,9 +22,14 @@ public class BuildersKt { @Trace(dispatcher = true) public static final T runBlocking(CoroutineContext context, Function2, ? extends Object> block) { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); + Token token = Utils.getToken(context); + if(token != null) { + token.link(); + } else { + NRCoroutineToken nrContextToken = Utils.setToken(context); + if(nrContextToken != null) { + context = context.plus(nrContextToken); + } } String name = Utils.getCoroutineName(context); if(name != null) { @@ -43,23 +49,32 @@ public static final T runBlocking(CoroutineContext context, Function2 Deferred async(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { - String name = Utils.getCoroutineName(context); - if(name == null) { - name = Utils.getCoroutineName(scope.getCoroutineContext()); - } - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async",name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async"); - } + if (!Utils.ignoreScope(scope)) { + String name = Utils.getCoroutineName(context); + if(name == null) { + name = Utils.getCoroutineName(scope.getCoroutineContext()); + } + if(name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async",name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async"); + } - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; + Token token = Utils.getToken(context); + if(token != null) { + token.link(); + } else { + NRCoroutineToken nrContextToken = Utils.setToken(context); + if(nrContextToken != null) { + context = context.plus(nrContextToken); + } + } + if(!(block instanceof NRFunction2Wrapper)) { + NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); + block = wrapper; + } + } else { + NewRelic.getAgent().getTransaction().ignore(); } return Weaver.callOriginal(); } @@ -86,24 +101,36 @@ public static final Object invoke(CoroutineDispatcher dispatcher, Function2< @Trace(dispatcher = true) public static final kotlinx.coroutines.Job launch(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); - String name = Utils.getCoroutineName(context); - if(name == null) { - name = Utils.getCoroutineName(scope.getCoroutineContext()); - } - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","launch",name); + if (!Utils.ignoreScope(scope)) { + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); + + String name = Utils.getCoroutineName(context); + if (name == null) { + name = Utils.getCoroutineName(scope.getCoroutineContext()); + } + if (name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch", name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch"); + } + NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); + Token token = Utils.getToken(context); + if(token != null) { + token.link(); + } else { + NRCoroutineToken nrContextToken = Utils.setToken(context); + if(nrContextToken != null) { + context = context.plus(nrContextToken); + } + } + if (!(block instanceof NRFunction2Wrapper)) { + NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper( + block); + block = wrapper; + } } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","launch"); - } - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; + NewRelic.getAgent().getTransaction().ignore(); } Job j = Weaver.callOriginal(); return j; @@ -121,9 +148,14 @@ public static final Object withContext(CoroutineContext context,Function2, ? extends Object> wrapper = new NRFunction2Wrapper(block); diff --git a/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/Utils.java b/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/Utils.java index aa01f7e..796f371 100644 --- a/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/Utils.java +++ b/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/Utils.java @@ -16,12 +16,15 @@ import kotlin.coroutines.jvm.internal.BaseContinuationImpl; import kotlinx.coroutines.AbstractCoroutine; import kotlinx.coroutines.CoroutineName; +import kotlinx.coroutines.CoroutineScope; import kotlinx.coroutines.DispatchedTask; public class Utils implements AgentConfigListener { private static final List ignoredContinuations = new ArrayList(); + private static final List ignoredScopes = new ArrayList<>(); private static final String CONTIGNORECONFIG = "Coroutines.ignores.continuations"; + private static final String SCOPESIGNORECONFIG = "Coroutines.ignores.scopes"; private static final String DISPATCHEDIGNORECONFIG = "Coroutines.ignores.dispatched"; private static final String DELAYED_ENABLED_CONFIG = "Coroutines.delayed.enabled"; @@ -95,6 +98,32 @@ private static void loadConfig(Config config) { if (ignores != null && !ignores.isEmpty()) { DispatchedTaskIgnores.configure(ignores); } + ignores = config.getValue(SCOPESIGNORECONFIG); + if (ignores != null && !ignores.isEmpty()) { + ignoredScopes.clear(); + String[] ignoresList = ignores.split(","); + + for(String ignore : ignoresList) { + if (!ignoredScopes.contains(ignore)) { + ignoredScopes.add(ignore); + NewRelic.getAgent().getLogger().log(Level.FINE, "Will ignore CoroutineScopes named {0}", ignore); + } + } + } else if(!ignoredScopes.isEmpty()) { + ignoredScopes.clear(); + } + + } + + public static boolean ignoreScope(CoroutineScope scope) { + CoroutineContext ctx = scope.getCoroutineContext(); + String name = getCoroutineName(ctx); + String className = scope.getClass().getName(); + return ignoreScope(className) || ignoreScope(name); + } + + public static boolean ignoreScope(String coroutineScope) { + return ignoredScopes.contains(coroutineScope); } public static boolean ignoreContinuation(String cont_string) { @@ -193,4 +222,4 @@ public static String getContinuationString(Continuation continuation) { return null; } -} +} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/BuildersKt.java b/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/BuildersKt.java index 02bc13f..f2fad92 100644 --- a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/BuildersKt.java +++ b/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/BuildersKt.java @@ -1,6 +1,7 @@ package kotlinx.coroutines; import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; @@ -21,9 +22,14 @@ public class BuildersKt { @Trace(dispatcher = true) public static final T runBlocking(CoroutineContext context, Function2, ? extends Object> block) { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); + Token token = Utils.getToken(context); + if(token != null) { + token.link(); + } else { + NRCoroutineToken nrContextToken = Utils.setToken(context); + if(nrContextToken != null) { + context = context.plus(nrContextToken); + } } String name = Utils.getCoroutineName(context); if(name != null) { @@ -43,23 +49,32 @@ public static final T runBlocking(CoroutineContext context, Function2 Deferred async(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { - String name = Utils.getCoroutineName(context); - if(name == null) { - name = Utils.getCoroutineName(scope.getCoroutineContext()); - } - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async",name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async"); - } + if (!Utils.ignoreScope(scope)) { + String name = Utils.getCoroutineName(context); + if(name == null) { + name = Utils.getCoroutineName(scope.getCoroutineContext()); + } + if(name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async",name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async"); + } - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; + Token token = Utils.getToken(context); + if(token != null) { + token.link(); + } else { + NRCoroutineToken nrContextToken = Utils.setToken(context); + if(nrContextToken != null) { + context = context.plus(nrContextToken); + } + } + if(!(block instanceof NRFunction2Wrapper)) { + NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); + block = wrapper; + } + } else { + NewRelic.getAgent().getTransaction().ignore(); } return Weaver.callOriginal(); } @@ -86,24 +101,36 @@ public static final Object invoke(CoroutineDispatcher dispatcher, Function2< @Trace(dispatcher = true) public static final kotlinx.coroutines.Job launch(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); - String name = Utils.getCoroutineName(context); - if(name == null) { - name = Utils.getCoroutineName(scope.getCoroutineContext()); - } - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","launch",name); + if (!Utils.ignoreScope(scope)) { + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); + + String name = Utils.getCoroutineName(context); + if (name == null) { + name = Utils.getCoroutineName(scope.getCoroutineContext()); + } + if (name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch", name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch"); + } + NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); + Token token = Utils.getToken(context); + if(token != null) { + token.link(); + } else { + NRCoroutineToken nrContextToken = Utils.setToken(context); + if(nrContextToken != null) { + context = context.plus(nrContextToken); + } + } + if (!(block instanceof NRFunction2Wrapper)) { + NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper( + block); + block = wrapper; + } } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","launch"); - } - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; + NewRelic.getAgent().getTransaction().ignore(); } Job j = Weaver.callOriginal(); return j; @@ -121,9 +148,14 @@ public static final Object withContext(CoroutineContext context,Function2, ? extends Object> wrapper = new NRFunction2Wrapper(block); diff --git a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRContinuationWrapper.java b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRContinuationWrapper.java index 966d29f..5a016fa 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRContinuationWrapper.java +++ b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRContinuationWrapper.java @@ -9,11 +9,11 @@ import kotlin.coroutines.CoroutineContext; public class NRContinuationWrapper implements Continuation { - + private Continuation delegate = null; private String name = null; private static boolean isTransformed = false; - + public NRContinuationWrapper(Continuation d, String n) { delegate = d; name = n; @@ -22,7 +22,7 @@ public NRContinuationWrapper(Continuation d, String n) { isTransformed = true; } } - + @Override public CoroutineContext getContext() { return delegate.getContext(); diff --git a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/Utils.java b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/Utils.java index 06d8992..94850ff 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/Utils.java +++ b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/Utils.java @@ -16,12 +16,15 @@ import kotlin.coroutines.jvm.internal.BaseContinuationImpl; import kotlinx.coroutines.AbstractCoroutine; import kotlinx.coroutines.CoroutineName; +import kotlinx.coroutines.CoroutineScope; import kotlinx.coroutines.DispatchedTask; public class Utils implements AgentConfigListener { private static final List ignoredContinuations = new ArrayList(); + private static final List ignoredScopes = new ArrayList<>(); private static final String CONTIGNORECONFIG = "Coroutines.ignores.continuations"; + private static final String SCOPESIGNORECONFIG = "Coroutines.ignores.scopes"; private static final String DISPATCHEDIGNORECONFIG = "Coroutines.ignores.dispatched"; private static final String DELAYED_ENABLED_CONFIG = "Coroutines.delayed.enabled"; @@ -95,6 +98,32 @@ private static void loadConfig(Config config) { if (ignores != null && !ignores.isEmpty()) { DispatchedTaskIgnores.configure(ignores); } + ignores = config.getValue(SCOPESIGNORECONFIG); + if (ignores != null && !ignores.isEmpty()) { + ignoredScopes.clear(); + String[] ignoresList = ignores.split(","); + + for(String ignore : ignoresList) { + if (!ignoredScopes.contains(ignore)) { + ignoredScopes.add(ignore); + NewRelic.getAgent().getLogger().log(Level.FINE, "Will ignore CoroutineScopes named {0}", ignore); + } + } + } else if(!ignoredScopes.isEmpty()) { + ignoredScopes.clear(); + } + + } + + public static boolean ignoreScope(CoroutineScope scope) { + CoroutineContext ctx = scope.getCoroutineContext(); + String name = getCoroutineName(ctx); + String className = scope.getClass().getName(); + return ignoreScope(className) || ignoreScope(name); + } + + public static boolean ignoreScope(String coroutineScope) { + return ignoredScopes.contains(coroutineScope); } public static boolean ignoreContinuation(String cont_string) { diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/BuildersKt.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/BuildersKt.java index a7470fc..0faa2bc 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/BuildersKt.java +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/BuildersKt.java @@ -1,6 +1,7 @@ package kotlinx.coroutines; import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; @@ -21,9 +22,14 @@ public class BuildersKt { @Trace(dispatcher = true) public static final T runBlocking(CoroutineContext context, Function2, ? extends Object> block) { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); + Token token = Utils.getToken(context); + if(token != null) { + token.link(); + } else { + NRCoroutineToken nrContextToken = Utils.setToken(context); + if(nrContextToken != null) { + context = context.plus(nrContextToken); + } } String name = Utils.getCoroutineName(context); if(name != null) { @@ -43,23 +49,32 @@ public static final T runBlocking(CoroutineContext context, Function2 Deferred async(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { - String name = Utils.getCoroutineName(context); - if(name == null) { - name = Utils.getCoroutineName(scope.getCoroutineContext()); - } - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async",name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async"); - } + if (!Utils.ignoreScope(scope)) { + String name = Utils.getCoroutineName(context); + if(name == null) { + name = Utils.getCoroutineName(scope.getCoroutineContext()); + } + if(name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async",name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async"); + } - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; + Token token = Utils.getToken(context); + if(token != null) { + token.link(); + } else { + NRCoroutineToken nrContextToken = Utils.setToken(context); + if(nrContextToken != null) { + context = context.plus(nrContextToken); + } + } + if(!(block instanceof NRFunction2Wrapper)) { + NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); + block = wrapper; + } + } else { + NewRelic.getAgent().getTransaction().ignore(); } return Weaver.callOriginal(); } @@ -86,24 +101,36 @@ public static final Object invoke(CoroutineDispatcher dispatcher, Function2< @Trace(dispatcher = true) public static final kotlinx.coroutines.Job launch(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); - String name = Utils.getCoroutineName(context); - if(name == null) { - name = Utils.getCoroutineName(scope.getCoroutineContext()); - } - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","launch",name); + if (!Utils.ignoreScope(scope)) { + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); + + String name = Utils.getCoroutineName(context); + if (name == null) { + name = Utils.getCoroutineName(scope.getCoroutineContext()); + } + if (name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch", name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch"); + } + NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); + Token token = Utils.getToken(context); + if(token != null) { + token.link(); + } else { + NRCoroutineToken nrContextToken = Utils.setToken(context); + if(nrContextToken != null) { + context = context.plus(nrContextToken); + } + } + if (!(block instanceof NRFunction2Wrapper)) { + NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper( + block); + block = wrapper; + } } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","launch"); - } - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; + NewRelic.getAgent().getTransaction().ignore(); } Job j = Weaver.callOriginal(); return j; @@ -121,9 +148,14 @@ public static final Object withContext(CoroutineContext context,Function2, ? extends Object> wrapper = new NRFunction2Wrapper(block); diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/DispatcherExecutor.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/DispatcherExecutor.java index ce10fb1..034b646 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/DispatcherExecutor.java +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/DispatcherExecutor.java @@ -1,6 +1,7 @@ package kotlinx.coroutines; import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; @@ -10,6 +11,7 @@ @Weave(type = MatchType.BaseClass) abstract class DispatcherExecutor { + @Trace public void execute(Runnable r) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","DispatcherExecutor","execute"); NRRunnable wrapper = Utils.getRunnableWrapper(r); From 7bbffa89a2ee723a5697b1e4e3825e720902a684 Mon Sep 17 00:00:00 2001 From: Doug Hilpipre Date: Fri, 2 Aug 2024 07:12:22 -0500 Subject: [PATCH 2/3] updates to span attributes --- .../src/main/java/kotlinx/coroutines/BuildersKt.java | 2 ++ .../src/main/java/kotlinx/coroutines/BuildersKt.java | 8 ++++++++ .../src/main/java/kotlinx/coroutines/BuildersKt.java | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/BuildersKt.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/BuildersKt.java index c7a319f..0c060d1 100644 --- a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/BuildersKt.java +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/BuildersKt.java @@ -111,8 +111,10 @@ public static final kotlinx.coroutines.Job launch(CoroutineScope scope, Coroutin } if (name != null) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch", name); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); } else { NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch"); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); } NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); Token token = Utils.getToken(context); diff --git a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/BuildersKt.java b/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/BuildersKt.java index f2fad92..0efc143 100644 --- a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/BuildersKt.java +++ b/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/BuildersKt.java @@ -34,8 +34,10 @@ public static final T runBlocking(CoroutineContext context, Function2 T runBlocking(CoroutineContext context, Function2 Deferred async(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { if (!Utils.ignoreScope(scope)) { + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); String name = Utils.getCoroutineName(context); if(name == null) { name = Utils.getCoroutineName(scope.getCoroutineContext()); } if(name != null) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async",name); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); } else { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async"); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); } Token token = Utils.getToken(context); @@ -111,8 +117,10 @@ public static final kotlinx.coroutines.Job launch(CoroutineScope scope, Coroutin } if (name != null) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch", name); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); } else { NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch"); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); } NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); Token token = Utils.getToken(context); diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/BuildersKt.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/BuildersKt.java index 0faa2bc..9f2ac41 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/BuildersKt.java +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/BuildersKt.java @@ -50,14 +50,18 @@ public static final T runBlocking(CoroutineContext context, Function2 Deferred async(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { if (!Utils.ignoreScope(scope)) { + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); String name = Utils.getCoroutineName(context); if(name == null) { name = Utils.getCoroutineName(scope.getCoroutineContext()); } if(name != null) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async",name); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); } else { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async"); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); } Token token = Utils.getToken(context); @@ -111,8 +115,10 @@ public static final kotlinx.coroutines.Job launch(CoroutineScope scope, Coroutin } if (name != null) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch", name); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); } else { NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch"); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); } NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); Token token = Utils.getToken(context); From c294d4b320097973d68c5f18895cdc457674a774 Mon Sep 17 00:00:00 2001 From: Doug Hilpipre Date: Fri, 2 Aug 2024 08:00:12 -0500 Subject: [PATCH 3/3] Update README.md --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 5b71ce3..0131a9a 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,10 @@ The following things are captured as part of the instrumentation Instrumentation of methods with high invocation rates can lead to CPU overhead especially if its average response time is very small (i.e. less than a few milliseconds). Therefore it is possible to configure the agent to ignore certain suspend methods, dispatched tasks and continuation resumeWiths. This configuation is done in the newrelic.yml file. +### Finding Coroutine Scopes to Ignore +This is basically meant for Standalone Coroutines that you don't want to track for some reason such as it is a long running task that doesn't need to be tracked. Lazy Coroutines are a good example. If the agent encounters that scope it will stop tracing that transaction, hence if you disable a scope that is part of another transaction rather than just itself it will also disable that transaction. But the configuration is dynamic so you can remove to restore the transaction. To find the value to use for the Coroutine Scope to ignore go into the transaction trace and select the "Custom/Builders/launch" or "Custom/Builders/async" span. In the Attributes tab find CoroutineScope-Class for the value to use as shown below. +image + ### Finding Possible Methods to Ignore Run the following NRQL query where appName is the name of the application using Kotlin Coroutines. @@ -90,6 +94,9 @@ To configure methods to ignore, edit the newrelic.yml file in the New Relic Java Note that these setting are dynamic, so typically the agent should pick up changes within a minute or so and implement the changes without having to restart. +### Configuring Scopes to Ignore +Similar to configuring the method to ignore above except add a line scopes: to the configuration as shown: +image ## Building