Skip to content

Commit

Permalink
Merge pull request #363 from richard-gibson/http4s-blaze
Browse files Browse the repository at this point in the history
Http4s blaze
  • Loading branch information
twcrone authored Aug 20, 2021
2 parents 8422a9f + b11c24b commit f2e18ca
Show file tree
Hide file tree
Showing 66 changed files with 2,796 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ ExitTracer createTracer(Object invocationTarget, int signatureId, boolean dispat
*/
ExitTracer createSqlTracer(Object invocationTarget, int signatureId, String metricName, int flags);

ExitTracer createScalaTxnTracer();

/**
* Returns the current transaction. This should not be called directly - instead use {@link Agent#getTransaction()}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,9 @@ public ExitTracer createTracer(Object invocationTarget, int signatureId, boolean
public ExitTracer createSqlTracer(Object invocationTarget, int signatureId, String metricName, int flags) {
return null;
}

@Override
public ExitTracer createScalaTxnTracer() {
return null;
}
}
2 changes: 2 additions & 0 deletions instrumentation/akka-http-2.11_2.4.5/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ jar {

dependencies {
implementation(project(":agent-bridge"))
implementation(project(":newrelic-weaver-api"))
implementation(project(":newrelic-weaver-scala-api"))
implementation("com.typesafe.akka:akka-http_2.11:10.1.8")
implementation("com.typesafe.akka:akka-stream_2.11:2.5.19")
implementation("com.typesafe.akka:akka-actor_2.11:2.5.19")
Expand Down
2 changes: 2 additions & 0 deletions instrumentation/akka-http-2.13_10.1.8/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ jar {

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.1.8")
implementation("com.typesafe.akka:akka-stream_2.13:2.5.23")
implementation("com.typesafe.akka:akka-actor_2.13:2.5.23")
Expand Down
26 changes: 26 additions & 0 deletions instrumentation/cats-effect-2/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
apply plugin: 'scala'

dependencies {
implementation(project(":newrelic-api"))
implementation(project(":agent-bridge"))
implementation(project(":newrelic-weaver-api"))
implementation("org.typelevel:cats-effect_2.13:2.5.1")
implementation("org.scala-lang:scala-library:2.13.3")
}

jar {
manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.cats-effect-2',
'Implementation-Title-Alias': 'cats-effect_instrumentation' }
}

verifyInstrumentation {
passes 'org.typelevel:cats-effect_2.13:[2.1,)'
passes 'org.typelevel:cats-effect_2.12:[2.1,)'
excludeRegex 'org.typelevel:cats-effect_2.1(2|3):3.*'
excludeRegex '.*(RC|M)[0-9]*'
}

site {
title 'Scala'
type 'Other'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package cats.effect.internals;

import com.newrelic.agent.bridge.AgentBridge;
import com.newrelic.api.agent.weaver.NewField;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.Weaver;

import static cats.effect.internals.Utils.clearThreadTokenAndRefCountAndTxn;
import static cats.effect.internals.Utils.setThreadTokenAndRefCount;
import static cats.effect.internals.Utils.getThreadTokenAndRefCount;
import static cats.effect.internals.Utils.logTokenInfo;

@Weave(originalName = "cats.effect.internals.IOShift")
public class IOShift {

@Weave(originalName = "cats.effect.internals.IOShift$Tick")
public static class Tick {
@NewField
private AgentBridge.TokenAndRefCount tokenAndRefCount;

public Tick(scala.Function1<scala.util.Either<java.lang.Throwable, scala.runtime.BoxedUnit>,
scala.runtime.BoxedUnit> cb) {
this.tokenAndRefCount = getThreadTokenAndRefCount();
logTokenInfo(tokenAndRefCount, "IOTick token info set");
}

public void run() {
try {
setThreadTokenAndRefCount(this.tokenAndRefCount);
logTokenInfo(tokenAndRefCount, "Token info set in thread");
Weaver.callOriginal();
} finally {
logTokenInfo(tokenAndRefCount, "Clearing token info from thread ");
clearThreadTokenAndRefCountAndTxn(this.tokenAndRefCount);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package cats.effect.internals;

import com.newrelic.agent.bridge.AgentBridge;
import com.newrelic.api.agent.weaver.NewField;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.Weaver;
import scala.Function1;
import scala.concurrent.ExecutionContext;
import scala.runtime.BoxedUnit;
import scala.util.Either;

import static cats.effect.internals.Utils.clearThreadTokenAndRefCountAndTxn;
import static cats.effect.internals.Utils.setThreadTokenAndRefCount;
import static cats.effect.internals.Utils.getThreadTokenAndRefCount;
import static cats.effect.internals.Utils.logTokenInfo;

@Weave(originalName = "cats.effect.internals.IOTimer")
public class IOTimer {

@Weave(originalName = "cats.effect.internals.IOTimer$ShiftTick")
public static class ShiftTick {
@NewField
private AgentBridge.TokenAndRefCount tokenAndRefCount;

public ShiftTick(final IOConnection conn, final Function1<Either<Throwable, BoxedUnit>, BoxedUnit> cb,
final ExecutionContext ec) {
this.tokenAndRefCount = getThreadTokenAndRefCount();
logTokenInfo(tokenAndRefCount, "IOTick token info set");
}

public void run() {
try {
setThreadTokenAndRefCount(this.tokenAndRefCount);
logTokenInfo(tokenAndRefCount, "Token info set in thread");
Weaver.callOriginal();
} finally {
logTokenInfo(tokenAndRefCount, "Clearing token info from thread ");
clearThreadTokenAndRefCountAndTxn(this.tokenAndRefCount);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package cats.effect.internals;

import com.newrelic.agent.bridge.AgentBridge;
import com.newrelic.agent.bridge.Transaction;

import java.text.MessageFormat;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;

public final class Utils {
private Utils() {}
public static AgentBridge.TokenAndRefCount getThreadTokenAndRefCount() {
AgentBridge.TokenAndRefCount tokenAndRefCount = AgentBridge.activeToken.get();
if (tokenAndRefCount == null) {
Transaction tx = AgentBridge.getAgent().getTransaction(false);
if (tx != null) {
tokenAndRefCount = new AgentBridge.TokenAndRefCount(tx.getToken(),
AgentBridge.getAgent().getTracedMethod(), new AtomicInteger(1));
}
} else {
tokenAndRefCount.refCount.incrementAndGet();
}
return tokenAndRefCount;
}

public static void setThreadTokenAndRefCount(AgentBridge.TokenAndRefCount tokenAndRefCount) {
if (tokenAndRefCount != null) {
AgentBridge.activeToken.set(tokenAndRefCount);
tokenAndRefCount.token.link();
}
}

public static void clearThreadTokenAndRefCountAndTxn(AgentBridge.TokenAndRefCount tokenAndRefCount) {
AgentBridge.activeToken.remove();
if (tokenAndRefCount != null && tokenAndRefCount.refCount.decrementAndGet() == 0) {
tokenAndRefCount.token.expire();
tokenAndRefCount.token = null;
}
}

public static void logTokenInfo(AgentBridge.TokenAndRefCount tokenAndRefCount, String msg) {
if (AgentBridge.getAgent().getLogger().isLoggable(Level.FINEST)) {
String tokenMsg = (tokenAndRefCount != null && tokenAndRefCount.token != null)
? String.format("[%s:%s:%d]", tokenAndRefCount.token, tokenAndRefCount.token.getTransaction(),
tokenAndRefCount.refCount.get())
: "[Empty token]";
AgentBridge.getAgent().getLogger().log(Level.FINEST, MessageFormat.format("{0}: token info {1}", tokenMsg, msg));
}
}
}
32 changes: 32 additions & 0 deletions instrumentation/http4s-blaze-client-2.12_0.21/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
apply plugin: 'scala'

dependencies {
implementation(project(":newrelic-api"))
implementation(project(":agent-bridge"))
implementation(project(":newrelic-weaver-api"))
implementation(project(":newrelic-weaver-scala-api"))
implementation("org.scala-lang:scala-library:2.12.14")
implementation('org.http4s:http4s-blaze-server_2.12:0.21.24')
implementation('org.http4s:http4s-blaze-client_2.12:0.21.24')
testImplementation(project(":instrumentation:cats-effect-2")) { transitive = false }
testImplementation(project(":instrumentation:newrelic-scala-cats-api")) { transitive = false }
testImplementation(project(":newrelic-scala-cats-api"))
testImplementation("org.http4s:http4s-dsl_2.12:0.21.24")

}

jar {
manifest {
attributes 'Implementation-Title': 'com.newrelic.instrumentation.http4s-blaze-client-2.12_0.21'
attributes 'Implementation-Vendor': 'New Relic'
attributes 'Implementation-Vendor-Id': 'com.newrelic'
attributes 'Implementation-Version': 1.0
}
}
verifyInstrumentation {
passes 'org.http4s:http4s-blaze-client_2.12:[0.21,0.22)'
excludeRegex '.*(RC|M)[0-9]*'
}

sourceSets.main.scala.srcDirs = ['src/main/scala', 'src/main/java']
sourceSets.main.java.srcDirs = []
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.http4s;

import cats.effect.ConcurrentEffect;
import cats.effect.Resource;
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.http4s.NewrelicClientMiddleware$;
import org.http4s.client.Client;

@Weave(type = MatchType.ExactClass, originalName = "org.http4s.client.blaze.BlazeClientBuilder")
public abstract class BlazeClientBuilder_Instrumentation<F> {

public ConcurrentEffect F() {
return Weaver.callOriginal();
}

public Resource<F, Client<F>> resource() {
Resource<F, Client<F>> delegateResource = Weaver.callOriginal();
return NewrelicClientMiddleware$.MODULE$.resource(delegateResource, F());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.nr.instrumentation.http4s

import com.newrelic.api.agent.{ExtendedInboundHeaders, HeaderType}
import org.http4s.Response
import org.http4s.util.CaseInsensitiveString

import collection.JavaConverters._
import java.util

class InboundResponseWrapper[F[_]](response: Response[F]) extends ExtendedInboundHeaders {
/**
* Return the type of header key syntax used for this.
*
* @return An <code>enum</code> specifying the type of headers present.
* @since 3.5.0
*/
override def getHeaderType: HeaderType = HeaderType.HTTP

/**
* Returns the value of the specified request header as a <code>String</code>. If the request does not include a
* header with the specified input name, then this method returns <code>null</code>.
*
* @param name The name of the desired request header.
* @return A <code>String</code> containing the value of the specified input request header, or <code>null</code> if the request header is not present.
* @since 3.5.0
*/
override def getHeader(name: String): String =
response.headers.find(_.name == CaseInsensitiveString(name)).map(_.value).orNull

override def getHeaders(name: String): util.List[String] =
response.headers.toList.map(_.name.toString()).asJava
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.nr.instrumentation.http4s

import cats.effect.{ConcurrentEffect, Resource, Sync}
import org.http4s.Request
import com.newrelic.agent.bridge.AgentBridge
import com.newrelic.api.agent.HttpParameters
import org.http4s.client.Client

import java.net.URI

object NewrelicClientMiddleware {
def construct[F[_] : Sync, T](t: T): F[T] = Sync[F].delay(t)

def clientResource[F[_] : ConcurrentEffect](client: Client[F]): Client[F] =
Client { req: Request[F] =>
for {
seg <- Resource.liftF(
construct {
val txn = AgentBridge.getAgent.getTransaction
val segment = txn.startSegment("HTTP4S client call")
segment.addOutboundRequestHeaders(new OutboundRequestWrapper(req))
segment
})
response <- client.run(req)
newRes <- Resource.liftF(
ConcurrentEffect[F].handleErrorWith
(construct {
seg.reportAsExternal(HttpParameters
.library("HTTP4S")
.uri(new URI(req.uri.toString()))
.procedure(req.method.toString())
.inboundHeaders(new InboundResponseWrapper(response))
.build())
seg.end()
response
})(_ => construct(response))
)
} yield newRes
}

def resource[F[_] : ConcurrentEffect](delegate: Resource[F, Client[F]]): Resource[F, Client[F]] =
delegate.map(clientResource(_))
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.nr.instrumentation.http4s

import com.newrelic.api.agent.{HeaderType, OutboundHeaders}
import org.http4s.Request

class OutboundRequestWrapper[F[_]](val request: Request[F]) extends OutboundHeaders {
/**
* Return the type of header key syntax used for this.
*
* @return An <code>enum</code> specifying the type of headers present.
* @since 3.5.0
*/
override def getHeaderType: HeaderType = HeaderType.HTTP

/**
* Sets a response header with the given name and value.
* NO-OP HTTP4s Request Headers are immutable and so can't be set from here
*/
override def setHeader(name: String, value: String): Unit = {
}
}
33 changes: 33 additions & 0 deletions instrumentation/http4s-blaze-client-2.12_0.22/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
apply plugin: 'scala'

dependencies {
implementation(project(":newrelic-api"))
implementation(project(":agent-bridge"))
implementation(project(":newrelic-weaver-api"))
implementation(project(":newrelic-weaver-scala-api"))
implementation("org.scala-lang:scala-library:2.12.14")
implementation('org.http4s:http4s-blaze-server_2.12:0.22.0')
implementation('org.http4s:http4s-blaze-client_2.12:0.22.0')
testImplementation(project(":instrumentation:cats-effect-2")) { transitive = false }
testImplementation(project(":instrumentation:newrelic-scala-cats-api")) { transitive = false }
testImplementation(project(":newrelic-scala-cats-api"))
testImplementation("org.http4s:http4s-dsl_2.12:0.22.0")

}

jar {
manifest {
attributes 'Implementation-Title': 'com.newrelic.instrumentation.http4s-blaze-client-2.12_0.22'
attributes 'Implementation-Vendor': 'New Relic'
attributes 'Implementation-Vendor-Id': 'com.newrelic'
attributes 'Implementation-Version': 1.0
}
}
verifyInstrumentation {
passes 'org.http4s:http4s-blaze-client_2.12:[0.22.0,1.0)'
excludeRegex '.*(RC|M)[0-9]*'
excludeRegex '.*0.22\\-[0-9].*'
}

sourceSets.main.scala.srcDirs = ['src/main/scala', 'src/main/java']
sourceSets.main.java.srcDirs = []
Loading

0 comments on commit f2e18ca

Please sign in to comment.