From 51139b3d4604bca79724af70e98795ada8c44b72 Mon Sep 17 00:00:00 2001 From: Thomas Spring Date: Thu, 10 Dec 2020 16:13:38 -0800 Subject: [PATCH 1/5] Add akka-http-core_2.13-10.2.0 instrumentation --- .../akka-http-core-2.13_10.2.0/build.gradle | 35 +++ .../http/scaladsl/AsyncRequestHandler.scala | 55 ++++ .../http/scaladsl/HttpExtInstrumentation.java | 66 +++++ .../http/scaladsl/HttpInstrumentation.java | 46 ++++ .../http/scaladsl/IncomingConnection.java | 38 +++ .../http/scaladsl/SyncRequestHandler.scala | 59 ++++ .../akkahttpcore/AkkaHttpInboundHeaders.scala | 36 +++ .../akkahttpcore/AkkaHttpUtils.scala | 50 ++++ .../akkahttpcore/RequestWrapper.scala | 62 +++++ .../akkahttpcore/ResponseFuture.scala | 54 ++++ .../akkahttpcore/ResponseWrapper.scala | 37 +++ .../akka/http/core/AkkaHttpCoreTest.scala | 259 ++++++++++++++++++ .../akka/http/core/AkkaServer.scala | 77 ++++++ .../akka/http/core/PlayServer.scala | 70 +++++ settings.gradle | 1 + 15 files changed, 945 insertions(+) create mode 100644 instrumentation/akka-http-core-2.13_10.2.0/build.gradle create mode 100644 instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/AsyncRequestHandler.scala create mode 100644 instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/HttpExtInstrumentation.java create mode 100644 instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/HttpInstrumentation.java create mode 100644 instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/IncomingConnection.java create mode 100644 instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/SyncRequestHandler.scala create mode 100644 instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/AkkaHttpInboundHeaders.scala create mode 100644 instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/AkkaHttpUtils.scala create mode 100644 instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/RequestWrapper.scala create mode 100644 instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/ResponseFuture.scala create mode 100644 instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/ResponseWrapper.scala create mode 100644 instrumentation/akka-http-core-2.13_10.2.0/src/test/scala/com/agent/instrumentation/akka/http/core/AkkaHttpCoreTest.scala create mode 100644 instrumentation/akka-http-core-2.13_10.2.0/src/test/scala/com/agent/instrumentation/akka/http/core/AkkaServer.scala create mode 100644 instrumentation/akka-http-core-2.13_10.2.0/src/test/scala/com/agent/instrumentation/akka/http/core/PlayServer.scala diff --git a/instrumentation/akka-http-core-2.13_10.2.0/build.gradle b/instrumentation/akka-http-core-2.13_10.2.0/build.gradle new file mode 100644 index 0000000000..52695d6e69 --- /dev/null +++ b/instrumentation/akka-http-core-2.13_10.2.0/build.gradle @@ -0,0 +1,35 @@ +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.5.23") + + 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') + } +} diff --git a/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/AsyncRequestHandler.scala b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/AsyncRequestHandler.scala new file mode 100644 index 0000000000..bfe04e140d --- /dev/null +++ b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/AsyncRequestHandler.scala @@ -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 + } + } + } +} diff --git a/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/HttpExtInstrumentation.java b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/HttpExtInstrumentation.java new file mode 100644 index 0000000000..ebf182f222 --- /dev/null +++ b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/HttpExtInstrumentation.java @@ -0,0 +1,66 @@ +/* + * + * * 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 bindAndHandleAsync( + Function1> 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 bindAndHandleSync( + Function1 handler, + String interfaceString, int port, + ConnectionContext connectionContext, + ServerSettings settings, + LoggingAdapter adapter, Materializer mat) { + + SyncRequestHandler wrapperHandler = new SyncRequestHandler(handler); + handler = wrapperHandler; + + return Weaver.callOriginal(); + } + + // We are weaving the singleRequestImpl method here rather than just singleRequest because the javadsl only flows through here + public Future singleRequest(HttpRequest httpRequest, HttpsConnectionContext connectionContext, ConnectionPoolSettings poolSettings, + LoggingAdapter loggingAdapter) { + final Segment segment = NewRelic.getAgent().getTransaction().startSegment("Akka", "singleRequest"); + + Future responseFuture = Weaver.callOriginal(); + + AkkaHttpUtils.finishSegmentOnComplete(httpRequest, responseFuture, segment); + + return responseFuture; + } + +} diff --git a/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/HttpInstrumentation.java b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/HttpInstrumentation.java new file mode 100644 index 0000000000..e359506ee7 --- /dev/null +++ b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/HttpInstrumentation.java @@ -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); + } + } + +} diff --git a/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/IncomingConnection.java b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/IncomingConnection.java new file mode 100644 index 0000000000..637a396f2b --- /dev/null +++ b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/IncomingConnection.java @@ -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 func, Materializer mat) { + + SyncRequestHandler wrapperHandler = new SyncRequestHandler(func); + func = wrapperHandler; + + Weaver.callOriginal(); + } + + public void handleWithAsyncHandler(Function1> func, int parallel, Materializer mat) { + + AsyncRequestHandler wrapperHandler = new AsyncRequestHandler(func, mat.executionContext()); + func = wrapperHandler; + + Weaver.callOriginal(); + } + + +} diff --git a/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/SyncRequestHandler.scala b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/SyncRequestHandler.scala new file mode 100644 index 0000000000..894fc09d19 --- /dev/null +++ b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/SyncRequestHandler.scala @@ -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 + } + } +} diff --git a/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/AkkaHttpInboundHeaders.scala b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/AkkaHttpInboundHeaders.scala new file mode 100644 index 0000000000..79113db7aa --- /dev/null +++ b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/AkkaHttpInboundHeaders.scala @@ -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 + } +} diff --git a/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/AkkaHttpUtils.scala b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/AkkaHttpUtils.scala new file mode 100644 index 0000000000..ce1c5b0350 --- /dev/null +++ b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/AkkaHttpUtils.scala @@ -0,0 +1,50 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation.akkahttpcore + +import java.net.URI +import java.util.concurrent.Executors + +import akka.http.scaladsl.model.{HttpRequest, HttpResponse} +import com.newrelic.agent.bridge.AgentBridge +import com.newrelic.api.agent.weaver.Weaver +import com.newrelic.api.agent.{HttpParameters, Segment} + +import scala.concurrent.{ExecutionContext, ExecutionContextExecutor, Future} +import scala.util.{Failure, Success} + +object AkkaHttpUtils { + + implicit val executor: ExecutionContextExecutor = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(4)) + + def finishSegmentOnComplete(httpRequest: HttpRequest, httpResponseFuture: Future[HttpResponse], segment: Segment): Unit = { + httpResponseFuture onComplete { + case Success(response) => + try { + segment.reportAsExternal(HttpParameters + .library("AkkaHttpClient") + .uri(new URI(httpRequest.uri.toString())) + .procedure(httpRequest.method.value) + .inboundHeaders(new AkkaHttpInboundHeaders(httpRequest)) + .build()) + segment.end() + } catch { + case t: Throwable => AgentBridge.instrumentation.noticeInstrumentationError(t, Weaver.getImplementationTitle) + } + + case Failure(t) => + try { + // In the case of an error, just end the segment. + // We probably don't want to report an error here because it may just be a 404 + segment.end() + } catch { + case t: Throwable => AgentBridge.instrumentation.noticeInstrumentationError(t, Weaver.getImplementationTitle) + } + } + } +} diff --git a/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/RequestWrapper.scala b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/RequestWrapper.scala new file mode 100644 index 0000000000..91a27c1fc9 --- /dev/null +++ b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/RequestWrapper.scala @@ -0,0 +1,62 @@ +/* + * + * * 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.{ExtendedRequest, HeaderType} + +import scala.jdk.CollectionConverters._ + +class RequestWrapper(request: HttpRequest) extends ExtendedRequest { + + def getMethod: String = { + request.method.name + } + + def getRequestURI: String = { + request.uri.path.toString() + } + + def getRemoteUser: String = { + null + } + + def getParameterNames: java.util.Enumeration[_] = { + request.uri.query().toMap.keysIterator.asJavaEnumeration + } + + def getParameterValues(name: String): Array[String] = { + request.uri.query().getAll(name).toArray + } + + def getAttribute(name: String): AnyRef = { + null + } + + def getCookieValue(name: String): String = { + request.cookies.find(cookie => cookie.name.equalsIgnoreCase(name)).map(cookie => cookie.value).orNull + } + + def getHeaderType: HeaderType = { + HeaderType.HTTP + } + + def getHeader(name: String): String = { + request.headers.find(header => header.is(name.toLowerCase)).map(header => header.value).orNull + } + + override def getHeaders(name: String): util.List[String] = { + val headers = request.headers.filter(header => header.is(name.toLowerCase)).map(header => header.value) + if (headers.isEmpty) { + return null + } + headers.asJava + } +} diff --git a/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/ResponseFuture.scala b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/ResponseFuture.scala new file mode 100644 index 0000000000..801a266f68 --- /dev/null +++ b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/ResponseFuture.scala @@ -0,0 +1,54 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation.akkahttpcore + +import java.util.logging.Level + +import akka.http.scaladsl.model.HttpResponse +import com.newrelic.agent.bridge.{AgentBridge, Token} +import com.newrelic.api.agent.{NewRelic, Transaction} +import com.newrelic.api.agent.weaver.Weaver + +import scala.concurrent.{ExecutionContext, Future} + +object ResponseFuture { + + def wrapResponse(token: Token)(implicit ec: ExecutionContext): (HttpResponse) => Future[HttpResponse] = { response:HttpResponse => { + Future { + var updatedResponse: HttpResponse = response + var localToken = token + + try { + val txn: Transaction = localToken.getTransaction + if (txn != null) { + val wrappedResponse = new ResponseWrapper(response) + txn.setWebResponse(wrappedResponse) + txn.addOutboundResponseHeaders() + txn.markResponseSent() + updatedResponse = wrappedResponse.response + + localToken.expire() + localToken = null + } + } catch { + case t: Throwable => AgentBridge.instrumentation.noticeInstrumentationError(t, Weaver.getImplementationTitle) + try { + localToken.expire() + localToken = null; + } + catch { + case t: Throwable => AgentBridge.instrumentation.noticeInstrumentationError(t, Weaver.getImplementationTitle) + } + + } + + updatedResponse + } + } + } +} diff --git a/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/ResponseWrapper.scala b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/ResponseWrapper.scala new file mode 100644 index 0000000000..f7b7b68ffa --- /dev/null +++ b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/com/nr/instrumentation/akkahttpcore/ResponseWrapper.scala @@ -0,0 +1,37 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation.akkahttpcore + +import akka.http.scaladsl.model.HttpResponse +import akka.http.scaladsl.model.headers.RawHeader +import com.newrelic.api.agent.{HeaderType, ExtendedResponse} + +class ResponseWrapper(var response: HttpResponse) extends ExtendedResponse { + + def getStatus: Int = { + response.status.intValue + } + + def getStatusMessage: String = { + response.status.reason + } + + def getContentType: String = { + response.entity.contentType.value + } + + override def getHeaderType = HeaderType.HTTP + + override def setHeader(name: String, value: String): Unit = { + response = response.addHeader(new RawHeader(name, value)) + } + + def getContentLength: Long = { + response.entity.contentLengthOption.getOrElse(0L) + } +} diff --git a/instrumentation/akka-http-core-2.13_10.2.0/src/test/scala/com/agent/instrumentation/akka/http/core/AkkaHttpCoreTest.scala b/instrumentation/akka-http-core-2.13_10.2.0/src/test/scala/com/agent/instrumentation/akka/http/core/AkkaHttpCoreTest.scala new file mode 100644 index 0000000000..cfb9a6ee9e --- /dev/null +++ b/instrumentation/akka-http-core-2.13_10.2.0/src/test/scala/com/agent/instrumentation/akka/http/core/AkkaHttpCoreTest.scala @@ -0,0 +1,259 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http.core + +import java.util +import java.util.concurrent.TimeUnit + +import akka.actor.ActorSystem +import akka.http.scaladsl.Http +import akka.http.scaladsl.model.headers.RawHeader +import akka.http.scaladsl.model.{HttpHeader, HttpRequest, HttpResponse} +import akka.stream.ActorMaterializer +import com.newrelic.agent.HeadersUtil +import com.newrelic.agent.introspec._ +import com.newrelic.agent.util.Obfuscator +import com.newrelic.api.agent.Trace +import org.junit.Assert.{assertNotNull, assertTrue} +import org.junit.runner.RunWith +import org.junit.{Assert, Test} + +import scala.concurrent.duration.DurationInt +import scala.concurrent.{Await, Future} + +@RunWith(classOf[InstrumentationTestRunner]) +@InstrumentationTestConfig(includePrefixes = Array("scala", "akka")) +class AkkaHttpCoreTest { + + implicit val system: ActorSystem = ActorSystem() + implicit val materializer: ActorMaterializer = ActorMaterializer() + + val akkaServer = new AkkaServer() + val playServer = new PlayServer() + var port: Int = InstrumentationTestRunner.getIntrospector.getRandomPort + val baseUrl: String = "http://localhost:" + port + + @Test + def syncHandlerAkkaServerTest(): Unit = { + akkaServer.start(port, async = false) + + Http().singleRequest(HttpRequest(uri = baseUrl + "/ping")) + + val introspector: Introspector = InstrumentationTestRunner.getIntrospector + awaitFinishedTx(introspector, 1) + akkaServer.stop() + Assert.assertEquals(1, introspector.getFinishedTransactionCount()) + val txName = introspector.getTransactionNames.iterator.next + Assert.assertEquals("WebTransaction/AkkaHttpCore/akkaHandler", txName) + } + + @Test + def asyncHandlerAkkaServerTest(): Unit = { + akkaServer.start(port, async = true) + Http().singleRequest(HttpRequest(uri = baseUrl + "/asyncPing")) + + val introspector: Introspector = InstrumentationTestRunner.getIntrospector + awaitFinishedTx(introspector, 1) + akkaServer.stop() + Assert.assertEquals(1, introspector.getFinishedTransactionCount()) + val txName = introspector.getTransactionNames.iterator.next + Assert.assertEquals("WebTransaction/AkkaHttpCore/akkaHandler", txName) + } + + @Test + def syncHandlerPlayServerTest(): Unit = { + playServer.start(port, async = false) + Http().singleRequest(HttpRequest(uri = baseUrl + "/ping")) + + val introspector: Introspector = InstrumentationTestRunner.getIntrospector + awaitFinishedTx(introspector, 1) + playServer.stop() + Assert.assertEquals(1, introspector.getFinishedTransactionCount()) + val txName = introspector.getTransactionNames.iterator.next + Assert.assertEquals("WebTransaction/AkkaHttpCore/akkaHandler", txName) + } + + @Test + def asyncHandlerPlayServerTest(): Unit = { + playServer.start(port, async = true) + + Http().singleRequest(HttpRequest(uri = baseUrl + "/asyncPing")) + + val introspector: Introspector = InstrumentationTestRunner.getIntrospector + awaitFinishedTx(introspector, 1) + playServer.stop() + Assert.assertEquals(1, introspector.getFinishedTransactionCount()) + val txName = introspector.getTransactionNames.iterator.next + Assert.assertEquals("WebTransaction/AkkaHttpCore/akkaHandler", txName) + } + + @Test + def syncHandlerPlayServerCatTest(): Unit = { + playServer.start(port, async = false) + + val idHeader = Obfuscator.obfuscateNameUsingKey("1xyz234#1xyz3333", "cafebabedeadbeef8675309babecafe1beefdead") + + val requestHeaders: Seq[HttpHeader] = List(new RawHeader(HeadersUtil.NEWRELIC_ID_HEADER, idHeader)) + val responseFuture: Future[HttpResponse] = + Http().singleRequest(HttpRequest(uri = baseUrl + "/ping", headers = collection.immutable.Seq[HttpHeader](requestHeaders: _*))) + + val result: HttpResponse = Await.result(responseFuture, new DurationInt(10).seconds) + var hasNewRelicHeader: Boolean = false + hasNewRelicHeader = result.headers.exists(header => header.name().contains(HeadersUtil.NEWRELIC_APP_DATA_HEADER)) + + Assert.assertTrue(hasNewRelicHeader) + + val introspector: Introspector = InstrumentationTestRunner.getIntrospector + awaitFinishedTx(introspector, 1) + playServer.stop() + Assert.assertEquals(1, introspector.getFinishedTransactionCount()) + val txName = introspector.getTransactionNames.iterator.next + Assert.assertEquals("WebTransaction/AkkaHttpCore/akkaHandler", txName) + } + + @Test + def asyncHandlerPlayServerCatTest(): Unit = { + playServer.start(port, async = true) + + val idHeader = Obfuscator.obfuscateNameUsingKey("1xyz234#1xyz3333", "cafebabedeadbeef8675309babecafe1beefdead") + val requestHeaders: Seq[HttpHeader] = List(new RawHeader(HeadersUtil.NEWRELIC_ID_HEADER, idHeader)) + + val responseFuture: Future[HttpResponse] = + Http().singleRequest(HttpRequest(uri = baseUrl + "/asyncPing", headers = collection.immutable.Seq[HttpHeader](requestHeaders: _*))) + + val result: HttpResponse = Await.result(responseFuture, new DurationInt(1).seconds) + var hasNewRelicHeader: Boolean = false + hasNewRelicHeader = result.headers.exists(header => header.name().contains(HeadersUtil.NEWRELIC_APP_DATA_HEADER)) + Assert.assertTrue(hasNewRelicHeader) + + val introspector: Introspector = InstrumentationTestRunner.getIntrospector + awaitFinishedTx(introspector, 1) + playServer.stop() + Assert.assertEquals(1, introspector.getFinishedTransactionCount()) + val txName = introspector.getTransactionNames.iterator.next + Assert.assertEquals("WebTransaction/AkkaHttpCore/akkaHandler", txName) + } + + @Test + def syncHandlerAkkaServerCatTest(): Unit = { + akkaServer.start(port, async = false) + + val idHeader = Obfuscator.obfuscateNameUsingKey("1xyz234#1xyz3333", "cafebabedeadbeef8675309babecafe1beefdead") + val requestHeaders: Seq[HttpHeader] = List(new RawHeader(HeadersUtil.NEWRELIC_ID_HEADER, idHeader)) + + val responseFuture: Future[HttpResponse] = + Http().singleRequest(HttpRequest(uri = baseUrl + "/ping", headers = collection.immutable.Seq[HttpHeader](requestHeaders: _*))) + + val result: HttpResponse = Await.result(responseFuture, new DurationInt(10).seconds) + var hasNewRelicHeader: Boolean = false + hasNewRelicHeader = result.headers.exists(header => header.name().contains(HeadersUtil.NEWRELIC_APP_DATA_HEADER)) + Assert.assertTrue(hasNewRelicHeader) + + val introspector: Introspector = InstrumentationTestRunner.getIntrospector + awaitFinishedTx(introspector, 1) + akkaServer.stop() + Assert.assertEquals(1, introspector.getFinishedTransactionCount()) + val txName = introspector.getTransactionNames.iterator.next + Assert.assertEquals("WebTransaction/AkkaHttpCore/akkaHandler", txName) + } + + @Test + def asyncHandlerAkkaServerCatTest(): Unit = { + akkaServer.start(port, async = true) + + val idHeader = Obfuscator.obfuscateNameUsingKey("1xyz234#1xyz3333", "cafebabedeadbeef8675309babecafe1beefdead") + val requestHeaders: Seq[HttpHeader] = List(new RawHeader(HeadersUtil.NEWRELIC_ID_HEADER, idHeader)) + + val responseFuture: Future[HttpResponse] = + Http().singleRequest(HttpRequest(uri = baseUrl + "/asyncPing", headers = collection.immutable.Seq[HttpHeader](requestHeaders: _*))) + + val result: HttpResponse = Await.result(responseFuture, new DurationInt(1).seconds) + var hasNewRelicHeader: Boolean = false + hasNewRelicHeader = result.headers.exists(header => header.name().contains(HeadersUtil.NEWRELIC_APP_DATA_HEADER)) + Assert.assertTrue(hasNewRelicHeader) + + val introspector: Introspector = InstrumentationTestRunner.getIntrospector + awaitFinishedTx(introspector, 1) + akkaServer.stop() + Assert.assertEquals(1, introspector.getFinishedTransactionCount()) + val txName = introspector.getTransactionNames.iterator.next + Assert.assertEquals("WebTransaction/AkkaHttpCore/akkaHandler", txName) + } + + @Test + def asyncHandlerAkkaServerHttpClientTest(): Unit = { + akkaServer.start(port, async = true) + makeHttpRequest(true) + + verifyHttpClientMetrics() + } + + @Test + def syncHandlerAkkaServerHttpClientTest(): Unit = { + akkaServer.start(port, async = false) + makeHttpRequest(false) + + verifyHttpClientMetrics() + } + + def verifyHttpClientMetrics() = { + val introspector: Introspector = InstrumentationTestRunner.getIntrospector + awaitFinishedTx(introspector, 2) + akkaServer.stop() + Assert.assertEquals(2, introspector.getFinishedTransactionCount()) + val txNames = introspector.getTransactionNames + + val clientTx = "OtherTransaction/Custom/com.agent.instrumentation.akka.http.core.AkkaHttpCoreTest/makeHttpRequest" + val serverTx = "WebTransaction/AkkaHttpCore/akkaHandler" + + // The @Trace(dispatcher = true) below where the Http call is made + Assert.assertTrue(txNames.contains(clientTx)) + // The WebTransaction from the AkkaHttp server itself + Assert.assertTrue(txNames.contains(serverTx)) + + Assert.assertEquals(1, MetricsHelper.getScopedMetricCount(clientTx, "External/localhost/AkkaHttpClient/GET")) + Assert.assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/AkkaHttpClient/GET")) + + // external rollups + Assert.assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/all")) + Assert.assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/allOther")) + Assert.assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/all")) + + // verify timing of External/all metrics + val externalMetrics: TracedMetricData = InstrumentationTestRunner.getIntrospector.getUnscopedMetrics.get("External/all") + assertNotNull(externalMetrics) + assertTrue(externalMetrics.getTotalTimeInSec > 0) + + val transactionEvents: util.Collection[TransactionEvent] = introspector.getTransactionEvents(clientTx) + Assert.assertEquals(1, transactionEvents.size) + val transactionEvent: TransactionEvent = transactionEvents.iterator.next + Assert.assertEquals(1, transactionEvent.getExternalCallCount) + Assert.assertTrue(transactionEvent.getExternalDurationInSec > 0) + + val externalRequests: util.Collection[ExternalRequest] = introspector.getExternalRequests(clientTx) + Assert.assertEquals(1, externalRequests.size) + val externalRequest: ExternalRequest = externalRequests.iterator.next + Assert.assertEquals(1, externalRequest.getCount) + Assert.assertEquals("localhost", externalRequest.getHostname) + Assert.assertEquals("AkkaHttpClient", externalRequest.getLibrary) + Assert.assertEquals("GET", externalRequest.getOperation) + } + + @Trace(dispatcher = true, nameTransaction = true) + private def makeHttpRequest(async: Boolean): Unit = { + Http().singleRequest(HttpRequest(uri = baseUrl + (if (async) "/asyncPing" else "/ping"))) + } + + private def awaitFinishedTx(introspector: Introspector, expectedTxCount: Int) = { + val timeout = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(30) // 30 seconds from now + while (System.currentTimeMillis() < timeout && introspector.getFinishedTransactionCount() <= expectedTxCount - 1) { + Thread.sleep(100) + } + Thread.sleep(100) + } +} diff --git a/instrumentation/akka-http-core-2.13_10.2.0/src/test/scala/com/agent/instrumentation/akka/http/core/AkkaServer.scala b/instrumentation/akka-http-core-2.13_10.2.0/src/test/scala/com/agent/instrumentation/akka/http/core/AkkaServer.scala new file mode 100644 index 0000000000..a02b9a6154 --- /dev/null +++ b/instrumentation/akka-http-core-2.13_10.2.0/src/test/scala/com/agent/instrumentation/akka/http/core/AkkaServer.scala @@ -0,0 +1,77 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http.core + +import akka.actor.ActorSystem +import akka.event.Logging +import akka.http.scaladsl.Http +import akka.http.scaladsl.model.HttpMethods._ +import akka.http.scaladsl.model._ +import akka.stream.ActorMaterializer +import akka.stream.scaladsl.{Source, _} +import akka.util.Timeout +import com.typesafe.config.ConfigFactory + +import scala.concurrent.duration._ +import scala.concurrent.{Await, Future} +import scala.language.postfixOps + +//how the akka http core docs' example sets up a server +class AkkaServer() { + implicit val system = ActorSystem() + implicit val executor = system.dispatcher + implicit val materializer = ActorMaterializer() + implicit val timeout: Timeout = 3 seconds + + val config = ConfigFactory.load() + val logger = Logging(system, getClass) + + var serverSource: Source[Http.IncomingConnection, Future[Http.ServerBinding]] = _ + var bindingFuture: Future[Http.ServerBinding] = _ + + def start(port: Int, async: Boolean) = { + + serverSource = Http().bind(interface = "localhost", port) + + if (async) { + + val asyncRequestHandler: HttpRequest => Future[HttpResponse] = { + case HttpRequest(GET, Uri.Path("/asyncPing"), _, _, _) => Future[HttpResponse](HttpResponse(entity = "Hoops!")) + } + + bindingFuture = serverSource.to(Sink.foreach { + connection => + println("accepted connection from: " + connection.remoteAddress) + connection handleWithAsyncHandler asyncRequestHandler + }).run() + } + else { + + val requestHandler: HttpRequest => HttpResponse = { + case HttpRequest(GET, Uri.Path("/ping"), _, _, _) => + HttpResponse(entity = "Hoops!") + } + + bindingFuture = serverSource.to(Sink.foreach { + connection => + println("accepted connection from: " + connection.remoteAddress) + connection handleWithSyncHandler requestHandler + }).run() + } + + Await.ready({ + bindingFuture + }, timeout.duration) + } + + def stop() = { + if (bindingFuture != null) { + bindingFuture.flatMap(_.unbind()).onComplete(_ => system.terminate()) + } + } +} diff --git a/instrumentation/akka-http-core-2.13_10.2.0/src/test/scala/com/agent/instrumentation/akka/http/core/PlayServer.scala b/instrumentation/akka-http-core-2.13_10.2.0/src/test/scala/com/agent/instrumentation/akka/http/core/PlayServer.scala new file mode 100644 index 0000000000..2a950ec591 --- /dev/null +++ b/instrumentation/akka-http-core-2.13_10.2.0/src/test/scala/com/agent/instrumentation/akka/http/core/PlayServer.scala @@ -0,0 +1,70 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http.core + +import akka.actor.ActorSystem +import akka.event.Logging +import akka.http.scaladsl.Http +import akka.http.scaladsl.model.HttpMethods._ +import akka.http.scaladsl.model._ +import akka.stream.ActorMaterializer +import akka.stream.scaladsl.{Source, _} +import akka.util.Timeout +import com.typesafe.config.ConfigFactory + +import scala.concurrent.duration._ +import scala.concurrent.{Await, Future} +import scala.language.postfixOps + +//how play 2.6 sets up a server +class PlayServer() { + implicit val system = ActorSystem() + implicit val executor = system.dispatcher + implicit val materializer = ActorMaterializer() + implicit val timeout: Timeout = 3 seconds + + val config = ConfigFactory.load() + val logger = Logging(system, getClass) + + var bindingFuture: Future[Http.ServerBinding] = _ + + def start(port: Int, async: Boolean) = { + + if (async) { + + val asyncRequestHandler: HttpRequest => Future[HttpResponse] = { + case HttpRequest(GET, Uri.Path("/asyncPing"), _, _, _) => + Future[HttpResponse](HttpResponse(entity = "Hoops!")) + } + + bindingFuture = Http().bindAndHandleAsync(asyncRequestHandler, interface = "localhost", port) + + } + else { + + val requestHandler: HttpRequest => HttpResponse = { + case HttpRequest(GET, Uri.Path("/ping"), _, _, _) => + HttpResponse(entity = "Boops!") + } + + bindingFuture = Http().bindAndHandleSync(requestHandler, interface = "localhost", port) + } + + Await.ready({ + bindingFuture + }, timeout.duration) + } + + def stop() = { + if (bindingFuture != null) { + bindingFuture.flatMap(_.unbind()).onComplete(_ => { + system.terminate() + }) + } + } +} diff --git a/settings.gradle b/settings.gradle index 5ec57bb693..3c6156b5a5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -62,6 +62,7 @@ include 'instrumentation:akka-http-core-10.0' include 'instrumentation:akka-http-core-2.11_10.0.11' include 'instrumentation:akka-http-core-2.13_10.1.8' include 'instrumentation:akka-http-core-10.2.0' +include 'instrumentation:akka-http-core-2.13_10.2.0' include 'instrumentation:async-http-client-2.0.0' include 'instrumentation:async-http-client-2.1.0' include 'instrumentation:cassandra-datastax-2.1.2' From 7ac68b674cf99baaabd6fe419188cb005fe8ac9c Mon Sep 17 00:00:00 2001 From: Thomas Spring Date: Thu, 10 Dec 2020 16:15:58 -0800 Subject: [PATCH 2/5] additional verifier ranges for projects with separate scala 2.13 to ensure no overlap --- instrumentation/akka-2.2/build.gradle | 1 + instrumentation/akka-http-core-10.2.0/build.gradle | 3 +++ instrumentation/akka-http-core-2.13_10.1.8/build.gradle | 3 +++ 3 files changed, 7 insertions(+) diff --git a/instrumentation/akka-2.2/build.gradle b/instrumentation/akka-2.2/build.gradle index a5fce03ca5..7c17a62a7a 100644 --- a/instrumentation/akka-2.2/build.gradle +++ b/instrumentation/akka-2.2/build.gradle @@ -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' } diff --git a/instrumentation/akka-http-core-10.2.0/build.gradle b/instrumentation/akka-http-core-10.2.0/build.gradle index b9f2ebb623..14cae9c9db 100644 --- a/instrumentation/akka-http-core-10.2.0/build.gradle +++ b/instrumentation/akka-http-core-10.2.0/build.gradle @@ -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 { diff --git a/instrumentation/akka-http-core-2.13_10.1.8/build.gradle b/instrumentation/akka-http-core-2.13_10.1.8/build.gradle index 2f37f03a99..5e49e676e2 100644 --- a/instrumentation/akka-http-core-2.13_10.1.8/build.gradle +++ b/instrumentation/akka-http-core-2.13_10.1.8/build.gradle @@ -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 { From d011bf42a430a36db857bd89c43a282f8a6c92ff Mon Sep 17 00:00:00 2001 From: Thomas Spring Date: Thu, 10 Dec 2020 16:23:26 -0800 Subject: [PATCH 3/5] Remove incorrect code comment --- .../main/scala/akka/http/scaladsl/HttpExtInstrumentation.java | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/HttpExtInstrumentation.java b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/HttpExtInstrumentation.java index ebf182f222..ad13320cd6 100644 --- a/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/HttpExtInstrumentation.java +++ b/instrumentation/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/HttpExtInstrumentation.java @@ -51,7 +51,6 @@ public Future bindAndHandleSync( return Weaver.callOriginal(); } - // We are weaving the singleRequestImpl method here rather than just singleRequest because the javadsl only flows through here public Future singleRequest(HttpRequest httpRequest, HttpsConnectionContext connectionContext, ConnectionPoolSettings poolSettings, LoggingAdapter loggingAdapter) { final Segment segment = NewRelic.getAgent().getTransaction().startSegment("Akka", "singleRequest"); From a9eb488e26153fa31b5dadb3e03e2c6c0de1a03b Mon Sep 17 00:00:00 2001 From: Thomas Spring Date: Thu, 10 Dec 2020 17:58:19 -0800 Subject: [PATCH 4/5] Fix test by upgrading akka-streams dep --- instrumentation/akka-http-core-2.13_10.2.0/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/akka-http-core-2.13_10.2.0/build.gradle b/instrumentation/akka-http-core-2.13_10.2.0/build.gradle index 52695d6e69..cf6db8ed2f 100644 --- a/instrumentation/akka-http-core-2.13_10.2.0/build.gradle +++ b/instrumentation/akka-http-core-2.13_10.2.0/build.gradle @@ -7,7 +7,7 @@ jar { 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.5.23") + implementation("com.typesafe.akka:akka-stream_2.13:2.6.10") testImplementation(project(":instrumentation:akka-2.2")) { transitive = false } } From c5af0a66a572bd13e7ffc0a6618f215001c4977d Mon Sep 17 00:00:00 2001 From: Thomas Spring Date: Fri, 11 Dec 2020 13:23:23 -0800 Subject: [PATCH 5/5] remove extra whitespace Co-authored-by: jack-berg <34418638+jack-berg@users.noreply.github.com> --- instrumentation/akka-http-core-2.13_10.2.0/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/akka-http-core-2.13_10.2.0/build.gradle b/instrumentation/akka-http-core-2.13_10.2.0/build.gradle index cf6db8ed2f..bcdf86cd0b 100644 --- a/instrumentation/akka-http-core-2.13_10.2.0/build.gradle +++ b/instrumentation/akka-http-core-2.13_10.2.0/build.gradle @@ -20,7 +20,6 @@ verifyInstrumentation { 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 {