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

Support micronaut 4.x #7035

Merged
merged 1 commit into from
May 17, 2024
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
@@ -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
Loading