-
Notifications
You must be signed in to change notification settings - Fork 144
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
Add new akka-http-2.13_10.1.8 instrumentation module #271
Changes from all commits
f62fac7
0a1ea01
d63a60b
3303b31
e84acf4
504470d
fdf4d31
89e7c8c
1b58524
8c3ae8b
b2debbc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ sourceSets.test.scala.srcDir "src/test/java" | |
sourceSets.test.java.srcDirs = [] | ||
|
||
jar { | ||
manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.akka-http-2.4.5' } | ||
manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.akka-http-2.11_2.4.5' } | ||
} | ||
|
||
dependencies { | ||
|
@@ -21,18 +21,21 @@ dependencies { | |
|
||
verifyInstrumentation { | ||
passesOnly 'com.typesafe.akka:akka-http-experimental_2.11:[2.4.5,)' | ||
passesOnly 'com.typesafe.akka:akka-http-experimental_2.10:[2.4.5,)' | ||
passesOnly 'com.typesafe.akka:akka-http_2.10:[2.4.5,)' | ||
passesOnly('com.typesafe.akka:akka-http_2.11:[2.4.5,)') { | ||
passesOnly('com.typesafe.akka:akka-http_2.11:[10.0.0,)') { | ||
compile("com.typesafe.akka:akka-stream_2.11:2.5.19") | ||
} | ||
passesOnly('com.typesafe.akka:akka-http_2.12:[2.4.5,)') { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed the Scala 2.10 verifier checks from the old instrumentation module because there are no 2.10 compiled versions >= I also updated the start range for Scala 2.11 and 2.12 because |
||
passesOnly('com.typesafe.akka:akka-http_2.12:[10.0.0,)') { | ||
compile("com.typesafe.akka:akka-stream_2.11:2.5.19") | ||
} | ||
fails('com.typesafe.akka:akka-http_2.13:[10.1.8,)') | ||
|
||
excludeRegex 'com.typesafe.akka:akka-http-experimental_2.11:.*(RC|M)[0-9]*$' | ||
excludeRegex 'com.typesafe.akka:akka-http_2.11:.*(RC|M)[0-9]*$' | ||
excludeRegex 'com.typesafe.akka:akka-http_2.12:.*(RC|M)[0-9]*$' | ||
excludeRegex 'com.typesafe.akka:akka-http_2.13:.*(RC|M)[0-9]*$' | ||
excludeRegex 'com.typesafe.akka:akka-http_2.11:.*-[0-9a-f]{8}$' | ||
excludeRegex 'com.typesafe.akka:akka-http_2.12:.*-[0-9a-f]{8}$' | ||
excludeRegex 'com.typesafe.akka:akka-http_2.13:.*-[0-9a-f]{8}$' | ||
} | ||
|
||
site { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
apply plugin: 'scala' | ||
|
||
sourceSets.test.scala.srcDir "src/test/java" | ||
sourceSets.test.java.srcDirs = [] | ||
|
||
jar { | ||
manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.akka-http-2.13_10.1.8' } | ||
} | ||
|
||
dependencies { | ||
implementation(project(":agent-bridge")) | ||
implementation("com.typesafe.akka:akka-http_2.13:10.1.8") | ||
implementation("com.typesafe.akka:akka-stream_2.13:2.5.23") | ||
implementation("com.typesafe.akka:akka-actor_2.13:2.5.23") | ||
|
||
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("javax.xml.bind:jaxb-api:2.3.0") | ||
} | ||
|
||
verifyInstrumentation { | ||
passesOnly('com.typesafe.akka:akka-http_2.13:[10.1.8,)') { | ||
compile("com.typesafe.akka:akka-stream_2.13:2.5.23") | ||
} | ||
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' | ||
} | ||
|
||
test { | ||
// our dependency on rest-assured precludes running the tests on java 1.7 due to | ||
// akka http only being able to run on java 1.8 and above from this version onward | ||
onlyIf { | ||
!project.hasProperty("test7") | ||
} | ||
} |
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.http.PathMatcherUtils; | ||
import com.agent.instrumentation.akka.http.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.http.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.http.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.http.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; | ||
} | ||
} | ||
} |
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.server | ||
|
||
import akka.event.LoggingAdapter | ||
import akka.http.scaladsl.marshalling.AkkaHttpToResponseMarshallable | ||
import akka.http.scaladsl.model._ | ||
import akka.http.scaladsl.settings.{ParserSettings, RoutingSettings} | ||
import akka.stream.Materializer | ||
import com.agent.instrumentation.akka.http.PathMatcherUtils | ||
import com.newrelic.api.agent.weaver.{Weave, Weaver} | ||
|
||
import java.util.concurrent.LinkedBlockingDeque | ||
import java.util.concurrent.atomic.{AtomicBoolean, AtomicInteger} | ||
import scala.collection.mutable | ||
import scala.concurrent.{ExecutionContextExecutor, Future} | ||
|
||
@Weave(originalName = "akka.http.scaladsl.server.RequestContextImpl") | ||
abstract class AkkaHttpRequestContext(request: HttpRequest, | ||
unmatchedPath: Uri.Path, | ||
executionContext: ExecutionContextExecutor, | ||
materializer: Materializer, | ||
log: LoggingAdapter, | ||
settings: RoutingSettings, | ||
parserSettings: ParserSettings) { | ||
|
||
def complete(trm: AkkaHttpToResponseMarshallable): Future[RouteResult] = { | ||
val contextWrapper = PathMatcherUtils.nrRequestContext.get() | ||
if (trm != null && contextWrapper != null) { | ||
trm.token = contextWrapper.token | ||
} | ||
Weaver.callOriginal() // This ends up calling complete on our NewRelicRequestContextWrapper | ||
} | ||
|
||
def reconfigure(executionContext: ExecutionContextExecutor, materializer: Materializer, log: LoggingAdapter, settings: RoutingSettings): RequestContext = { | ||
Weaver.callOriginal() | ||
} | ||
|
||
private def copy(request: HttpRequest, | ||
unmatchedPath: Uri.Path, | ||
executionContext: ExecutionContextExecutor, | ||
materializer: Materializer, | ||
log: LoggingAdapter, | ||
settings: RoutingSettings, | ||
parserSettings: ParserSettings): RequestContextImpl = { | ||
return new NewRelicRequestContextWrapper(this, Weaver.callOriginal(), null, new LinkedBlockingDeque[String](), | ||
new AtomicBoolean(false), new AtomicInteger(0), new AtomicInteger(0), new LinkedBlockingDeque[String], new mutable.HashSet[String], request, | ||
unmatchedPath, executionContext, materializer, log, settings, parserSettings) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed the module to make the Scala version clear.