diff --git a/instrumentation/apache-pekko-1/REAME.md b/instrumentation/apache-pekko-1/REAME.md new file mode 100644 index 0000000000..bdb0c88b0c --- /dev/null +++ b/instrumentation/apache-pekko-1/REAME.md @@ -0,0 +1,5 @@ +## Apache Pekko Instrumentation + +Apache Pekko is a fork of Akka 2.6. +This instrumentation is a direct lift of the existing instrumentation module `akka-2.2`. + diff --git a/instrumentation/apache-pekko-1/build.gradle b/instrumentation/apache-pekko-1/build.gradle new file mode 100644 index 0000000000..b7de87582d --- /dev/null +++ b/instrumentation/apache-pekko-1/build.gradle @@ -0,0 +1,19 @@ +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.apache-pekko-1'} +} + +dependencies { + implementation(project(":agent-bridge")) + implementation('org.apache.pekko:pekko-actor_2.13:1.0.0') +} + +verifyInstrumentation { + passes 'org.apache.pekko:pekko-actor_2.13:[1.0.0,)' + passes 'org.apache.pekko:pekko-actor_3:[1.0.0,)' + passes 'org.apache.pekko:pekko-actor_2.12:[1.0.0,)' +} + +site { + title 'Pekko' + type 'Framework' +} \ No newline at end of file diff --git a/instrumentation/apache-pekko-1/src/main/java/com/nr/instrumentation/pekko1/PekkoUtil.java b/instrumentation/apache-pekko-1/src/main/java/com/nr/instrumentation/pekko1/PekkoUtil.java new file mode 100644 index 0000000000..cf12862db7 --- /dev/null +++ b/instrumentation/apache-pekko-1/src/main/java/com/nr/instrumentation/pekko1/PekkoUtil.java @@ -0,0 +1,101 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation.pekko1; + +import org.apache.pekko.actor.ActorRef; +import com.newrelic.agent.bridge.AgentBridge; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class PekkoUtil { + + private static final String tempActorName = "temp"; + + private static final Set tickClasses = new HashSet<>(Arrays.asList( + "org.apache.pekko.remote.RemoteWatcher$Heartbeat$", "org.apache.pekko.remote.RemoteWatcher$HeartbeatTick$", + "org.apache.pekko.remote.RemoteWatcher$ReapUnreachableTick$", "spray.io.TickGenerator$Tick$")); + + // Loggers + private static final String defaultLoggerPrefix = "org.apache.pekko.event.Logging"; + private static final String slf4jLoggerPrefix = "org.apache.pekko.event.slf4j.Slf4jLogger"; + + // Subscriber + private static final String pekkoStreamPrefix = "org.apache.pekko.stream"; + + private static final Pattern allDigitsPattern = Pattern.compile("\\d+"); + private static final String pekkoIOTCPIncomingConnection = "org.apache.pekko.io.TcpIncomingConnection"; + private static final String sprayCanHttpServerConnection = "spray.can.server.HttpServerConnection"; + + public static boolean isHeartBeatMessage(String name) { + return tickClasses.contains(name); + } + + public static boolean isLogger(String receiver) { + if (receiver.startsWith(defaultLoggerPrefix)) { + return true; + } + if (receiver.startsWith(slf4jLoggerPrefix)) { + return true; + } + return false; + } + + public static boolean isTempActor(ActorRef actorRef) { + return actorRef.path().parent().name().equals(tempActorName); + } + + public static boolean isPekkoStream(String messageClassName) { + if (messageClassName.startsWith(pekkoStreamPrefix)) { + return true; + } + return false; + } + + public static String getActor(ActorRef actorRef) { + if (actorRef == null || actorRef.path() == null) { + return ""; + } else if (isTempActor(actorRef)) { + return tempActorName; + } else if (actorRef.path().parent().name().equals("/")) { + // Report actor system name, not deadletters. + // Should be equivalent to actorRef.path().parent().equals(actorRef.path().root() + return actorRef.path().parent().address().system(); + } else { + return actorRef.path().name(); + } + } + + public static void recordTellMetric(String receiverName, String senderName, String messageClass) { + if (senderName == null || receiverName == null) { + return; + } + + if (isHeartBeatMessage(messageClass)) { + return; + } + + if (isLogger(receiverName)) { + return; + } + + Matcher matcher = allDigitsPattern.matcher(senderName); + if (receiverName.equals(pekkoIOTCPIncomingConnection) && matcher.matches()) { + senderName = tempActorName; + } else if (receiverName.equals(sprayCanHttpServerConnection) && matcher.matches()) { + senderName = tempActorName; + } else if (senderName.startsWith("$")) { + senderName = tempActorName; + } + + AgentBridge.getAgent().getTracedMethod().setMetricName("Pekko", senderName, "tell", receiverName); + } +} diff --git a/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/actor/ActorCell_Instrumentation.java b/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/actor/ActorCell_Instrumentation.java new file mode 100644 index 0000000000..543f1e1f7c --- /dev/null +++ b/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/actor/ActorCell_Instrumentation.java @@ -0,0 +1,41 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.apache.pekko.actor; + +import org.apache.pekko.dispatch.Envelope_Instrumentation; +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.TransactionNamePriority; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.pekko1.PekkoUtil; + +@Weave(originalName = "org.apache.pekko.actor.ActorCell") +public abstract class ActorCell_Instrumentation { + @Trace(async = true) + public void invoke(Envelope_Instrumentation envelope) { + String receiver = (actor() == null) ? null : actor().getClass().getName(); + + String messageClassName = envelope.message().getClass().getName(); + if (receiver != null && !PekkoUtil.isHeartBeatMessage(messageClassName) && !PekkoUtil.isLogger(receiver)) { + if (envelope.token != null) { + if (envelope.token.link()) { + AgentBridge.getAgent().getTracedMethod().setMetricName("Pekko", "receive", receiver); + AgentBridge.getAgent().getTransaction().setTransactionName(TransactionNamePriority.FRAMEWORK_LOW, + false, "Actor", receiver, "invoke"); + } + envelope.token.expire(); + envelope.token = null; + } + } + + Weaver.callOriginal(); + } + + public abstract Actor actor(); +} diff --git a/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/actor/ActorRef_Implementation.java b/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/actor/ActorRef_Implementation.java new file mode 100644 index 0000000000..e1cc5089a2 --- /dev/null +++ b/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/actor/ActorRef_Implementation.java @@ -0,0 +1,31 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.apache.pekko.actor; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.pekko1.PekkoUtil; + +@Weave(originalName = "org.apache.pekko.actor.ActorRef") +public abstract class ActorRef_Implementation { + + @Trace + public void forward(Object message, ActorContext context) { + String name = path().name(); + + if (!PekkoUtil.isHeartBeatMessage(message.getClass().getName()) && !PekkoUtil.isLogger(name)) { + AgentBridge.getAgent().getTracedMethod().setMetricName("Pekko", "forward", name); + } + + Weaver.callOriginal(); + } + + public abstract ActorPath path(); +} diff --git a/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/actor/Cell_Instrumentation.java b/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/actor/Cell_Instrumentation.java new file mode 100644 index 0000000000..2c37bbc82f --- /dev/null +++ b/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/actor/Cell_Instrumentation.java @@ -0,0 +1,49 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.apache.pekko.actor; + +import org.apache.pekko.dispatch.Envelope_Instrumentation; +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.Transaction; +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; +import com.nr.instrumentation.pekko1.PekkoUtil; + +@Weave(type = MatchType.Interface, originalName = "org.apache.pekko.actor.Cell") +public abstract class Cell_Instrumentation { + + public abstract ActorRef self(); + + @Trace + public void sendMessage(Envelope_Instrumentation envelope) { + String receiver = props().actorClass().getName(); + String messageClassName = envelope.message().getClass().getName(); + + Transaction transaction = AgentBridge.getAgent().getTransaction(false); + if (transaction != null && transaction.isStarted() && !PekkoUtil.isHeartBeatMessage(messageClassName) && !PekkoUtil.isLogger(receiver) && !PekkoUtil.isPekkoStream(messageClassName)) { + + String sender = PekkoUtil.getActor(envelope.sender()); + PekkoUtil.recordTellMetric(receiver, sender, messageClassName); + + if (envelope.token != null ) { + // Pekko may migrate envelopes to another ActorCell. + // See UnstartedCell in RepointableActorRef.scala. + // We expire and replace the existing token just to be on the safe side. + envelope.token.expire(); + envelope.token = null; // this prevents the warning even though it's immediately reassigned + } + envelope.token = transaction.getToken(); + } + + Weaver.callOriginal(); + } + + public abstract Props props(); +} diff --git a/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/dispatch/Envelope_Instrumentation.java b/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/dispatch/Envelope_Instrumentation.java new file mode 100644 index 0000000000..a47c2491a0 --- /dev/null +++ b/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/dispatch/Envelope_Instrumentation.java @@ -0,0 +1,23 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.apache.pekko.dispatch; + +import org.apache.pekko.actor.ActorRef; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; + +@Weave(originalName = "org.apache.pekko.dispatch.Envelope") +public abstract class Envelope_Instrumentation { + @NewField + public Token token; + + public abstract Object message(); + + public abstract ActorRef sender(); +} diff --git a/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/pattern/AskSupport_Instrumentation.java b/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/pattern/AskSupport_Instrumentation.java new file mode 100644 index 0000000000..4dd860a88f --- /dev/null +++ b/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/pattern/AskSupport_Instrumentation.java @@ -0,0 +1,43 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.apache.pekko.pattern; + +import org.apache.pekko.actor.ActorRef; +import org.apache.pekko.actor.ActorSelection; +import com.newrelic.agent.bridge.AgentBridge; +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; +import com.nr.instrumentation.pekko1.PekkoUtil; + +@Weave(type = MatchType.Interface, originalName = "org.apache.pekko.pattern.AskSupport") +public class AskSupport_Instrumentation { + + @Trace + public ActorRef ask(ActorRef actorRef) { + String receiver = PekkoUtil.getActor(actorRef); + + if (!PekkoUtil.isLogger(receiver)) { + AgentBridge.getAgent().getTracedMethod().setMetricName("Pekko", "ask", receiver); + } + return Weaver.callOriginal(); + } + + @Trace + public ActorSelection ask(ActorSelection actorSelection) { + ActorRef actorRef = actorSelection.anchor(); + String receiver = PekkoUtil.getActor(actorRef); + + if (!PekkoUtil.isLogger(receiver)) { + AgentBridge.getAgent().getTracedMethod().setMetricName("Pekko", "ask", receiver); + } + return Weaver.callOriginal(); + } + +} diff --git a/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/routing/RoutedActorCell_Instrumentation.java b/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/routing/RoutedActorCell_Instrumentation.java new file mode 100644 index 0000000000..d4edbf8f0a --- /dev/null +++ b/instrumentation/apache-pekko-1/src/main/java/org.apache.pekko/routing/RoutedActorCell_Instrumentation.java @@ -0,0 +1,30 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.apache.pekko.routing; + +import org.apache.pekko.dispatch.Envelope_Instrumentation; +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; + +@Weave(type = MatchType.ExactClass, originalName = "org.apache.pekko.routing.RoutedActorCell") +public abstract class RoutedActorCell_Instrumentation { + + @Trace + public void sendMessage(Envelope_Instrumentation envelope) { + Weaver.callOriginal(); + + // The "RoutedActorCell" does not act like other actors, we need to expire the token on the envelope to prevent a token timeout + if (envelope.token != null) { + envelope.token.expire(); + envelope.token = null; + } + } + +} diff --git a/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/PekkoTest.java b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/PekkoTest.java new file mode 100644 index 0000000000..e2422530b5 --- /dev/null +++ b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/PekkoTest.java @@ -0,0 +1,142 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation.pekko1.test; + +import org.apache.pekko.actor.ActorSystem; +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TracedMetricData; +import com.newrelic.test.marker.Java17IncompatibleTest; +import com.newrelic.test.marker.Java21IncompatibleTest; +import com.nr.instrumentation.pekko1.test.actors.broadcasting.ActorA; +import com.nr.instrumentation.pekko1.test.actors.broadcasting.ActorB; +import com.nr.instrumentation.pekko1.test.actors.broadcasting.ActorC; +import com.nr.instrumentation.pekko1.test.actors.broadcasting.branches.ActorNoTxnBranch; +import com.nr.instrumentation.pekko1.test.actors.forwarding.ForwardActor; +import com.nr.instrumentation.pekko1.test.actors.routing.Routee; +import com.nr.instrumentation.pekko1.test.actors.routing.RoutingActor; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; + +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +// Not compatible with Java 11+ and Scala 2.13+ https://github.com/scala/bug/issues/12340 +@Category({ Java17IncompatibleTest.class, Java21IncompatibleTest.class }) +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = {"org.apache.pekko.actor", "org.apache.pekko.dispatch", "org.apache.pekko.pattern", "org.apache.pekko.routing"}) +public class PekkoTest { + + @Test + public void testSend() throws InterruptedException { + ActorSystem system = ActorSystem.create("PekkoTestSystem"); + TestApp.sendMessageInTransaction(system); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + assertEquals(1, finishedTransactionCount); + String transactionName = "OtherTransaction/PekkoForward/Forward"; + Map metricsForTransaction = introspector.getMetricsForTransaction(transactionName); + assertTrue(metricsForTransaction.containsKey("Pekko/forward/forwardActor")); + assertTrue(metricsForTransaction.containsKey("Pekko/receive/" + ForwardActor.class.getName())); + } + + @Test + public void testBroadcast() throws InterruptedException { + ActorSystem system = ActorSystem.create("PekkoTestSystem"); + TestApp.broadcastInTransaction(system); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + assertEquals(1, finishedTransactionCount); + String transactionName = "OtherTransaction/Pekko/Broadcast"; + Map metricsForTransaction = introspector.getMetricsForTransaction(transactionName); + assertTrue(metricsForTransaction.containsKey("Pekko/receive/" + ActorA.class.getName())); + assertTrue(metricsForTransaction.containsKey("Pekko/receive/" + ActorB.class.getName())); + assertTrue(metricsForTransaction.containsKey("Pekko/receive/" + ActorC.class.getName())); + + Map unscopedMetrics = introspector.getUnscopedMetrics(); + assertTrue(unscopedMetrics.containsKey("ActorA")); + assertTrue(unscopedMetrics.containsKey("ActorB")); + assertTrue(unscopedMetrics.containsKey("ActorC")); + } + + @Test + public void testBroadcastStress() throws InterruptedException { + /** + * @formatter:off + * + * This test tries to cover the following case: + * + * Suppose System broadcasts a message to A and B. B forwards the same message to C. + * + * System + * / \ + * A B @Trace(dispatcher = true) + * | + * C + * + * + * A possible execution of this can be as follows: + * + * A is busy, and does not read the message. + * B starts the transaction, and forwards message to C + * A wakes up, and reads message. + * C reads message. + * + * We want to ensure that A never links to B. + * + * To increase the likelihood of running into the execution above, we have actor A forward the same message to itself several times. + * We do the same thing for actor C. + */ + + ActorSystem system = ActorSystem.create("PekkoTestSystem"); + TestApp.broadcastStress(system); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + assertEquals(1, finishedTransactionCount); + assertTrue(introspector.getTransactionNames().contains("OtherTransaction/Pekko/ParentActor")); + + Map unscopedMetrics = introspector.getUnscopedMetrics(); + assertFalse(unscopedMetrics.containsKey(ActorNoTxnBranch.ROLLUP_NAME)); + } + + @Test + public void testRoutedActorSend() throws InterruptedException { + ActorSystem system = ActorSystem.create("PekkoTestSystem"); + TestApp.sendRoutedMessageInTransaction(system); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + assertEquals(1, finishedTransactionCount); + String transactionName = "OtherTransaction/Pekko/Routing"; + Map metricsForTransaction = introspector.getMetricsForTransaction(transactionName); + assertTrue(metricsForTransaction.containsKey("Pekko/receive/" + RoutingActor.class.getName())); + } + + @Test + public void testRoutedActorToRouteeSend() throws InterruptedException { + ActorSystem system = ActorSystem.create("PekkoTestSystem"); + TestApp.sendRoutedMessageToRouteeInTransaction(system); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + assertEquals(1, finishedTransactionCount); + String transactionName = "OtherTransaction/Pekko/Routee"; + Map metricsForTransaction = introspector.getMetricsForTransaction(transactionName); + assertTrue(metricsForTransaction.containsKey("Pekko/receive/" + RoutingActor.class.getName())); + assertTrue(metricsForTransaction.containsKey("Pekko/receive/" + Routee.class.getName())); + } + +} diff --git a/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/TestApp.java b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/TestApp.java new file mode 100644 index 0000000000..adec9261d7 --- /dev/null +++ b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/TestApp.java @@ -0,0 +1,74 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation.pekko1.test; + +import org.apache.pekko.actor.ActorRef; +import org.apache.pekko.actor.ActorSystem; +import org.apache.pekko.actor.Props; +import org.apache.pekko.routing.RoundRobinPool; +import com.newrelic.api.agent.Trace; +import com.nr.instrumentation.pekko1.test.actors.broadcasting.ActorA; +import com.nr.instrumentation.pekko1.test.actors.broadcasting.ActorB; +import com.nr.instrumentation.pekko1.test.actors.broadcasting.ActorC; +import com.nr.instrumentation.pekko1.test.actors.broadcasting.branches.ActorNoTxnBranch; +import com.nr.instrumentation.pekko1.test.actors.broadcasting.branches.ParentActor; +import com.nr.instrumentation.pekko1.test.actors.forwarding.InitActor; +import com.nr.instrumentation.pekko1.test.actors.routing.Routee; +import com.nr.instrumentation.pekko1.test.actors.routing.RoutingActor; + +public class TestApp { + + @Trace(dispatcher = true) + public static void sendMessageInTransaction(ActorSystem system) throws InterruptedException { + ActorRef testActor = system.actorOf(Props.create(InitActor.class)); + + Thread.sleep(2000); // Let system initialize + testActor.tell("hi", null); + Thread.sleep(2000); // Let message processing finish + } + + + @Trace(dispatcher = true) + public static void broadcastInTransaction(ActorSystem system) throws InterruptedException { + ActorRef actorA = system.actorOf(Props.create(ActorA.class)); + ActorRef actorB = system.actorOf(Props.create(ActorB.class)); + ActorRef actorC = system.actorOf(Props.create(ActorC.class)); + + Thread.sleep(1000); // Let system initialize + system.actorSelection("/user/*").tell("message", null); + Thread.sleep(2000); // Let all actors process messages + } + + public static void broadcastStress(ActorSystem system) throws InterruptedException { + ActorRef actorStartTxn = system.actorOf(Props.create(ParentActor.class)); + ActorRef actorNoTxnBranch = system.actorOf(Props.create(ActorNoTxnBranch.class)); + + Thread.sleep(1000); // Let system initialize + system.actorSelection("/user/*").tell("IMPORTANT_MESSSAGE", null); + Thread.sleep(2000); // Let all actors process messages + } + + @Trace(dispatcher = true) + public static void sendRoutedMessageInTransaction(ActorSystem system) throws InterruptedException { + ActorRef router = system.actorOf(new RoundRobinPool(5).props(Props.create(RoutingActor.class))); + Thread.sleep(2000); // Let system initialize + router.tell("routing", null); + Thread.sleep(2000); // Let message processing finish + } + + @Trace(dispatcher = true) + public static void sendRoutedMessageToRouteeInTransaction(ActorSystem system) throws InterruptedException { + final ActorRef routee = system.actorOf(Props.create(Routee.class)); + + ActorRef router = system.actorOf(new RoundRobinPool(5).props(Props.create(RoutingActor.class, routee))); + + Thread.sleep(2000); // Let system initialize + router.tell("routing", null); + Thread.sleep(2000); // Let message processing finish + } +} diff --git a/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/ActorA.java b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/ActorA.java new file mode 100644 index 0000000000..6f9d15938a --- /dev/null +++ b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/ActorA.java @@ -0,0 +1,28 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation.pekko1.test.actors.broadcasting; + +import org.apache.pekko.actor.AbstractActor; +//import org.apache.pekko.actor.UntypedActor; +import org.apache.pekko.japi.pf.FI; +import org.apache.pekko.japi.pf.ReceiveBuilder; +import com.newrelic.api.agent.NewRelic; + +public class ActorA extends AbstractActor { + + @Override + public Receive createReceive() { + return ReceiveBuilder.create().matchAny(new FI.UnitApply() { + @Override + public void apply(Object message) throws Exception { + NewRelic.setTransactionName("Pekko", "Broadcast"); + NewRelic.getAgent().getTracedMethod().addRollupMetricName("ActorA"); + } + }).build(); + } +} diff --git a/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/ActorB.java b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/ActorB.java new file mode 100644 index 0000000000..c6089e40b9 --- /dev/null +++ b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/ActorB.java @@ -0,0 +1,26 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation.pekko1.test.actors.broadcasting; + +import org.apache.pekko.actor.AbstractActor; +//import org.apache.pekko.actor.UntypedActor; +import org.apache.pekko.japi.pf.FI; +import org.apache.pekko.japi.pf.ReceiveBuilder; +import com.newrelic.api.agent.NewRelic; + +public class ActorB extends AbstractActor { + @Override + public Receive createReceive() { + return ReceiveBuilder.create().matchAny(new FI.UnitApply() { + @Override + public void apply(Object message) throws Exception { + NewRelic.getAgent().getTracedMethod().addRollupMetricName("ActorB"); + } + }).build(); + } +} diff --git a/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/ActorC.java b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/ActorC.java new file mode 100644 index 0000000000..c459a85344 --- /dev/null +++ b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/ActorC.java @@ -0,0 +1,26 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation.pekko1.test.actors.broadcasting; + +import org.apache.pekko.actor.AbstractActor; +//import org.apache.pekko.actor.UntypedActor; +import org.apache.pekko.japi.pf.FI; +import org.apache.pekko.japi.pf.ReceiveBuilder; +import com.newrelic.api.agent.NewRelic; + +public class ActorC extends AbstractActor { + @Override + public Receive createReceive() { + return ReceiveBuilder.create().matchAny(new FI.UnitApply() { + @Override + public void apply(Object message) throws Exception { + NewRelic.getAgent().getTracedMethod().addRollupMetricName("ActorC"); + } + }).build(); + } +} diff --git a/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/branches/ActorNoTxnBranch.java b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/branches/ActorNoTxnBranch.java new file mode 100644 index 0000000000..2b070937ff --- /dev/null +++ b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/branches/ActorNoTxnBranch.java @@ -0,0 +1,36 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation.pekko1.test.actors.broadcasting.branches; + +import org.apache.pekko.actor.AbstractActor; +//import org.apache.pekko.actor.UntypedActor; +import org.apache.pekko.japi.pf.FI; +import org.apache.pekko.japi.pf.ReceiveBuilder; +import com.newrelic.api.agent.NewRelic; + +public class ActorNoTxnBranch extends AbstractActor { + public static final String ROLLUP_NAME = "ActorNoTxnBranch"; + + private static int count = 0; + + @Override + public Receive createReceive() { + return ReceiveBuilder.create().matchAny(new FI.UnitApply() { + @Override + public void apply(Object message) throws Exception { + if (count == 1000) { + return; + } + count++; + + NewRelic.getAgent().getTracedMethod().addRollupMetricName(ROLLUP_NAME); + self().forward(message, getContext()); + } + }).build(); + } +} diff --git a/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/branches/ChildActor.java b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/branches/ChildActor.java new file mode 100644 index 0000000000..b250727af0 --- /dev/null +++ b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/branches/ChildActor.java @@ -0,0 +1,24 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation.pekko1.test.actors.broadcasting.branches; + +import org.apache.pekko.actor.UntypedAbstractActor; + +public class ChildActor extends UntypedAbstractActor { + private static int count = 0; + + @Override + public void onReceive(Object message) throws Exception { + if (count == 1000) { + return; + } + + count++; + self().forward(message, getContext()); + } +} diff --git a/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/branches/ParentActor.java b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/branches/ParentActor.java new file mode 100644 index 0000000000..7c3bd59be4 --- /dev/null +++ b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/broadcasting/branches/ParentActor.java @@ -0,0 +1,37 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation.pekko1.test.actors.broadcasting.branches; + +import org.apache.pekko.actor.AbstractActor; +import org.apache.pekko.actor.ActorRef; +import org.apache.pekko.actor.Props; +import org.apache.pekko.japi.pf.FI; +import org.apache.pekko.japi.pf.ReceiveBuilder; +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; + +import static org.junit.Assert.assertNotNull; + +public class ParentActor extends AbstractActor { + private ActorRef childActor = getContext().actorOf(Props.create(ChildActor.class), "child"); + + @Override + public Receive createReceive() { + return ReceiveBuilder.create().matchAny(new FI.UnitApply() { + @Override + @Trace(dispatcher = true) + public void apply(Object message) throws InterruptedException { + assertNotNull(AgentBridge.getAgent().getTransaction(false)); + NewRelic.setTransactionName("Pekko", "ParentActor"); + childActor.forward(message, getContext()); + Thread.sleep(2000); + } + }).build(); + } +} diff --git a/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/forwarding/ForwardActor.java b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/forwarding/ForwardActor.java new file mode 100644 index 0000000000..1667952cc1 --- /dev/null +++ b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/forwarding/ForwardActor.java @@ -0,0 +1,28 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation.pekko1.test.actors.forwarding; + +import org.apache.pekko.actor.AbstractActor; +//import org.apache.pekko.actor.UntypedActor; + +import org.apache.pekko.japi.pf.FI; +import org.apache.pekko.japi.pf.ReceiveBuilder; +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; + +public class ForwardActor extends AbstractActor { + @Override + public Receive createReceive() { + return ReceiveBuilder.create().matchAny(new FI.UnitApply() { + @Override + public void apply(Object message) { + NewRelic.setTransactionName("PekkoForward", "Forward"); + } + }).build(); + } +} diff --git a/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/forwarding/InitActor.java b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/forwarding/InitActor.java new file mode 100644 index 0000000000..d3a205b46d --- /dev/null +++ b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/forwarding/InitActor.java @@ -0,0 +1,37 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation.pekko1.test.actors.forwarding; + +import org.apache.pekko.actor.AbstractActor; +import org.apache.pekko.actor.Actor; +import org.apache.pekko.actor.ActorRef; +import org.apache.pekko.actor.Props; +import org.apache.pekko.japi.Creator; +import org.apache.pekko.japi.pf.FI; +import org.apache.pekko.japi.pf.ReceiveBuilder; + +public class InitActor extends AbstractActor { + private static Props props = Props.create(new Creator() { + @Override + public Actor create() { + return new ForwardActor(); + } + }); + + private ActorRef forwardActor = getContext().actorOf(props, "forwardActor"); + + @Override + public Receive createReceive() { + return new ReceiveBuilder().matchAny(new FI.UnitApply() { + @Override + public void apply(Object message) throws Exception { + forwardActor.forward(message, getContext()); + } + }).build(); + } +} \ No newline at end of file diff --git a/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/routing/Routee.java b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/routing/Routee.java new file mode 100644 index 0000000000..a9131f6780 --- /dev/null +++ b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/routing/Routee.java @@ -0,0 +1,29 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation.pekko1.test.actors.routing; + +import org.apache.pekko.actor.AbstractActor; +//import org.apache.pekko.actor.UntypedActor; +import org.apache.pekko.japi.pf.FI; +import org.apache.pekko.japi.pf.ReceiveBuilder; +import com.newrelic.api.agent.NewRelic; + +public class Routee extends AbstractActor { + + @Override + public Receive createReceive() { + return ReceiveBuilder.create().matchAny(new FI.UnitApply() { + @Override + public void apply(Object messsage) { + NewRelic.setTransactionName("Pekko", "Routee"); + NewRelic.getAgent().getTracedMethod().addRollupMetricName("Routee"); + + } + }).build(); + } +} diff --git a/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/routing/RoutingActor.java b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/routing/RoutingActor.java new file mode 100644 index 0000000000..edd8f3a0da --- /dev/null +++ b/instrumentation/apache-pekko-1/src/test/java/com/nr/instrumentation/pekko1/test/actors/routing/RoutingActor.java @@ -0,0 +1,44 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation.pekko1.test.actors.routing; + +import org.apache.pekko.actor.AbstractActor; +import org.apache.pekko.actor.ActorRef; +//import org.apache.pekko.actor.UntypedActor; +import org.apache.pekko.japi.pf.FI; +import org.apache.pekko.japi.pf.ReceiveBuilder; +import com.newrelic.api.agent.NewRelic; + +public class RoutingActor extends AbstractActor { + + private final ActorRef routee; + + public RoutingActor() { + this.routee = null; + } + + public RoutingActor(ActorRef routee) { + this.routee = routee; + } + + @Override + public Receive createReceive() { + return ReceiveBuilder.create().matchAny(new FI.UnitApply() { + @Override + public void apply(Object message) { + NewRelic.setTransactionName("Pekko", "Routing"); + NewRelic.getAgent().getTracedMethod().addRollupMetricName("RoutingActor"); + if (routee != null) { + routee.forward(message, getContext()); + } + + } + }).build(); + } + +} diff --git a/settings.gradle b/settings.gradle index 25c83ddc99..0d490bdf78 100644 --- a/settings.gradle +++ b/settings.gradle @@ -91,6 +91,7 @@ include 'instrumentation:akka-http-core-10.2.0' include 'instrumentation:apache-log4j-1' include 'instrumentation:apache-log4j-2.6' include 'instrumentation:apache-log4j-2.11' +include 'instrumentation:apache-pekko-1' include 'instrumentation:apache-struts-2.0' include 'instrumentation:async-http-client-2.0.0' include 'instrumentation:async-http-client-2.1.0'