Migrate servlet instrumentation to byte buddy.
This commit is contained in:
parent
e21ed1052f
commit
e5924b3fe9
|
@ -21,7 +21,8 @@ dependencies {
|
|||
compile project(':dd-java-agent:tooling')
|
||||
compile project(':dd-trace-annotations')
|
||||
|
||||
|
||||
compile project(':dd-java-agent:integrations:servlet-2')
|
||||
compile project(':dd-java-agent:integrations:servlet-3')
|
||||
compile project(':dd-java-agent:integrations:spring-web')
|
||||
|
||||
compile deps.bytebuddy
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
package com.datadoghq.agent.integration;
|
||||
|
||||
import io.opentracing.ActiveSpan;
|
||||
import io.opentracing.NoopTracerFactory;
|
||||
import io.opentracing.SpanContext;
|
||||
import io.opentracing.Tracer;
|
||||
import io.opentracing.contrib.web.servlet.filter.HttpServletRequestExtractAdapter;
|
||||
import io.opentracing.contrib.web.servlet.filter.ServletFilterSpanDecorator;
|
||||
import io.opentracing.propagation.Format;
|
||||
import io.opentracing.tag.Tags;
|
||||
import java.util.Collections;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jboss.byteman.rule.Rule;
|
||||
|
||||
/** Please be very careful not to introduce any Servlet 3 dependencies into this class. */
|
||||
@Slf4j
|
||||
public class Servlet2Helper extends OpenTracingHelper {
|
||||
|
||||
/**
|
||||
* Used as a key of {@link HttpServletRequest#setAttribute(String, Object)} to inject server span
|
||||
* context
|
||||
*/
|
||||
public static final String SERVER_SPAN_CONTEXT =
|
||||
Servlet2Helper.class.getName() + ".activeSpanContext";
|
||||
|
||||
public static final String SERVLET_OPERATION_NAME = "servlet.request";
|
||||
|
||||
protected final Tracer tracer;
|
||||
|
||||
public Servlet2Helper(final Rule rule) {
|
||||
super(rule);
|
||||
Tracer tracerResolved;
|
||||
try {
|
||||
tracerResolved = getTracer();
|
||||
tracerResolved = tracerResolved == null ? NoopTracerFactory.create() : tracerResolved;
|
||||
} catch (final Exception e) {
|
||||
tracerResolved = NoopTracerFactory.create();
|
||||
log.warn("Failed to retrieve the tracer, using a NoopTracer instead: {}", e.getMessage());
|
||||
log.warn(e.getMessage(), e);
|
||||
}
|
||||
tracer = tracerResolved;
|
||||
}
|
||||
|
||||
public void onRequest(final HttpServletRequest req, final HttpServletResponse resp) {
|
||||
if (req.getAttribute(SERVER_SPAN_CONTEXT) != null) {
|
||||
// Perhaps we're already tracing?
|
||||
return;
|
||||
}
|
||||
|
||||
final SpanContext extractedContext =
|
||||
tracer.extract(Format.Builtin.HTTP_HEADERS, new HttpServletRequestExtractAdapter(req));
|
||||
|
||||
final ActiveSpan span =
|
||||
tracer
|
||||
.buildSpan(SERVLET_OPERATION_NAME)
|
||||
.asChildOf(extractedContext)
|
||||
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)
|
||||
.startActive();
|
||||
|
||||
req.setAttribute(SERVER_SPAN_CONTEXT, span.context());
|
||||
|
||||
ServletFilterSpanDecorator.STANDARD_TAGS.onRequest(req, span);
|
||||
}
|
||||
|
||||
public void onError(
|
||||
final HttpServletRequest req, final HttpServletResponse resp, final Throwable ex) {
|
||||
if (req.getAttribute(SERVER_SPAN_CONTEXT) == null) {
|
||||
// Doesn't look like an active span was started at the beginning
|
||||
return;
|
||||
}
|
||||
|
||||
final ActiveSpan span = tracer.activeSpan();
|
||||
if (span != null) {
|
||||
ServletFilterSpanDecorator.STANDARD_TAGS.onError(req, resp, ex, span);
|
||||
span.log(Collections.singletonMap("error.object", ex));
|
||||
span.deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
public void onResponse(final HttpServletRequest req, final HttpServletResponse resp) {
|
||||
if (req.getAttribute(SERVER_SPAN_CONTEXT) == null) {
|
||||
// Doesn't look like an active span was started at the beginning
|
||||
return;
|
||||
}
|
||||
|
||||
final ActiveSpan span = tracer.activeSpan();
|
||||
if (span != null) {
|
||||
ServletFilterSpanDecorator.STANDARD_TAGS.onResponse(req, resp, span);
|
||||
span.deactivate();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
package com.datadoghq.agent.integration;
|
||||
|
||||
import io.opentracing.ActiveSpan;
|
||||
import io.opentracing.contrib.web.servlet.filter.ServletFilterSpanDecorator;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.servlet.AsyncEvent;
|
||||
import javax.servlet.AsyncListener;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jboss.byteman.rule.Rule;
|
||||
|
||||
@Slf4j
|
||||
public class Servlet3Helper extends Servlet2Helper {
|
||||
|
||||
public Servlet3Helper(final Rule rule) {
|
||||
super(rule);
|
||||
}
|
||||
|
||||
/**
|
||||
* The distinction with this method compared with Servlet2Helper.onResponse is the addition of the
|
||||
* async support.
|
||||
*
|
||||
* @param req
|
||||
* @param resp
|
||||
*/
|
||||
@Override
|
||||
public void onResponse(final HttpServletRequest req, final HttpServletResponse resp) {
|
||||
if (req.getAttribute(SERVER_SPAN_CONTEXT) == null) {
|
||||
// Doesn't look like an active span was started at the beginning
|
||||
return;
|
||||
}
|
||||
|
||||
final ActiveSpan span = tracer.activeSpan();
|
||||
if (span != null) {
|
||||
if (req.isAsyncStarted()) {
|
||||
addAsyncListeners(req, resp, span);
|
||||
} else {
|
||||
ServletFilterSpanDecorator.STANDARD_TAGS.onResponse(req, resp, span);
|
||||
}
|
||||
span.deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
private void addAsyncListeners(
|
||||
final HttpServletRequest req, final HttpServletResponse resp, final ActiveSpan span) {
|
||||
|
||||
final ActiveSpan.Continuation cont = span.capture();
|
||||
final AtomicBoolean activated = new AtomicBoolean(false);
|
||||
// what if async is already finished? This would not be called
|
||||
req.getAsyncContext()
|
||||
.addListener(
|
||||
new AsyncListener() {
|
||||
@Override
|
||||
public void onComplete(final AsyncEvent event) throws IOException {
|
||||
if (activated.compareAndSet(false, true)) {
|
||||
try (ActiveSpan activeSpan = cont.activate()) {
|
||||
ServletFilterSpanDecorator.STANDARD_TAGS.onResponse(
|
||||
(HttpServletRequest) event.getSuppliedRequest(),
|
||||
(HttpServletResponse) event.getSuppliedResponse(),
|
||||
span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout(final AsyncEvent event) throws IOException {
|
||||
if (activated.compareAndSet(false, true)) {
|
||||
try (ActiveSpan activeSpan = cont.activate()) {
|
||||
ServletFilterSpanDecorator.STANDARD_TAGS.onTimeout(
|
||||
(HttpServletRequest) event.getSuppliedRequest(),
|
||||
(HttpServletResponse) event.getSuppliedResponse(),
|
||||
event.getAsyncContext().getTimeout(),
|
||||
span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final AsyncEvent event) throws IOException {
|
||||
if (activated.compareAndSet(false, true)) {
|
||||
try (ActiveSpan activeSpan = cont.activate()) {
|
||||
ServletFilterSpanDecorator.STANDARD_TAGS.onError(
|
||||
(HttpServletRequest) event.getSuppliedRequest(),
|
||||
(HttpServletResponse) event.getSuppliedResponse(),
|
||||
event.getThrowable(),
|
||||
span);
|
||||
span.log(Collections.singletonMap("error.object", event.getThrowable()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartAsync(final AsyncEvent event) throws IOException {}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -9,3 +9,18 @@ versionScan {
|
|||
"javax.servlet.FilterChain" : null,
|
||||
]
|
||||
}
|
||||
|
||||
apply from: "${rootDir}/gradle/java.gradle"
|
||||
|
||||
dependencies {
|
||||
compileOnly group: 'javax.servlet', name: 'servlet-api', version: '2.3'
|
||||
|
||||
compile project(':dd-trace')
|
||||
compile project(':dd-java-agent:tooling')
|
||||
|
||||
compile deps.bytebuddy
|
||||
compile deps.opentracing
|
||||
|
||||
compile group: 'io.opentracing.contrib', name: 'opentracing-web-servlet-filter', version: '0.0.9'
|
||||
compile group: 'com.google.auto.service', name: 'auto-service', version: '1.0-rc3'
|
||||
}
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
package dd.inst.servlet2;
|
||||
|
||||
import static dd.trace.ClassLoaderHasClassMatcher.classLoaderHasClasses;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isProtected;
|
||||
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 dd.trace.Instrumenter;
|
||||
import io.opentracing.ActiveSpan;
|
||||
import io.opentracing.SpanContext;
|
||||
import io.opentracing.contrib.web.servlet.filter.HttpServletRequestExtractAdapter;
|
||||
import io.opentracing.contrib.web.servlet.filter.ServletFilterSpanDecorator;
|
||||
import io.opentracing.propagation.Format;
|
||||
import io.opentracing.tag.Tags;
|
||||
import io.opentracing.util.GlobalTracer;
|
||||
import java.util.Collections;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import net.bytebuddy.agent.builder.AgentBuilder;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public final class HttpServlet2Instrumentation implements Instrumenter {
|
||||
public static final String SERVLET_OPERATION_NAME = "servlet.request";
|
||||
|
||||
@Override
|
||||
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
|
||||
return agentBuilder
|
||||
.type(
|
||||
named("javax.servlet.http.HttpServlet"),
|
||||
not(classLoaderHasClasses("javax.servlet.AsyncEvent", "javax.servlet.AsyncListener"))
|
||||
.and(
|
||||
classLoaderHasClasses(
|
||||
"javax.servlet.ServletContextEvent", "javax.servlet.FilterChain")))
|
||||
.transform(
|
||||
new AgentBuilder.Transformer.ForAdvice()
|
||||
.advice(
|
||||
named("service")
|
||||
.and(takesArgument(0, named("javax.servlet.http.HttpServletRequest")))
|
||||
.and(takesArgument(1, named("javax.servlet.http.HttpServletResponse")))
|
||||
.and(isProtected()),
|
||||
HttpServlet2Advice.class.getName()))
|
||||
.asDecorator();
|
||||
}
|
||||
|
||||
public static class HttpServlet2Advice {
|
||||
|
||||
@Advice.OnMethodEnter
|
||||
public static ActiveSpan startSpan(@Advice.Argument(0) final HttpServletRequest req) {
|
||||
|
||||
final SpanContext extractedContext =
|
||||
GlobalTracer.get()
|
||||
.extract(Format.Builtin.HTTP_HEADERS, new HttpServletRequestExtractAdapter(req));
|
||||
|
||||
final ActiveSpan span =
|
||||
GlobalTracer.get()
|
||||
.buildSpan(SERVLET_OPERATION_NAME)
|
||||
.asChildOf(extractedContext)
|
||||
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)
|
||||
.startActive();
|
||||
|
||||
ServletFilterSpanDecorator.STANDARD_TAGS.onRequest(req, span);
|
||||
return span;
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class)
|
||||
public static void stopSpan(
|
||||
@Advice.Argument(0) final HttpServletRequest req,
|
||||
@Advice.Argument(1) final HttpServletResponse resp,
|
||||
@Advice.Enter final ActiveSpan span,
|
||||
@Advice.Thrown final Throwable throwable) {
|
||||
|
||||
if (span != null) {
|
||||
if (throwable != null) {
|
||||
ServletFilterSpanDecorator.STANDARD_TAGS.onError(req, resp, throwable, span);
|
||||
span.log(Collections.singletonMap("error.object", throwable));
|
||||
} else {
|
||||
ServletFilterSpanDecorator.STANDARD_TAGS.onResponse(req, resp, span);
|
||||
}
|
||||
span.deactivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,3 +10,18 @@ versionScan {
|
|||
"javax.servlet.AsyncListener": null,
|
||||
]
|
||||
}
|
||||
|
||||
apply from: "${rootDir}/gradle/java.gradle"
|
||||
|
||||
dependencies {
|
||||
compileOnly group: 'javax.servlet', name: 'javax.servlet-api', version: '3.0.1'
|
||||
|
||||
compile project(':dd-trace')
|
||||
compile project(':dd-java-agent:tooling')
|
||||
|
||||
compile deps.bytebuddy
|
||||
compile deps.opentracing
|
||||
|
||||
compile group: 'io.opentracing.contrib', name: 'opentracing-web-servlet-filter', version: '0.0.9'
|
||||
compile group: 'com.google.auto.service', name: 'auto-service', version: '1.0-rc3'
|
||||
}
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
package dd.inst.servlet3;
|
||||
|
||||
import static dd.trace.ClassLoaderHasClassMatcher.classLoaderHasClasses;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isProtected;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import dd.trace.Instrumenter;
|
||||
import io.opentracing.ActiveSpan;
|
||||
import io.opentracing.SpanContext;
|
||||
import io.opentracing.contrib.web.servlet.filter.HttpServletRequestExtractAdapter;
|
||||
import io.opentracing.contrib.web.servlet.filter.ServletFilterSpanDecorator;
|
||||
import io.opentracing.propagation.Format;
|
||||
import io.opentracing.tag.Tags;
|
||||
import io.opentracing.util.GlobalTracer;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.servlet.AsyncEvent;
|
||||
import javax.servlet.AsyncListener;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import net.bytebuddy.agent.builder.AgentBuilder;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public final class HttpServlet3Instrumentation implements Instrumenter {
|
||||
public static final String SERVLET_OPERATION_NAME = "servlet.request";
|
||||
|
||||
@Override
|
||||
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
|
||||
return agentBuilder
|
||||
.type(
|
||||
named("javax.servlet.http.HttpServlet"),
|
||||
classLoaderHasClasses("javax.servlet.AsyncEvent", "javax.servlet.AsyncListener"))
|
||||
.transform(
|
||||
new AgentBuilder.Transformer.ForAdvice()
|
||||
.advice(
|
||||
named("service")
|
||||
.and(takesArgument(0, named("javax.servlet.http.HttpServletRequest")))
|
||||
.and(takesArgument(1, named("javax.servlet.http.HttpServletResponse")))
|
||||
.and(isProtected()),
|
||||
HttpServlet3Advice.class.getName()))
|
||||
.asDecorator();
|
||||
}
|
||||
|
||||
public static class HttpServlet3Advice {
|
||||
|
||||
@Advice.OnMethodEnter
|
||||
public static ActiveSpan startSpan(@Advice.Argument(0) final HttpServletRequest req) {
|
||||
|
||||
final SpanContext extractedContext =
|
||||
GlobalTracer.get()
|
||||
.extract(Format.Builtin.HTTP_HEADERS, new HttpServletRequestExtractAdapter(req));
|
||||
|
||||
final ActiveSpan span =
|
||||
GlobalTracer.get()
|
||||
.buildSpan(SERVLET_OPERATION_NAME)
|
||||
.asChildOf(extractedContext)
|
||||
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)
|
||||
.startActive();
|
||||
|
||||
ServletFilterSpanDecorator.STANDARD_TAGS.onRequest(req, span);
|
||||
return span;
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class)
|
||||
public static void stopSpan(
|
||||
@Advice.Argument(0) final HttpServletRequest req,
|
||||
@Advice.Argument(1) final HttpServletResponse resp,
|
||||
@Advice.Enter final ActiveSpan span,
|
||||
@Advice.Thrown final Throwable throwable) {
|
||||
|
||||
if (span != null) {
|
||||
if (throwable != null) {
|
||||
ServletFilterSpanDecorator.STANDARD_TAGS.onError(req, resp, throwable, span);
|
||||
span.log(Collections.singletonMap("error.object", throwable));
|
||||
} else if (req.isAsyncStarted()) {
|
||||
final ActiveSpan.Continuation cont = span.capture();
|
||||
final AtomicBoolean activated = new AtomicBoolean(false);
|
||||
// what if async is already finished? This would not be called
|
||||
req.getAsyncContext().addListener(new TagSettingAsyncListener(activated, cont, span));
|
||||
} else {
|
||||
ServletFilterSpanDecorator.STANDARD_TAGS.onResponse(req, resp, span);
|
||||
}
|
||||
span.deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
public static class TagSettingAsyncListener implements AsyncListener {
|
||||
private final AtomicBoolean activated;
|
||||
private final ActiveSpan.Continuation cont;
|
||||
private final ActiveSpan span;
|
||||
|
||||
public TagSettingAsyncListener(
|
||||
final AtomicBoolean activated,
|
||||
final ActiveSpan.Continuation cont,
|
||||
final ActiveSpan span) {
|
||||
this.activated = activated;
|
||||
this.cont = cont;
|
||||
this.span = span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(final AsyncEvent event) throws IOException {
|
||||
if (activated.compareAndSet(false, true)) {
|
||||
try (ActiveSpan activeSpan = cont.activate()) {
|
||||
ServletFilterSpanDecorator.STANDARD_TAGS.onResponse(
|
||||
(HttpServletRequest) event.getSuppliedRequest(),
|
||||
(HttpServletResponse) event.getSuppliedResponse(),
|
||||
span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout(final AsyncEvent event) throws IOException {
|
||||
if (activated.compareAndSet(false, true)) {
|
||||
try (ActiveSpan activeSpan = cont.activate()) {
|
||||
ServletFilterSpanDecorator.STANDARD_TAGS.onTimeout(
|
||||
(HttpServletRequest) event.getSuppliedRequest(),
|
||||
(HttpServletResponse) event.getSuppliedResponse(),
|
||||
event.getAsyncContext().getTimeout(),
|
||||
span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final AsyncEvent event) throws IOException {
|
||||
if (event.getThrowable() != null && activated.compareAndSet(false, true)) {
|
||||
try (ActiveSpan activeSpan = cont.activate()) {
|
||||
ServletFilterSpanDecorator.STANDARD_TAGS.onError(
|
||||
(HttpServletRequest) event.getSuppliedRequest(),
|
||||
(HttpServletResponse) event.getSuppliedResponse(),
|
||||
event.getThrowable(),
|
||||
span);
|
||||
span.log(Collections.singletonMap("error.object", event.getThrowable()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartAsync(final AsyncEvent event) throws IOException {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package dd.inst.springweb;
|
||||
|
||||
import static dd.trace.ClassLoaderHasClassWithFieldMatcher.classLoaderHasClassWithField;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||
|
@ -31,7 +32,10 @@ public final class SpringWebInstrumentation implements Instrumenter {
|
|||
return agentBuilder
|
||||
.type(
|
||||
not(isInterface())
|
||||
.and(hasSuperType(named("org.springframework.web.servlet.HandlerAdapter"))))
|
||||
.and(hasSuperType(named("org.springframework.web.servlet.HandlerAdapter"))),
|
||||
classLoaderHasClassWithField(
|
||||
"org.springframework.web.servlet.HandlerMapping",
|
||||
"BEST_MATCHING_PATTERN_ATTRIBUTE"))
|
||||
.transform(
|
||||
new AgentBuilder.Transformer.ForAdvice()
|
||||
.advice(
|
||||
|
|
|
@ -80,15 +80,3 @@ IF TRUE
|
|||
DO
|
||||
com.datadoghq.agent.InstrumentationRulesManager.registerClassLoad($0);
|
||||
ENDRULE
|
||||
|
||||
|
||||
# Instrument Servlet
|
||||
# ===========================
|
||||
RULE HttpServlet-init
|
||||
CLASS ^javax.servlet.http.HttpServlet
|
||||
METHOD <init>
|
||||
AT EXIT
|
||||
IF TRUE
|
||||
DO
|
||||
com.datadoghq.agent.InstrumentationRulesManager.registerClassLoad($0);
|
||||
ENDRULE
|
||||
|
|
|
@ -106,73 +106,3 @@ IF TRUE
|
|||
DO
|
||||
patch($0)
|
||||
ENDRULE
|
||||
|
||||
# Instrument Servlet 2
|
||||
# ===========================
|
||||
RULE HttpServlet-2.service-entry
|
||||
CLASS ^javax.servlet.http.HttpServlet
|
||||
METHOD service(HttpServletRequest, HttpServletResponse)
|
||||
HELPER com.datadoghq.agent.integration.Servlet2Helper
|
||||
COMPILE
|
||||
AT ENTRY
|
||||
IF TRUE
|
||||
DO
|
||||
onRequest($1, $2)
|
||||
ENDRULE
|
||||
|
||||
RULE HttpServlet-2.service-exit
|
||||
CLASS ^javax.servlet.http.HttpServlet
|
||||
METHOD service(HttpServletRequest, HttpServletResponse)
|
||||
HELPER com.datadoghq.agent.integration.Servlet2Helper
|
||||
COMPILE
|
||||
AT EXIT
|
||||
IF TRUE
|
||||
DO
|
||||
onResponse($1, $2)
|
||||
ENDRULE
|
||||
|
||||
RULE HttpServlet-2.service-error
|
||||
CLASS ^javax.servlet.http.HttpServlet
|
||||
METHOD service(HttpServletRequest, HttpServletResponse)
|
||||
HELPER com.datadoghq.agent.integration.Servlet2Helper
|
||||
COMPILE
|
||||
AT EXCEPTION EXIT
|
||||
IF TRUE
|
||||
DO
|
||||
onError($1, $2, $^)
|
||||
ENDRULE
|
||||
|
||||
# Instrument Servlet 3
|
||||
# ===========================
|
||||
RULE HttpServlet-3.service-entry
|
||||
CLASS ^javax.servlet.http.HttpServlet
|
||||
METHOD service(HttpServletRequest, HttpServletResponse)
|
||||
HELPER com.datadoghq.agent.integration.Servlet3Helper
|
||||
COMPILE
|
||||
AT ENTRY
|
||||
IF TRUE
|
||||
DO
|
||||
onRequest($1, $2)
|
||||
ENDRULE
|
||||
|
||||
RULE HttpServlet-3.service-exit
|
||||
CLASS ^javax.servlet.http.HttpServlet
|
||||
METHOD service(HttpServletRequest, HttpServletResponse)
|
||||
HELPER com.datadoghq.agent.integration.Servlet3Helper
|
||||
COMPILE
|
||||
AT EXIT
|
||||
IF TRUE
|
||||
DO
|
||||
onResponse($1, $2)
|
||||
ENDRULE
|
||||
|
||||
RULE HttpServlet-3.service-error
|
||||
CLASS ^javax.servlet.http.HttpServlet
|
||||
METHOD service(HttpServletRequest, HttpServletResponse)
|
||||
HELPER com.datadoghq.agent.integration.Servlet3Helper
|
||||
COMPILE
|
||||
AT EXCEPTION EXIT
|
||||
IF TRUE
|
||||
DO
|
||||
onError($1, $2, $^)
|
||||
ENDRULE
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package dd.trace;
|
||||
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
public class ClassLoaderHasClassMatcher extends ElementMatcher.Junction.AbstractBase<ClassLoader> {
|
||||
|
||||
private final String[] names;
|
||||
|
||||
private ClassLoaderHasClassMatcher(final String... names) {
|
||||
this.names = names;
|
||||
}
|
||||
|
||||
public static ElementMatcher.Junction.AbstractBase<ClassLoader> classLoaderHasClasses(
|
||||
final String... names) {
|
||||
return new ClassLoaderHasClassMatcher(names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(final ClassLoader target) {
|
||||
try {
|
||||
if (target != null) {
|
||||
for (final String name : names) {
|
||||
Class.forName(name, false, target);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (final ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package dd.trace;
|
||||
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
public class ClassLoaderHasClassWithFieldMatcher
|
||||
extends ElementMatcher.Junction.AbstractBase<ClassLoader> {
|
||||
|
||||
private final String className;
|
||||
private final String fieldName;
|
||||
|
||||
private ClassLoaderHasClassWithFieldMatcher(final String className, final String fieldName) {
|
||||
this.className = className;
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
public static AbstractBase<ClassLoader> classLoaderHasClassWithField(
|
||||
final String className, final String fieldName) {
|
||||
return new ClassLoaderHasClassWithFieldMatcher(className, fieldName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(final ClassLoader target) {
|
||||
try {
|
||||
if (target != null) {
|
||||
final Class<?> aClass = Class.forName(className, false, target);
|
||||
aClass.getDeclaredField(fieldName);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (final ClassNotFoundException e) {
|
||||
return false;
|
||||
} catch (final NoSuchFieldException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue