Skip to content

Commit

Permalink
Create akka-http 10.2.0 instrumentation
Browse files Browse the repository at this point in the history
  • Loading branch information
obenkenobi committed Jun 20, 2024
1 parent aae1b1b commit c0b6e17
Show file tree
Hide file tree
Showing 36 changed files with 3,806 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@

package akka.http.scaladsl.server

import com.agent.instrumentation.akka.http.PathMatcherUtils

import java.util.concurrent.LinkedBlockingDeque
import java.util.concurrent.atomic.{AtomicBoolean, AtomicInteger}
import java.util.logging.Level

import com.agent.instrumentation.akka.http.PathMatcherUtils
import com.newrelic.agent.bridge.AgentBridge
import com.newrelic.api.agent.Trace

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ package akka.http.scaladsl.server

import java.util.concurrent.LinkedBlockingDeque
import java.util.concurrent.atomic.{AtomicBoolean, AtomicInteger}

import akka.event.LoggingAdapter
import akka.http.scaladsl.marshalling.AkkaHttpToResponseMarshallable
import akka.http.scaladsl.model._
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
package akka.http.scaladsl.server

import java.util.concurrent.atomic.{AtomicBoolean, AtomicInteger}

import akka.event.LoggingAdapter
import akka.http.scaladsl.marshalling.ToResponseMarshallable
import akka.http.scaladsl.model.{HttpRequest, Uri}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import akka.pattern.after
import akka.pattern.ask
import akka.stream.ActorMaterializer
import akka.util.{ByteString, Timeout}
import com.agent.instrumentation.akka.http.StatusCheckActor.Ping
import com.newrelic.api.agent.NewRelic

import scala.concurrent.ExecutionContext.Implicits.global
Expand Down
2 changes: 1 addition & 1 deletion instrumentation/akka-http-2.13_10.1.8/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ dependencies {
}

verifyInstrumentation {
passesOnly('com.typesafe.akka:akka-http_2.13:[10.1.8,)') {
passesOnly('com.typesafe.akka:akka-http_2.13:[10.1.8,10.2.0)') {
implementation("com.typesafe.akka:akka-stream_2.13:2.5.23")
}
excludeRegex 'com.typesafe.akka:akka-http_2.13:.*(RC|M)[0-9]*$'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package akka.http.scaladsl;

import com.newrelic.api.agent.weaver.SkipIfPresent;

@SkipIfPresent(originalName = "akka.http.scaladsl.ServerBuilder")
public class ServerBuilder {
}
37 changes: 37 additions & 0 deletions instrumentation/akka-http-2.13_10.2.0/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
apply plugin: 'scala'

isScalaProjectEnabled(project, "scala-2.13")

sourceSets.test.scala.srcDir "src/test/java"
sourceSets.test.java.srcDirs = []

jar {
manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.akka-http-2.13_10.2.0' }
}

dependencies {
implementation(project(":agent-bridge"))
implementation(project(":newrelic-weaver-api"))
implementation(project(":newrelic-weaver-scala-api"))
implementation("com.typesafe.akka:akka-http_2.13:10.2.0")
implementation("com.typesafe.akka:akka-stream_2.13:2.8.5")
implementation("com.typesafe.akka:akka-actor_2.13:2.8.5")

testImplementation(project(":instrumentation:akka-2.2")) { transitive = false }
testImplementation(project(":instrumentation:scala-2.13.0")) { transitive = false }
testImplementation("com.jayway.restassured:rest-assured:2.7.0")
testImplementation("jakarta.xml.ws:jakarta.xml.ws-api:2.3.3")
}

verifyInstrumentation {
passesOnly('com.typesafe.akka:akka-http_2.13:[10.2.0,10.5.0)') {
implementation("com.typesafe.akka:akka-stream_2.13:2.8.5")
}
excludeRegex 'com.typesafe.akka:akka-http_2.13:.*(RC|M)[0-9]*$'
excludeRegex 'com.typesafe.akka:akka-http_2.13:.*-[0-9a-f]{8}$'
}

site {
title 'Akka Http'
type 'Framework'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package akka.http.scaladsl.marshalling;

import akka.http.scaladsl.model.HttpRequest;
import akka.http.scaladsl.model.HttpResponse;
import com.agent.instrumentation.akka.http102.PathMatcherUtils;
import com.agent.instrumentation.akka.http102.RequestWrapper;
import com.newrelic.api.agent.NewRelic;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.Weaver;
import scala.concurrent.ExecutionContext;
import scala.concurrent.Future;

@Weave(originalName = "akka.http.scaladsl.marshalling.Marshal")
public class AkkaHttpMarshal<A> {

public Future<HttpResponse> toResponseFor(HttpRequest request, Marshaller<A, HttpResponse> m, ExecutionContext ec) {
NewRelic.getAgent().getTransaction().setWebRequest(new RequestWrapper(request));
PathMatcherUtils.reset();
return Weaver.callOriginal();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package akka.http.scaladsl.marshalling;

import akka.http.scaladsl.model.HttpResponse;
import com.agent.instrumentation.akka.http102.ResponseWrapper;
import com.newrelic.agent.bridge.AgentBridge;
import com.newrelic.api.agent.NewRelic;
import com.newrelic.api.agent.Token;
import com.newrelic.api.agent.Trace;
import com.newrelic.api.agent.Transaction;
import com.newrelic.api.agent.weaver.Weaver;
import scala.runtime.AbstractFunction1;

public class AkkaHttpMarshallerMapper extends AbstractFunction1<HttpResponse, HttpResponse> {

private final Token token;

public AkkaHttpMarshallerMapper(Token token) {
this.token = token;
}

@Override
@Trace(async = true)
public HttpResponse apply(HttpResponse httpResponse) {
try {
if (token != null) {
token.linkAndExpire();
}
ResponseWrapper responseWrapper = new ResponseWrapper(httpResponse);
Transaction transaction = NewRelic.getAgent().getTransaction();
transaction.setWebResponse(responseWrapper);
transaction.addOutboundResponseHeaders();
transaction.markResponseSent();

return responseWrapper.response();
} catch (Throwable t) {
AgentBridge.instrumentation.noticeInstrumentationError(t, Weaver.getImplementationTitle());
return httpResponse;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package akka.http.scaladsl.marshalling;

import akka.http.scaladsl.model.HttpResponse;
import com.newrelic.api.agent.Token;
import com.newrelic.api.agent.weaver.MatchType;
import com.newrelic.api.agent.weaver.NewField;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.Weaver;

@Weave(type = MatchType.Interface, originalName = "akka.http.scaladsl.marshalling.ToResponseMarshallable")
public abstract class AkkaHttpToResponseMarshallable {

@NewField
public Token token;

public Marshaller<Object, HttpResponse> marshaller() {
Marshaller<Object, HttpResponse> marshaller = Weaver.callOriginal();
AkkaHttpMarshallerMapper akkaHttpMarshallerMapper = new AkkaHttpMarshallerMapper(token);
return marshaller.map(akkaHttpMarshallerMapper);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package akka.http.scaladsl.server

import com.agent.instrumentation.akka.http102.PathMatcherUtils
import com.newrelic.agent.bridge.AgentBridge
import com.newrelic.api.agent.Trace

import java.util.concurrent.LinkedBlockingDeque
import java.util.concurrent.atomic.{AtomicBoolean, AtomicInteger}
import java.util.logging.Level
import scala.collection.mutable
import scala.concurrent.Future
import scala.runtime.AbstractFunction1

object AkkaHttpContextFunction {

final val retransformed = new AtomicBoolean(false)

def contextWrapper(original: Function1[RequestContext, Future[RouteResult]]): Function1[RequestContext, Future[RouteResult]] = {
if (retransformed.compareAndSet(false, true)) {
AgentBridge.getAgent.getLogger.log(Level.FINER, "Retransforming akka.http.scaladsl.server.AkkaHttpContextFunction")
AgentBridge.instrumentation.retransformUninstrumentedClass(classOf[ContextWrapper])
AgentBridge.getAgent.getLogger.log(Level.FINER, "Retransformed akka.http.scaladsl.server.AkkaHttpContextFunction")
}

new ContextWrapper(original)
}

}

class ContextWrapper(original: Function1[RequestContext, Future[RouteResult]]) extends AbstractFunction1[RequestContext, Future[RouteResult]] {

@Trace(dispatcher = true)
override def apply(ctx: RequestContext): Future[RouteResult] = {
try {
val tracedMethod = AgentBridge.getAgent.getTracedMethod
tracedMethod.setMetricName("AkkaHttp")
// Akka-http 10.1.5 uses CallbackRunnable and we lose transaction context between Directives
AgentBridge.getAgent.getTracedMethod.setTrackCallbackRunnable(true);
val token = AgentBridge.getAgent.getTransaction(false).getToken
PathMatcherUtils.setHttpRequest(ctx.request)
// We use this method to wire up our RequestContext wrapper and start our transaction
val newCtx = new NewRelicRequestContextWrapper(ctx, ctx.asInstanceOf[RequestContextImpl], token,
new LinkedBlockingDeque[String], new AtomicBoolean(false), new AtomicInteger(0), new AtomicInteger(0),
new LinkedBlockingDeque[String], new mutable.HashSet[String], ctx.request, ctx.unmatchedPath, ctx.executionContext, ctx.materializer,
ctx.log, ctx.settings, ctx.parserSettings)
original.apply(newCtx)
} catch {
case t: Throwable => {
AgentBridge.instrumentation.noticeInstrumentationError(t, "akka-http-2.4.5")
original.apply(ctx)
}
}
}

override def compose[A](g: (A) => RequestContext): (A) => Future[RouteResult] = original.compose(g)

override def andThen[A](g: (Future[RouteResult]) => A): (RequestContext) => A = original.andThen(g)

override def toString(): String = original.toString()

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package akka.http.scaladsl.server;

import akka.http.scaladsl.model.Uri;
import com.agent.instrumentation.akka.http102.PathMatcherUtils;
import com.newrelic.api.agent.weaver.MatchType;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.Weaver;
import scala.Tuple1;
import scala.runtime.BoxedUnit;

@Weave(type = MatchType.ExactClass, originalName = "akka.http.scaladsl.server.PathMatchers")
public class AkkaHttpPathMatchers {

@Weave(type = MatchType.ExactClass, originalName = "akka.http.scaladsl.server.PathMatchers$Slash$")
public static class AkkaHttpSlash$ {

public PathMatcher.Matching<BoxedUnit> apply(final Uri.Path path) {
PathMatcher.Matching<BoxedUnit> matching = Weaver.callOriginal();
PathMatcherUtils.appendSlash(path, matching);
return matching;
}

}

@Weave(type = MatchType.ExactClass, originalName = "akka.http.scaladsl.server.PathMatchers$Remaining$")
public static class AkkaHttpRemaining$ {

public PathMatcher.Matched<Tuple1<String>> apply(final Uri.Path path) {
PathMatcher.Matched<Tuple1<String>> matched = Weaver.callOriginal();
PathMatcherUtils.appendRemaining("Remaining", path, matched);
return matched;
}

}

@Weave(type = MatchType.ExactClass, originalName = "akka.http.scaladsl.server.PathMatchers$RemainingPath$")
public static class AkkaHttpRemainingPath$ {

public PathMatcher.Matched<Tuple1<String>> apply(final Uri.Path path) {
PathMatcher.Matched<Tuple1<String>> matched = Weaver.callOriginal();
PathMatcherUtils.appendRemaining("RemainingPath", path, matched);
return matched;
}

}

@Weave(type = MatchType.BaseClass, originalName = "akka.http.scaladsl.server.PathMatchers$NumberMatcher")
public static class AkkaHttpNumberMatcher<T> {

public PathMatcher.Matching<Tuple1<T>> apply(final Uri.Path path) {
PathMatcher.Matching<Tuple1<T>> matching = Weaver.callOriginal();
PathMatcherUtils.appendNumberMatch(getClass().getSimpleName().replaceAll("\\$", ""), path, matching);
return matching;
}

}

@Weave(type = MatchType.ExactClass, originalName = "akka.http.scaladsl.server.PathMatchers$Segment$")
public static class AkkaHttpSegment$ {

public PathMatcher.Matching<Tuple1<String>> apply(final Uri.Path path) {
PathMatcher.Matching<Tuple1<String>> matching = Weaver.callOriginal();
PathMatcherUtils.appendSegment(path, matching);
return matching;
}
}
}
Loading

0 comments on commit c0b6e17

Please sign in to comment.