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

Http4s blaze #363

Merged
merged 5 commits into from
Aug 20, 2021
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
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