Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pekko support #1811

Merged
merged 4 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions instrumentation/apache-pekko-1/REAME.md
Original file line number Diff line number Diff line change
@@ -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`.

19 changes: 19 additions & 0 deletions instrumentation/apache-pekko-1/build.gradle
Original file line number Diff line number Diff line change
@@ -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'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
*
* * Copyright 2020 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<String> 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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
*
* * Copyright 2020 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();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
*
* * Copyright 2020 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();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
*
* * Copyright 2020 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();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
*
* * Copyright 2020 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();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
*
* * Copyright 2020 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();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
*
* * Copyright 2020 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;
}
}

}
Loading
Loading