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

Akka http core 2 13 10 2 #149

Merged
merged 6 commits into from
Dec 15, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions instrumentation/akka-2.2/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ verifyInstrumentation {
passes 'com.typesafe.akka:akka-actor_2.10:[2.2.0-RC1,)'
passes 'com.typesafe.akka:akka-actor_2.11:[2.3.2,)'
passes 'com.typesafe.akka:akka-actor_2.12:[2.4.10,)'
passes 'com.typesafe.akka:akka-actor_2.13:[2.4.10,)'
exclude 'com.typesafe.akka:akka-actor_2.11:2.4-M1'
}

Expand Down
3 changes: 3 additions & 0 deletions instrumentation/akka-http-core-10.2.0/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ verifyInstrumentation {
fails('com.typesafe.akka:akka-http-core_2.12:[,10.2.0-M1)') {
compile("com.typesafe.akka:akka-stream_2.12:2.5.11")
}
fails('com.typesafe.akka:akka-http-core_2.13:[,10.2.0-M1)') {
compile("com.typesafe.akka:akka-stream_2.13:2.5.11")
}
}

site {
Expand Down
3 changes: 3 additions & 0 deletions instrumentation/akka-http-core-2.13_10.1.8/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ verifyInstrumentation {
passesOnly('com.typesafe.akka:akka-http-core_2.13:[10.1.8,10.2.0-RC1)') {
compile("com.typesafe.akka:akka-stream_2.13:2.5.23")
}
fails('com.typesafe.akka:akka-http-core_2.12:[10.1.8,)') {
compile("com.typesafe.akka:akka-stream_2.12:2.5.23")
}
}

site {
Expand Down
34 changes: 34 additions & 0 deletions instrumentation/akka-http-core-2.13_10.2.0/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
apply plugin: 'scala'

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

dependencies {
implementation(project(":agent-bridge"))
implementation("com.typesafe.akka:akka-http-core_2.13:10.2.0")
implementation("com.typesafe.akka:akka-stream_2.13:2.6.10")

testImplementation(project(":instrumentation:akka-2.2")) { transitive = false }
}

verifyInstrumentation {
fails('com.typesafe.akka:akka-http-core-experimental_2.13:[1.0,10.1.8)')
passesOnly('com.typesafe.akka:akka-http-core_2.13:[10.2.0-RC1,)') {
compile("com.typesafe.akka:akka-stream_2.13:2.5.23")
}
fails('com.typesafe.akka:akka-http-core_2.12:[10.2.0-RC1,)') {
compile("com.typesafe.akka:akka-stream_2.12:2.5.23")
}
}

site {
title 'Akka Http Core'
type 'Framework'
}

test {
onlyIf {
!project.hasProperty('test6') && !project.hasProperty('test7')
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package akka.http.scaladsl

import akka.http.scaladsl.model.{HttpRequest, HttpResponse}
import com.newrelic.agent.bridge.{AgentBridge, Token, TransactionNamePriority}
import com.newrelic.api.agent.weaver.Weaver
import com.newrelic.api.agent.{NewRelic, Trace}
import com.nr.instrumentation.akkahttpcore.{RequestWrapper, ResponseFuture}

import scala.concurrent.{ExecutionContext, Future}
import scala.runtime.AbstractFunction1

class AsyncRequestHandler(handler: HttpRequest => Future[HttpResponse])(implicit ec: ExecutionContext) extends AbstractFunction1[HttpRequest, Future[HttpResponse]] {

val transactionCategory: String = "AkkaHttpCore"

@Trace(dispatcher = true)
override def apply(param: HttpRequest): Future[HttpResponse] = {

var futureResponse: Future[HttpResponse] = null
var token: Token = null

try {
token = AgentBridge.getAgent.getTransaction.getToken
AgentBridge.getAgent.getTransaction.setTransactionName(TransactionNamePriority.SERVLET_NAME, true, transactionCategory, "akkaHandler")
NewRelic.getAgent.getTracedMethod.setMetricName("Akka", "RequestHandler")

val wrappedRequest: RequestWrapper = new RequestWrapper(param)
NewRelic.getAgent().getTransaction().setWebRequest(wrappedRequest)
} catch {
case t: Throwable => {
AgentBridge.instrumentation.noticeInstrumentationError(t, Weaver.getImplementationTitle())
}
}

futureResponse = handler.apply(param)

try {
// Modify the original response by passing it through our map function (since a copy
// is required due to the response headers being immutable). Return the (future) result of this map function.
futureResponse.flatMap(ResponseFuture.wrapResponse(token))
} catch {
case t: Throwable => {
AgentBridge.instrumentation.noticeInstrumentationError(t, Weaver.getImplementationTitle())
futureResponse
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package akka.http.scaladsl;

import akka.event.LoggingAdapter;
import akka.http.scaladsl.model.HttpRequest;
import akka.http.scaladsl.model.HttpResponse;
import akka.http.scaladsl.settings.ConnectionPoolSettings;
import akka.http.scaladsl.settings.ServerSettings;
import akka.stream.Materializer;
import com.newrelic.api.agent.NewRelic;
import com.newrelic.api.agent.Segment;
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.akkahttpcore.AkkaHttpUtils;
import scala.Function1;
import scala.concurrent.Future;

@Weave(type = MatchType.ExactClass, originalName = "akka.http.scaladsl.HttpExt")
public class HttpExtInstrumentation {

public Future<HttpInstrumentation.ServerBinding> bindAndHandleAsync(
Function1<HttpRequest, Future<HttpResponse>> handler,
String interfaceString, int port,
ConnectionContext connectionContext,
ServerSettings settings, int parallelism,
LoggingAdapter adapter, Materializer mat) {

AsyncRequestHandler wrapperHandler = new AsyncRequestHandler(handler, mat.executionContext());
handler = wrapperHandler;

return Weaver.callOriginal();
}

public Future<HttpInstrumentation.ServerBinding> bindAndHandleSync(
Function1<HttpRequest, HttpResponse> handler,
String interfaceString, int port,
ConnectionContext connectionContext,
ServerSettings settings,
LoggingAdapter adapter, Materializer mat) {

SyncRequestHandler wrapperHandler = new SyncRequestHandler(handler);
handler = wrapperHandler;

return Weaver.callOriginal();
}

public Future<HttpResponse> singleRequest(HttpRequest httpRequest, HttpsConnectionContext connectionContext, ConnectionPoolSettings poolSettings,
LoggingAdapter loggingAdapter) {
final Segment segment = NewRelic.getAgent().getTransaction().startSegment("Akka", "singleRequest");

Future<HttpResponse> responseFuture = Weaver.callOriginal();

AkkaHttpUtils.finishSegmentOnComplete(httpRequest, responseFuture, segment);

return responseFuture;
}

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

package akka.http.scaladsl;

import com.newrelic.agent.bridge.AgentBridge;
import com.newrelic.agent.bridge.ManifestUtils;
import com.newrelic.api.agent.weaver.MatchType;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.WeaveAllConstructors;
import com.newrelic.api.agent.weaver.Weaver;
import scala.Function0;
import scala.Function1;
import scala.concurrent.Future;
import scala.concurrent.duration.FiniteDuration;
import scala.runtime.BoxedUnit;

import java.net.InetSocketAddress;
import java.util.logging.Level;

@Weave(type = MatchType.ExactClass, originalName = "akka.http.scaladsl.Http")
public class HttpInstrumentation {

@Weave(type = MatchType.ExactClass, originalName = "akka.http.scaladsl.Http$ServerBinding")
public static class ServerBinding {

public InetSocketAddress localAddress() {
return Weaver.callOriginal();
}

@WeaveAllConstructors
public ServerBinding() {
AgentBridge.getAgent().getLogger().log(Level.FINE, "Setting akka-http port to: {0,number,#}", localAddress().getPort());
AgentBridge.publicApi.setAppServerPort(localAddress().getPort());
AgentBridge.publicApi.setServerInfo("Akka HTTP", ManifestUtils.getVersionFromManifest(getClass(), "akka-http-core", "10.0.11"));

AgentBridge.instrumentation.retransformUninstrumentedClass(SyncRequestHandler.class);
AgentBridge.instrumentation.retransformUninstrumentedClass(AsyncRequestHandler.class);
}
}

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

package akka.http.scaladsl;

import akka.http.scaladsl.model.HttpRequest;
import akka.http.scaladsl.model.HttpResponse;
import akka.stream.Materializer;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.Weaver;
import scala.Function1;
import scala.concurrent.Future;

@Weave(originalName = "akka.http.scaladsl.Http$IncomingConnection")
public class IncomingConnection {

public void handleWithSyncHandler(Function1<HttpRequest, HttpResponse> func, Materializer mat) {

SyncRequestHandler wrapperHandler = new SyncRequestHandler(func);
func = wrapperHandler;

Weaver.callOriginal();
}

public void handleWithAsyncHandler(Function1<HttpRequest, Future<HttpResponse>> func, int parallel, Materializer mat) {

AsyncRequestHandler wrapperHandler = new AsyncRequestHandler(func, mat.executionContext());
func = wrapperHandler;

Weaver.callOriginal();
}


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

package akka.http.scaladsl

import akka.http.scaladsl.model.{HttpRequest, HttpResponse}
import com.newrelic.agent.bridge.{AgentBridge, TransactionNamePriority}
import com.newrelic.api.agent.weaver.Weaver
import com.newrelic.api.agent.{NewRelic, Trace, Transaction}
import com.nr.instrumentation.akkahttpcore.{RequestWrapper, ResponseWrapper}

import scala.runtime.AbstractFunction1

class SyncRequestHandler(handler: HttpRequest => HttpResponse) extends AbstractFunction1[HttpRequest, HttpResponse] {

val transactionCategory :String = "AkkaHttpCore"

@Trace(dispatcher = true)
override def apply(param: HttpRequest): HttpResponse = {

try {
AgentBridge.getAgent.getTransaction.setTransactionName(TransactionNamePriority.SERVLET_NAME, true, transactionCategory, "akkaHandler")
NewRelic.getAgent.getTracedMethod.setMetricName("Akka", "RequestHandler")

val wrappedRequest: RequestWrapper = new RequestWrapper(param)
NewRelic.getAgent().getTransaction().setWebRequest(wrappedRequest)

} catch {
case t: Throwable => AgentBridge.instrumentation.noticeInstrumentationError(t, Weaver.getImplementationTitle());
}

val response: HttpResponse = handler.apply(param)

try {

var updatedResponse: HttpResponse = response

val txn: Transaction = NewRelic.getAgent().getTransaction()

if (txn != null) {
val wrappedResponse = new ResponseWrapper(response)
txn.setWebResponse(wrappedResponse)
txn.addOutboundResponseHeaders()
txn.markResponseSent()
updatedResponse = wrappedResponse.response
}

updatedResponse

} catch {
case t: Throwable => AgentBridge.instrumentation.noticeInstrumentationError(t, Weaver.getImplementationTitle());
response
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package com.nr.instrumentation.akkahttpcore

import java.util

import akka.http.scaladsl.model.HttpRequest
import com.newrelic.api.agent.{ExtendedInboundHeaders, HeaderType}

import scala.jdk.CollectionConverters._

class AkkaHttpInboundHeaders(val httpRequest: HttpRequest) extends ExtendedInboundHeaders {

override def getHeaderType: HeaderType = HeaderType.HTTP

override def getHeader(name: String): String = {
val header = httpRequest.getHeader(name)
if (!header.isPresent) {
return null
}
header.get().value()
}

override def getHeaders(name: String): util.List[String] = {
val headers = httpRequest.headers.filter(header => header.is(name.toLowerCase)).map(header => header.value)
if (headers.isEmpty) {
return null
}
headers.asJava
}
}
Loading