Skip to content

Commit

Permalink
Support micronaut 4.x (#7035)
Browse files Browse the repository at this point in the history
  • Loading branch information
amarziali authored May 17, 2024
1 parent f5e26a8 commit 96faef0
Show file tree
Hide file tree
Showing 16 changed files with 890 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
ext {
minJavaVersionForTests = JavaVersion.VERSION_17
}
muzzle {
pass {
group = "io.micronaut"
module = "micronaut-http-server-netty"
versions = "[4,)"
assertInverse = true
}
}

apply from: "$rootDir/gradle/java.gradle"

addTestSuiteForDir('latestDepTest', 'test')

[compileMain_java17Java, compileTestJava, compileLatestDepTestJava].each {
it.configure {
setJavaVersion(it, 17)
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}

[compileTestGroovy, compileLatestDepTestGroovy].each {
it.configure {
javaLauncher = getJavaLauncherFor(17)
}
}

dependencies {
main_java17CompileOnly group: 'io.micronaut', name: 'micronaut-http-server-netty', version: '4.0.0'

// Added to ensure cross compatibility:
testImplementation project(':dd-java-agent:instrumentation:micronaut:http-server-netty-2.0')
testImplementation project(':dd-java-agent:instrumentation:micronaut:http-server-netty-3.0')
testImplementation project(':dd-java-agent:instrumentation:netty-4.1')
testImplementation group: 'io.micronaut', name: 'micronaut-http-server-netty', version: '4.0.0', {
exclude group: 'org.slf4j', module: 'slf4j-api'
exclude group: 'ch.qos.logback', module: 'logback-classic'
}
testImplementation group: 'io.projectreactor', name: 'reactor-core', version: '3.5.7'
testImplementation group: 'io.micronaut', name: 'micronaut-jackson-databind', version: '4.0.0', {
exclude group: 'org.slf4j', module: 'slf4j-api'
exclude group: 'ch.qos.logback', module: 'logback-classic'
}
testAnnotationProcessor "io.micronaut:micronaut-inject-java:4.0.0"

latestDepTestImplementation group: 'io.micronaut', name: 'micronaut-http-server-netty', version: '4.+', {
exclude group: 'org.slf4j', module: 'slf4j-api'
exclude group: 'ch.qos.logback', module: 'logback-classic'
}
latestDepTestImplementation group: 'io.micronaut', name: 'micronaut-jackson-databind', version: '4.+', {
exclude group: 'org.slf4j', module: 'slf4j-api'
exclude group: 'ch.qos.logback', module: 'logback-classic'
}
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package datadog.trace.instrumentation.micronaut.v4_0;

import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.agent.tooling.InstrumenterModule;

@AutoService(InstrumenterModule.class)
public final class MicronautInstrumentation extends InstrumenterModule.Tracing
implements Instrumenter.ForKnownTypes {

public MicronautInstrumentation() {
super("micronaut", "micronaut-http-server-netty", "micronaut-http-server-netty-4");
}

@Override
public String[] knownMatchingTypes() {
return new String[] {
"io.micronaut.http.server.netty.RoutingInBoundHandler",
"io.micronaut.http.server.RouteExecutor",
"io.micronaut.http.server.netty.NettyRequestLifecycle",
};
}

@Override
public void methodAdvice(MethodTransformer transformer) {
transformer.applyAdvice(
isMethod().and(named("handleNormal")).and(takesNoArguments()),
packageName + ".ChannelAcceptAdvice");
transformer.applyAdvice(
isMethod()
.and(named("handleNormal"))
.and(takesArgument(0, named("io.micronaut.http.server.netty.NettyHttpRequest"))),
packageName + ".ChannelAcceptAdvice2");

transformer.applyAdvice(
isMethod()
.and(named("findRouteMatch"))
.and(takesArgument(0, named("io.micronaut.http.HttpRequest")))
.and(returns(named("io.micronaut.web.router.UriRouteMatch"))),
packageName + ".HandleRouteMatchAdvice");

transformer.applyAdvice(
isMethod()
.and(named("createDefaultErrorResponse"))
.and(takesArgument(0, named("io.micronaut.http.HttpRequest")))
.and(takesArgument(1, named("java.lang.Throwable"))),
packageName + ".CreateDefaultErrorResponseAdvice");

transformer.applyAdvice(
isMethod()
.and(named("encodeHttpResponse"))
.and(takesArgument(1, named("io.micronaut.http.server.netty.NettyHttpRequest")))
.and(
takesArgument(
2,
namedOneOf(
"io.micronaut.http.MutableHttpResponse",
"io.micronaut.http.HttpResponse"))),
packageName + ".EncodeHttpResponseAdvice");
}

@Override
public String[] helperClassNames() {
return new String[] {
packageName + ".MicronautDecorator",
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package datadog.trace.instrumentation.micronaut.v4_0;

import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.instrumentation.micronaut.v4_0.MicronautDecorator.DECORATE;
import static datadog.trace.instrumentation.micronaut.v4_0.MicronautDecorator.PARENT_SPAN_ATTRIBUTE;
import static datadog.trace.instrumentation.micronaut.v4_0.MicronautDecorator.SPAN_ATTRIBUTE;

import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import io.micronaut.http.server.netty.NettyHttpRequest;
import net.bytebuddy.asm.Advice;

public class ChannelAcceptAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AgentScope beginRequest(
@Advice.FieldValue("nettyRequest") final NettyHttpRequest request) {
final AgentSpan nettySpan = activeSpan();

if (request == null || nettySpan == null) {
return null;
}
final AgentSpan span = startSpan(DECORATE.spanName()).setMeasured(true);
DECORATE.afterStart(span);
request.setAttribute(SPAN_ATTRIBUTE, span);
request.setAttribute(PARENT_SPAN_ATTRIBUTE, nettySpan);

return activateSpan(span);
}

@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void endRequest(@Advice.Enter final AgentScope scope) {
if (scope != null) {
scope.close();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package datadog.trace.instrumentation.micronaut.v4_0;

import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.instrumentation.micronaut.v4_0.MicronautDecorator.DECORATE;
import static datadog.trace.instrumentation.micronaut.v4_0.MicronautDecorator.PARENT_SPAN_ATTRIBUTE;
import static datadog.trace.instrumentation.micronaut.v4_0.MicronautDecorator.SPAN_ATTRIBUTE;

import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import io.micronaut.http.server.netty.NettyHttpRequest;
import net.bytebuddy.asm.Advice;

public class ChannelAcceptAdvice2 {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AgentScope beginRequest(@Advice.Argument(0) final NettyHttpRequest request) {
final AgentSpan nettySpan = activeSpan();

if (request == null || nettySpan == null) {
return null;
}
final AgentSpan span = startSpan(DECORATE.spanName()).setMeasured(true);
DECORATE.afterStart(span);
request.setAttribute(SPAN_ATTRIBUTE, span);
request.setAttribute(PARENT_SPAN_ATTRIBUTE, nettySpan);

return activateSpan(span);
}

@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void endRequest(@Advice.Enter final AgentScope scope) {
if (scope != null) {
scope.close();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package datadog.trace.instrumentation.micronaut.v4_0;

import static datadog.trace.instrumentation.micronaut.v4_0.MicronautDecorator.DECORATE;
import static datadog.trace.instrumentation.micronaut.v4_0.MicronautDecorator.SPAN_ATTRIBUTE;

import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import io.micronaut.http.HttpRequest;
import net.bytebuddy.asm.Advice;

public class CreateDefaultErrorResponseAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void captureError(
@Advice.Argument(0) final HttpRequest httpRequest,
@Advice.Argument(1) final Throwable cause) {
AgentSpan span = httpRequest.getAttribute(SPAN_ATTRIBUTE, AgentSpan.class).orElse(null);
if (null == span) {
return;
}
DECORATE.onError(span, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package datadog.trace.instrumentation.micronaut.v4_0;

import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.instrumentation.micronaut.v4_0.MicronautDecorator.DECORATE;
import static datadog.trace.instrumentation.micronaut.v4_0.MicronautDecorator.SPAN_ATTRIBUTE;

import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.server.netty.NettyHttpRequest;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.implementation.bytecode.assign.Assigner;

public class EncodeHttpResponseAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void finishHandlerSpan(
@Advice.Argument(1) final NettyHttpRequest<?> request,
@Advice.Argument(value = 2, typing = Assigner.Typing.DYNAMIC) final HttpResponse<?> message) {
AgentSpan span = request.removeAttribute(SPAN_ATTRIBUTE, AgentSpan.class).orElse(null);
if (null == span) {
return;
}

try (final AgentScope scope = activateSpan(span)) {
DECORATE.onResponse(span, message);
DECORATE.beforeFinish(span);
span.finish();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package datadog.trace.instrumentation.micronaut.v4_0;

import static datadog.trace.instrumentation.micronaut.v4_0.MicronautDecorator.DECORATE;
import static datadog.trace.instrumentation.micronaut.v4_0.MicronautDecorator.PARENT_SPAN_ATTRIBUTE;
import static datadog.trace.instrumentation.micronaut.v4_0.MicronautDecorator.SPAN_ATTRIBUTE;

import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import io.micronaut.http.HttpRequest;
import io.micronaut.web.router.UriRouteMatch;
import net.bytebuddy.asm.Advice;

public class HandleRouteMatchAdvice {
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void captureRoute(
@Advice.Argument(0) final HttpRequest<?> request,
@Advice.Return final UriRouteMatch routeMatch) {
if (routeMatch == null) {
return;
}
AgentSpan span = request.getAttribute(SPAN_ATTRIBUTE, AgentSpan.class).orElse(null);
if (span == null) {
return;
}
final AgentSpan nettySpan =
request.getAttribute(PARENT_SPAN_ATTRIBUTE, AgentSpan.class).orElse(null);
if (nettySpan != null) {
DECORATE.onMicronautSpan(span, nettySpan, request, routeMatch);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package datadog.trace.instrumentation.micronaut.v4_0;

import static datadog.trace.bootstrap.instrumentation.decorator.http.HttpResourceDecorator.HTTP_RESOURCE_DECORATOR;

import datadog.trace.bootstrap.instrumentation.api.AgentPropagation;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes;
import datadog.trace.bootstrap.instrumentation.api.URIDataAdapter;
import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
import datadog.trace.bootstrap.instrumentation.decorator.HttpServerDecorator;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.web.router.UriRouteMatch;

public class MicronautDecorator
extends HttpServerDecorator<HttpRequest, HttpRequest, HttpResponse, Void> {
private static final CharSequence MICRONAUT_CONTROLLER =
UTF8BytesString.create("micronaut-controller");
public static final String SPAN_ATTRIBUTE = "datadog.trace.instrumentation.micronaut-netty.Span";
public static final String PARENT_SPAN_ATTRIBUTE =
"datadog.trace.instrumentation.micronaut-netty.ParentSpan";

public static MicronautDecorator DECORATE = new MicronautDecorator();

@Override
protected String[] instrumentationNames() {
return new String[] {"micronaut"};
}

@Override
protected AgentPropagation.ContextVisitor<Void> getter() {
return null;
}

@Override
protected AgentPropagation.ContextVisitor<HttpResponse> responseGetter() {
return null;
}

@Override
public CharSequence spanName() {
return MICRONAUT_CONTROLLER;
}

@Override
protected String method(HttpRequest httpRequest) {
return httpRequest.getMethod().name();
}

@Override
protected URIDataAdapter url(HttpRequest httpRequest) {
return null;
}

@Override
protected String peerHostIP(HttpRequest httpRequest) {
return null;
}

@Override
protected int peerPort(HttpRequest httpRequest) {
return 0;
}

@Override
protected int status(HttpResponse mutableHttpResponse) {
return mutableHttpResponse.getStatus().getCode();
}

@Override
protected CharSequence spanType() {
return InternalSpanTypes.HTTP_SERVER;
}

@Override
protected CharSequence component() {
return MICRONAUT_CONTROLLER;
}

public void onMicronautSpan(
final AgentSpan span,
final AgentSpan parent,
final HttpRequest<?> request,
final UriRouteMatch uriRouteMatch) {
String route = null;
if (uriRouteMatch.getRouteInfo() != null
&& uriRouteMatch.getRouteInfo().getUriMatchTemplate() != null) {
route = uriRouteMatch.getRouteInfo().getUriMatchTemplate().toPathString();
}
if (null != route) {
HTTP_RESOURCE_DECORATOR.withRoute(parent, request.getMethod().name(), route);
}
span.setResourceName(DECORATE.spanNameForMethod(uriRouteMatch.getTargetMethod()));
}
}
Loading

0 comments on commit 96faef0

Please sign in to comment.