Skip to content

Commit

Permalink
feat: update tracing / telemetry
Browse files Browse the repository at this point in the history
  • Loading branch information
patsta32 committed Nov 6, 2024
1 parent 462b383 commit bf5c656
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 54 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import sbt.Keys.cleanFiles
ThisBuild / scalaVersion := Dependencies.scalaVersion
scalaVersion := Dependencies.scalaVersion

val releaseVersion = sys.env.getOrElse("TAG", "2.0.0-alpha.rc.4")
val releaseVersion = sys.env.getOrElse("TAG", "2.1.0-rc-1")
addCommandAlias("packageSmithy4Play", "smithy4play/package")
addCommandAlias("publishSmithy4Play", "smithy4play/publish;smithy4playInstrumentation/publish")
addCommandAlias("publishLocalWithInstrumentation", "publishLocalSmithy4PlayInstrumentation;publishLocalSmithy4Play")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,31 @@
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

public class TestInstrumentation implements TypeInstrumentation {

public class MiddlewareInstrumentation implements TypeInstrumentation {


@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed("de.innfactory.smithy4play.routing.TestClass");
return hasClassesNamed("de.innfactory.smithy4play.routing.middleware.Smithy4PlayMiddleware");
}

@Override
public ElementMatcher<TypeDescription> typeMatcher() {
System.out.println("- - - TestInstrumentation typeMatcher - - -");
return named("de.innfactory.smithy4play.routing.TestClass");
System.out.println("- - - MiddlewareInstrumentation typeMatcher - - -");
return extendsClass(
named("de.innfactory.smithy4play.routing.middleware.Smithy4PlayMiddleware")
).or(
named("de.innfactory.smithy4play.routing.middleware.Smithy4PlayMiddleware")
);
}

@Override
public void transform(TypeTransformer transformer) {
System.out.println("- - - TestInstrumentation transform - - -");

transformer.applyAdviceToMethod(
named("test")
.and(takesArgument(0, named("java.lang.String")))
.and(returns(named("java.lang.String"))),
named("skipMiddleware")
.and(takesArgument(0, named("de.innfactory.smithy4play.routing.context.RoutingContext")))
.and(returns(named("java.lang.Boolean"))),
this.getClass().getName() + "$ApplyAdvice");
}

Expand All @@ -49,48 +52,38 @@ public static class ApplyAdvice {

@Advice.OnMethodEnter()
public static void onEnter(
@Advice.Argument(0) String test,
@Advice.Argument(0) Boolean b,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {

//System.out.println("TestInstrumentation ApplyAdvice onEnter " + test);
// span.addEvent("ADVICE smithy4play " + test);

Context parentContext = currentContext();
//System.out.println("TestInstrumentation ApplyAdvice shouldStart " + shouldStart);
Span mySpan = GlobalOpenTelemetry.get().getTracer("smithy4play").spanBuilder("test span").startSpan();
//System.out.println("TestInstrumentation ApplyAdvice mySpan " + mySpan.getSpanContext().getSpanId());
Span mySpan = GlobalOpenTelemetry.get().getTracer("smithy4play").spanBuilder("smithy4play.Middleware").startSpan();
context = mySpan.storeInContext(parentContext);
Scope myScope = mySpan.makeCurrent();
//System.out.println("TestInstrumentation ApplyAdvice should start");
scope = myScope;
}

@Advice.OnMethodExit(onThrowable = Throwable.class)
public static void stopTraceOnResponse(
@Advice.This Object testClass,
@Advice.This Object currentClass,
@Advice.Thrown Throwable throwable,
@Advice.Argument(0) String test,
@Advice.Return(readOnly = false) String testout,
@Advice.Argument(0) Boolean b,
@Advice.Return(readOnly = false) Boolean returnValue,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
System.out.println("TestInstrumentation ApplyAdvice onExit");
Span mySpan = currentSpan();
if (scope != null) {
scope.close();
}
if(mySpan != null) {
mySpan.addEvent(testout);
mySpan.setAttribute("test", testout);
mySpan.updateName("TEST / " + testout);
if (mySpan != null) {
mySpan.addEvent("applyMiddleware: " + returnValue);
mySpan.setAttribute("class", currentClass.getClass().getName());
mySpan.updateName("smithy4play.Middleware " + currentClass.getClass().getName());
mySpan.end();
}
if (throwable != null) {
throwable.printStackTrace();
}
if (context != null) {
// System.out.println("TestInstrumentation ApplyAdvice onExit update update Span name");
// Span.fromContext(context).updateName(testout);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@ class Smithy4PlayInstrumentationModule extends InstrumentationModule("smithy4pla
override def isIndyModule: Boolean = false

override def isHelperClass(className: String): Boolean =
className.startsWith("io.opentelemetry.javaagent") || className.startsWith("de.innfactory.smithy4play.instrumentation")
className.startsWith("io.opentelemetry.javaagent") || className.startsWith("de.innfactory.smithy4play")

override def getAdditionalHelperClassNames: util.List[String] = List(
Smithy4PlaySingleton.getClass.getName,
classOf[TestInstrumentation].getName,
classOf[SmithyPlayRouterInstrumentation].getName,
classOf[MiddlewareInstrumentation].getName,
).asJava

override def typeInstrumentations(): util.List[TypeInstrumentation] =
asList(
new TestInstrumentation()
new MiddlewareInstrumentation(),
new SmithyPlayRouterInstrumentation(),
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package de.innfactory.smithy4play.instrumentation;

import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentSpan;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.*;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

public class SmithyPlayRouterInstrumentation implements TypeInstrumentation {


@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed("de.innfactory.smithy4play.routing.internal.Smithy4PlayRouterHandler");
}

@Override
public ElementMatcher<TypeDescription> typeMatcher() {
// System.out.println("- - - SmithyPlayRouterInstrumentation typeMatcher - - -");
return named("de.innfactory.smithy4play.routing.internal.Smithy4PlayRouterHandler");
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("handleForInstrument"),
this.getClass().getName() + "$ApplyAdvice");
//System.out.println("- - - SmithyPlayRouterInstrumentation transform - - -");
}

@SuppressWarnings("unused")
public static class ApplyAdvice {

@Advice.OnMethodEnter()
public static void onEnter(
@Advice.Argument(0) java.lang.String path,
@Advice.Argument(1) java.lang.String method,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
//System.out.println("ApplyAdvice Start applyHandler");
Context parentContext = currentContext();
Span mySpan = GlobalOpenTelemetry.get().getTracer("smithy4play").spanBuilder("smithy4play.Smithy4PlayRouter").startSpan();
context = mySpan.storeInContext(parentContext);
Scope myScope = mySpan.makeCurrent();
scope = myScope;
}

@Advice.OnMethodExit(onThrowable = Throwable.class)
public static void stopTraceOnResponse(
@Advice.This Object currentClass,
@Advice.Thrown Throwable throwable,
@Advice.Argument(0) java.lang.String path,
@Advice.Argument(1) java.lang.String method,
@Advice.Return(readOnly = false) java.lang.String returnValue,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
//System.out.println("ApplyAdvice End applyHandler");
Span mySpan = currentSpan();
if (scope != null) {
scope.close();
}
if (mySpan != null) {
String routeName = path;
String methodName = method;
mySpan.setAttribute("class", currentClass.getClass().getName());
mySpan.updateName(methodName + " " + routeName);
mySpan.setAttribute("http.request.method", methodName);
mySpan.setAttribute("http.request.url", routeName);
mySpan.end();
}
if (throwable != null) {
throwable.printStackTrace();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
package de.innfactory.smithy4play.routing.internal

import cats.data.{ EitherT, Kleisli }
import de.innfactory.smithy4play.{ logger, ContextRoute, RoutingResult }
import cats.data.{EitherT, Kleisli}
import de.innfactory.smithy4play.{ContextRoute, RoutingResult, logger}
import de.innfactory.smithy4play.codecs.Codec
import de.innfactory.smithy4play.routing.context.RoutingContextBase
import de.innfactory.smithy4play.routing.middleware.Middleware
import de.innfactory.smithy4play.routing.internal.{
deconstructPath,
getSmithy4sHttpMethod,
toSmithy4sHttpRequest,
toSmithy4sHttpUri
}
import de.innfactory.smithy4play.routing.internal.{deconstructPath, getSmithy4sHttpMethod, toSmithy4sHttpRequest, toSmithy4sHttpUri}
import de.innfactory.smithy4play.telemetry.Telemetry
import io.opentelemetry.api.trace.Span
import io.opentelemetry.context.Scope
Expand All @@ -21,7 +16,8 @@ import smithy4s.http.*
import smithy4s.interopcats.monadThrowShim
import smithy4s.kinds.FunctorAlgebra

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


class Smithy4PlayRouter[Alg[_[_, _, _, _, _]]](
impl: FunctorAlgebra[Alg, ContextRoute],
Expand All @@ -32,7 +28,7 @@ class Smithy4PlayRouter[Alg[_[_, _, _, _, _]]](

private val baseResponse = HttpResponse(200, Map.empty, Blob.empty)
private val errorHeaders = List(smithy4s.http.errorTypeHeader)

private val baseServerCodec: HttpUnaryServerCodecs.Builder[ContextRoute, RequestWrapped, Result] =
HttpUnaryServerCodecs
.builder[ContextRoute]
Expand Down Expand Up @@ -75,19 +71,12 @@ class Smithy4PlayRouter[Alg[_[_, _, _, _, _]]](
),
addDecodedPathParams = (r, v) => r.copy(r.req, v)
)

private val routerHandler = new Smithy4PlayRouterHandler(router)

private val handler = new PartialFunction[RequestHeader, Request[RawBuffer] => RoutingResult[Result]] {

override def isDefinedAt(x: RequestHeader): Boolean = {
val isdefined = router.isDefinedAt(x)
if (!isdefined) logger.debug(s"[${this.getClass.getName}] router is not defined at ${isdefined} ${x}")
isdefined
}

override def apply(v1: RequestHeader): Request[RawBuffer] => RoutingResult[Result] = { request =>
val ctx: RoutingContextBase = RoutingContextBase.fromRequest(request, service.hints, v1)
router.apply(v1)(RequestWrapped(request, Map.empty)).run(ctx)
}
override def isDefinedAt(x: RequestHeader): Boolean = routerHandler.isDefinedAtHandler(x)
override def apply(v1: RequestHeader): Request[RawBuffer] => RoutingResult[Result] = routerHandler.applyHandler(v1, service.hints)
}

def routes(): PartialFunction[RequestHeader, Request[RawBuffer] => RoutingResult[Result]] = handler
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package de.innfactory.smithy4play.routing.internal

import cats.data.{EitherT, Kleisli}
import de.innfactory.smithy4play.{ContextRoute, RoutingResult, logger}
import de.innfactory.smithy4play.codecs.Codec
import de.innfactory.smithy4play.routing.context.RoutingContextBase
import de.innfactory.smithy4play.routing.middleware.Middleware
import de.innfactory.smithy4play.routing.internal.{deconstructPath, getSmithy4sHttpMethod, toSmithy4sHttpRequest, toSmithy4sHttpUri}
import de.innfactory.smithy4play.telemetry.Telemetry
import play.api.mvc.*
import play.api.routing.Router.Routes
import smithy4s.*

class Smithy4PlayRouterHandler(router: PartialFunction[RequestHeader, RequestWrapped => ContextRoute[Result]]) {

def applyHandler(v1: RequestHeader, serviceHints: Hints): Request[RawBuffer] => RoutingResult[Result] = { request =>
val ctx: RoutingContextBase = RoutingContextBase.fromRequest(request,serviceHints, v1)
handleForInstrument(v1.path, v1.method)
router.apply(v1)(RequestWrapped(request, Map.empty)).run(ctx)
}

def handleForInstrument(path: String, method: String): String = {
logger.debug(s"[${this.getClass.getName}] handle route for ${method} ${path}")
""
}

def isDefinedAtHandler(v1: RequestHeader): Boolean = {
val isdefined = router.isDefinedAt(v1)
if (!isdefined) logger.debug(s"[${this.getClass.getName}] router is not defined at ${isdefined} ${v1}")
isdefined
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ abstract class Smithy4PlayMiddleware {

def skipMiddleware(r: RoutingContext): Boolean = false

def middlewareSkippingTelemetry(b: Boolean): Boolean = b

def logic(
r: RoutingContext,
next: RoutingContext => RoutingResult[Result]
Expand All @@ -25,9 +27,11 @@ abstract class Smithy4PlayMiddleware {
f: RoutingContext => RoutingResult[Result]
)(implicit ec: ExecutionContext): RoutingResult[Result] =
if (skipMiddleware(r)) {
middlewareSkippingTelemetry(true)
logger.debug(s"[${className}] skipping middleware")
f(r)
} else {
middlewareSkippingTelemetry(false)
logger.debug(s"[${className}] applying middleware")
logic(r, f)
}
Expand Down

0 comments on commit bf5c656

Please sign in to comment.