[rmi] Instrumentation for RMI 1.2 and later
includes instrumentation of client and server endpoints currently missing passing of execution context from client to server
This commit is contained in:
parent
fb8f7af342
commit
0ee80a0b95
|
@ -92,6 +92,7 @@ public class AgentInstaller {
|
|||
not(
|
||||
named("java.net.URL")
|
||||
.or(named("java.net.HttpURLConnection"))
|
||||
.or(nameStartsWith("java.rmi."))
|
||||
.or(nameStartsWith("java.util.concurrent."))
|
||||
.or(
|
||||
nameStartsWith("java.util.logging.")
|
||||
|
@ -111,6 +112,7 @@ public class AgentInstaller {
|
|||
.and(
|
||||
not(
|
||||
nameStartsWith("sun.net.www.protocol.")
|
||||
.or(nameStartsWith("sun.rmi.server."))
|
||||
.or(named("sun.net.www.http.HttpClient")))))
|
||||
.or(nameStartsWith("jdk."))
|
||||
.or(nameStartsWith("org.aspectj."))
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
muzzle {
|
||||
}
|
||||
|
||||
apply from: "${rootDir}/gradle/java.gradle"
|
||||
|
||||
task "rmic", dependsOn: testClasses {
|
||||
def clazz = 'rmi.app.ServerLegacy'
|
||||
String command = """rmic -g -keep -classpath ${sourceSets.test.output.classesDirs.asPath} -d ${buildDir}/classes/java/test ${clazz}"""
|
||||
command.execute().text
|
||||
}
|
||||
|
||||
test.dependsOn "rmic"
|
|
@ -0,0 +1,41 @@
|
|||
package datadog.trace.instrumentation.rmi;
|
||||
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.activateSpan;
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.activeSpan;
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.startSpan;
|
||||
|
||||
import datadog.trace.api.DDTags;
|
||||
import datadog.trace.instrumentation.api.AgentScope;
|
||||
import datadog.trace.instrumentation.api.AgentSpan;
|
||||
import java.lang.reflect.Method;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
|
||||
public class ClientAdvice {
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static AgentScope onEnter(@Advice.Argument(value = 1) final Method method) {
|
||||
if (activeSpan() == null) {
|
||||
return null;
|
||||
}
|
||||
final AgentSpan span =
|
||||
startSpan("rmi.invoke")
|
||||
.setTag(
|
||||
DDTags.RESOURCE_NAME,
|
||||
method.getDeclaringClass().getSimpleName() + "#" + method.getName())
|
||||
.setTag("span.origin.type", method.getDeclaringClass().getCanonicalName());
|
||||
return activateSpan(span, true);
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void stopSpan(
|
||||
@Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) {
|
||||
if (scope == null) {
|
||||
return;
|
||||
}
|
||||
final AgentSpan span = scope.span();
|
||||
if (throwable != null) {
|
||||
span.setError(true);
|
||||
span.addThrowable(throwable);
|
||||
}
|
||||
scope.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package datadog.trace.instrumentation.rmi;
|
||||
|
||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import datadog.trace.agent.tooling.Instrumenter;
|
||||
import java.util.Map;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public final class RmiClientInstrumentation extends Instrumenter.Default {
|
||||
|
||||
public RmiClientInstrumentation() {
|
||||
super("rmi", "rmi-client");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return not(isInterface()).and(safeHasSuperType(named("sun.rmi.server.UnicastRef")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
isMethod()
|
||||
.and(named("invoke"))
|
||||
.and(takesArgument(0, named("java.rmi.Remote")))
|
||||
.and(takesArgument(1, named("java.lang.reflect.Method"))),
|
||||
"datadog.trace.instrumentation.rmi.ClientAdvice");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package datadog.trace.instrumentation.rmi;
|
||||
|
||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import datadog.trace.agent.tooling.Instrumenter;
|
||||
import java.util.Map;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public final class RmiServerInstrumentation extends Instrumenter.Default {
|
||||
|
||||
public RmiServerInstrumentation() {
|
||||
super("rmi", "rmi-server");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return not(isInterface()).and(safeHasSuperType(named("java.rmi.server.RemoteServer")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
isMethod().and(isPublic()).and(not(isStatic())),
|
||||
"datadog.trace.instrumentation.rmi.ServerAdvice");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package datadog.trace.instrumentation.rmi;
|
||||
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.activateSpan;
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.startSpan;
|
||||
|
||||
import datadog.trace.api.DDTags;
|
||||
import datadog.trace.instrumentation.api.AgentScope;
|
||||
import datadog.trace.instrumentation.api.AgentSpan;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
|
||||
public class ServerAdvice {
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static AgentScope onEnter(
|
||||
@Advice.This final Object thiz, @Advice.Origin(value = "#m") final String method) {
|
||||
final AgentSpan span =
|
||||
startSpan("rmi.request")
|
||||
.setTag(DDTags.RESOURCE_NAME, thiz.getClass().getSimpleName() + "#" + method)
|
||||
.setTag("span.origin.type", thiz.getClass().getCanonicalName());
|
||||
return activateSpan(span, true);
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void stopSpan(
|
||||
@Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) {
|
||||
if (scope == null) {
|
||||
return;
|
||||
}
|
||||
final AgentSpan span = scope.span();
|
||||
if (throwable != null) {
|
||||
span.setError(true);
|
||||
span.addThrowable(throwable);
|
||||
}
|
||||
scope.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
import datadog.trace.agent.test.AgentTestRunner
|
||||
import datadog.trace.agent.test.utils.PortUtils
|
||||
import rmi.app.Greeter
|
||||
import rmi.app.Server
|
||||
import rmi.app.ServerLegacy
|
||||
|
||||
import java.rmi.registry.LocateRegistry
|
||||
import java.rmi.server.UnicastRemoteObject
|
||||
|
||||
import static datadog.trace.agent.test.asserts.ListWriterAssert.assertTraces
|
||||
import static datadog.trace.agent.test.utils.TraceUtils.basicSpan
|
||||
import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace
|
||||
|
||||
class RmiTest extends AgentTestRunner {
|
||||
def registryPort = PortUtils.randomOpenPort()
|
||||
def serverRegistry = LocateRegistry.createRegistry(registryPort)
|
||||
def clientRegistry = LocateRegistry.getRegistry("localhost", registryPort)
|
||||
|
||||
def cleanup() {
|
||||
UnicastRemoteObject.unexportObject(serverRegistry, true)
|
||||
}
|
||||
|
||||
def "Client call creates spans"() {
|
||||
setup:
|
||||
def server = new Server()
|
||||
serverRegistry.rebind(Server.RMI_ID, server)
|
||||
|
||||
when:
|
||||
def response = runUnderTrace("parent") {
|
||||
def client = (Greeter) clientRegistry.lookup(Server.RMI_ID)
|
||||
return client.hello("you")
|
||||
}
|
||||
|
||||
then:
|
||||
response.contains("Hello you")
|
||||
assertTraces(TEST_WRITER, 2) {
|
||||
trace(1, 2) {
|
||||
basicSpan(it, 0, "parent")
|
||||
span(1) {
|
||||
resourceName "Greeter#hello"
|
||||
operationName "rmi.invoke"
|
||||
childOf span(0)
|
||||
tags {
|
||||
"span.origin.type" Greeter.canonicalName
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
trace(0, 1) {
|
||||
span(0) {
|
||||
resourceName "Server#hello"
|
||||
operationName "rmi.request"
|
||||
tags {
|
||||
"span.origin.type" server.class.canonicalName
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
serverRegistry.unbind("Server")
|
||||
}
|
||||
|
||||
def "Calling server builtin methods doesn't create server spans"() {
|
||||
setup:
|
||||
def server = new Server()
|
||||
serverRegistry.rebind(Server.RMI_ID, server)
|
||||
|
||||
when:
|
||||
server.equals(new Server())
|
||||
server.getRef()
|
||||
server.hashCode()
|
||||
server.toString()
|
||||
server.getClass()
|
||||
|
||||
then:
|
||||
assertTraces(TEST_WRITER, 0) {}
|
||||
|
||||
cleanup:
|
||||
serverRegistry.unbind("Server")
|
||||
}
|
||||
|
||||
def "Service throws exception and its propagated to spans"() {
|
||||
setup:
|
||||
def server = new Server()
|
||||
serverRegistry.rebind(Server.RMI_ID, server)
|
||||
|
||||
when:
|
||||
runUnderTrace("parent") {
|
||||
def client = (Greeter) clientRegistry.lookup(Server.RMI_ID)
|
||||
client.exceptional()
|
||||
}
|
||||
|
||||
then:
|
||||
def thrownException = thrown(RuntimeException)
|
||||
assertTraces(TEST_WRITER, 2) {
|
||||
trace(1, 2) {
|
||||
basicSpan(it, 0, "parent", null, thrownException)
|
||||
span(1) {
|
||||
resourceName "Greeter#exceptional"
|
||||
operationName "rmi.invoke"
|
||||
childOf span(0)
|
||||
errored true
|
||||
|
||||
tags {
|
||||
"span.origin.type" Greeter.canonicalName
|
||||
errorTags(RuntimeException, String)
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
trace(0, 1) {
|
||||
span(0) {
|
||||
resourceName "Server#exceptional"
|
||||
operationName "rmi.request"
|
||||
errored true
|
||||
|
||||
tags {
|
||||
"span.origin.type" server.class.canonicalName
|
||||
errorTags(RuntimeException, String)
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
serverRegistry.unbind("Server")
|
||||
}
|
||||
|
||||
def "Client call using ServerLegacy_stub creates spans"() {
|
||||
setup:
|
||||
def server = new ServerLegacy()
|
||||
serverRegistry.rebind(ServerLegacy.RMI_ID, server)
|
||||
|
||||
|
||||
when:
|
||||
def response = runUnderTrace("parent") {
|
||||
def client = (Greeter) clientRegistry.lookup(ServerLegacy.RMI_ID)
|
||||
return client.hello("you")
|
||||
}
|
||||
|
||||
then:
|
||||
response.contains("Hello you")
|
||||
assertTraces(TEST_WRITER, 2) {
|
||||
trace(1, 2) {
|
||||
basicSpan(it, 0, "parent")
|
||||
span(1) {
|
||||
resourceName "Greeter#hello"
|
||||
operationName "rmi.invoke"
|
||||
childOf span(0)
|
||||
tags {
|
||||
"span.origin.type" Greeter.canonicalName
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
trace(0, 1) {
|
||||
span(0) {
|
||||
resourceName "ServerLegacy#hello"
|
||||
operationName "rmi.request"
|
||||
tags {
|
||||
"span.origin.type" server.class.canonicalName
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
serverRegistry.unbind(ServerLegacy.RMI_ID)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package rmi.app;
|
||||
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
|
||||
public interface Greeter extends Remote {
|
||||
String hello(String name) throws RemoteException;
|
||||
|
||||
void exceptional() throws RemoteException, RuntimeException;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package rmi.app;
|
||||
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.server.UnicastRemoteObject;
|
||||
|
||||
public class Server extends UnicastRemoteObject implements Greeter {
|
||||
static String RMI_ID = Server.class.getSimpleName();
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public Server() throws RemoteException {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String hello(final String name) {
|
||||
return "Hello " + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptional() throws RuntimeException {
|
||||
throw new RuntimeException("expected");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package rmi.app;
|
||||
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.server.UnicastRemoteObject;
|
||||
|
||||
public class ServerLegacy extends UnicastRemoteObject implements Greeter {
|
||||
static String RMI_ID = ServerLegacy.class.getSimpleName();
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ServerLegacy() throws RemoteException {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String hello(final String name) {
|
||||
return "Hello " + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptional() throws RuntimeException {
|
||||
throw new RuntimeException("expected");
|
||||
}
|
||||
}
|
|
@ -110,8 +110,9 @@ include ':dd-java-agent:instrumentation:play-ws-2'
|
|||
include ':dd-java-agent:instrumentation:play-ws-2.1'
|
||||
include ':dd-java-agent:instrumentation:rabbitmq-amqp-2.7'
|
||||
include ':dd-java-agent:instrumentation:ratpack-1.4'
|
||||
include ':dd-java-agent:instrumentation:rxjava-1'
|
||||
include ':dd-java-agent:instrumentation:reactor-core-3.1'
|
||||
include ':dd-java-agent:instrumentation:rmi'
|
||||
include ':dd-java-agent:instrumentation:rxjava-1'
|
||||
include ':dd-java-agent:instrumentation:servlet'
|
||||
include ':dd-java-agent:instrumentation:servlet:request-2'
|
||||
include ':dd-java-agent:instrumentation:servlet:request-3'
|
||||
|
|
Loading…
Reference in New Issue