parent
7405b7711b
commit
b1d06299cc
|
@ -0,0 +1,46 @@
|
|||
apply plugin: 'version-scan'
|
||||
|
||||
// TODO figure out the earliest version that can be supported, and the latest
|
||||
versionScan {
|
||||
group = "org.apache.tomcat.embed"
|
||||
module = 'tomcat-embed-jasper'
|
||||
versions = "[8.0.41,)"
|
||||
verifyPresent = [
|
||||
"org.apache.jasper.servlet.JspServlet": null,
|
||||
]
|
||||
}
|
||||
|
||||
apply from: "${rootDir}/gradle/java.gradle"
|
||||
|
||||
apply plugin: 'org.unbroken-dome.test-sets'
|
||||
|
||||
testSets {
|
||||
latestDepTest {
|
||||
dirName = 'test'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '8.0.41'
|
||||
|
||||
compile project(':dd-trace-ot')
|
||||
compile project(':dd-java-agent:agent-tooling')
|
||||
|
||||
compile deps.bytebuddy
|
||||
compile deps.opentracing
|
||||
annotationProcessor deps.autoservice
|
||||
implementation deps.autoservice
|
||||
testCompile project(':dd-java-agent:testing')
|
||||
|
||||
testCompile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '8.0.41'
|
||||
testCompile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '8.0.41'
|
||||
|
||||
testCompile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.10.0'
|
||||
}
|
||||
|
||||
configurations.latestDepTestCompile {
|
||||
resolutionStrategy {
|
||||
force group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '+'
|
||||
force group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '+'
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package datadog.trace.instrumentation.jsp;
|
||||
|
||||
import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
|
||||
import static io.opentracing.log.Fields.ERROR_OBJECT;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.*;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import datadog.trace.agent.tooling.DDAdvice;
|
||||
import datadog.trace.agent.tooling.DDTransformers;
|
||||
import datadog.trace.agent.tooling.Instrumenter;
|
||||
import datadog.trace.api.DDSpanTypes;
|
||||
import datadog.trace.api.DDTags;
|
||||
import io.opentracing.Scope;
|
||||
import io.opentracing.Span;
|
||||
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;
|
||||
import org.apache.jasper.JspCompilationContext;
|
||||
import org.apache.jasper.servlet.JspServletWrapper;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public final class JasperJSPInstrumentation extends Instrumenter.Configurable {
|
||||
|
||||
public JasperJSPInstrumentation() {
|
||||
super("jsp", "tomcat-jsp");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgentBuilder apply(final AgentBuilder agentBuilder) {
|
||||
return agentBuilder
|
||||
.type(
|
||||
named("org.apache.jasper.servlet.JspServletWrapper"),
|
||||
classLoaderHasClasses("org.apache.jasper.servlet.JspServlet"))
|
||||
.transform(DDTransformers.defaultTransformers())
|
||||
.transform(
|
||||
DDAdvice.create()
|
||||
.advice(
|
||||
named("service")
|
||||
.and(takesArgument(0, named("javax.servlet.http.HttpServletRequest")))
|
||||
.and(takesArgument(1, named("javax.servlet.http.HttpServletResponse")))
|
||||
.and(isPublic()),
|
||||
JasperJSPAdvice.class.getName()))
|
||||
.asDecorator();
|
||||
}
|
||||
|
||||
public static class JasperJSPAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static Scope startSpan(
|
||||
@Advice.Argument(0) final HttpServletRequest req,
|
||||
@Advice.Argument(2) final boolean preCompile) {
|
||||
final Scope scope =
|
||||
GlobalTracer.get()
|
||||
.buildSpan("jsp.service")
|
||||
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)
|
||||
.withTag(DDTags.SPAN_TYPE, DDSpanTypes.WEB_SERVLET)
|
||||
.withTag("servlet.context", req.getContextPath())
|
||||
.startActive(true);
|
||||
|
||||
final Span span = scope.span();
|
||||
span.setTag("jsp.precompile", preCompile);
|
||||
|
||||
Tags.COMPONENT.set(span, "tomcat-jsp-servlet");
|
||||
Tags.HTTP_METHOD.set(span, req.getMethod());
|
||||
Tags.HTTP_URL.set(span, req.getRequestURL().toString());
|
||||
return scope;
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void stopSpan(
|
||||
@Advice.Argument(1) final HttpServletResponse resp,
|
||||
@Advice.This JspServletWrapper jspServletWrapper,
|
||||
@Advice.Enter final Scope scope,
|
||||
@Advice.Thrown final Throwable throwable) {
|
||||
|
||||
final Span span = scope.span();
|
||||
JspCompilationContext jspCompilationContext = jspServletWrapper.getJspEngineContext();
|
||||
if (jspCompilationContext != null) {
|
||||
span.setTag("jsp.compiler", jspCompilationContext.getCompiler().getClass().getName());
|
||||
span.setTag("jsp.outputDir", jspCompilationContext.getOutputDir());
|
||||
span.setTag("jsp.classFQCN", jspCompilationContext.getFQCN());
|
||||
if (throwable != null) {
|
||||
span.setTag("jsp.classpath", jspCompilationContext.getClassPath());
|
||||
}
|
||||
}
|
||||
|
||||
if (throwable != null) {
|
||||
if (resp.getStatus() == HttpServletResponse.SC_OK) {
|
||||
// exception is thrown in filter chain, but status code is incorrect
|
||||
Tags.HTTP_STATUS.set(span, 500);
|
||||
}
|
||||
Tags.ERROR.set(span, Boolean.TRUE);
|
||||
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
|
||||
} else {
|
||||
Tags.HTTP_STATUS.set(span, resp.getStatus());
|
||||
}
|
||||
scope.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
import com.google.common.io.Files
|
||||
import datadog.trace.agent.test.AgentTestRunner
|
||||
import datadog.trace.agent.test.TestUtils
|
||||
import datadog.trace.api.DDSpanTypes
|
||||
import io.netty.handler.codec.http.HttpResponseStatus
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.apache.catalina.Context
|
||||
import org.apache.catalina.startup.Tomcat
|
||||
import org.apache.tomcat.JarScanFilter
|
||||
import org.apache.tomcat.JarScanType
|
||||
import spock.lang.Shared
|
||||
|
||||
import static datadog.trace.agent.test.ListWriterAssert.assertTraces
|
||||
|
||||
class JSPInstrumentationTest extends AgentTestRunner {
|
||||
|
||||
static final int PORT = TestUtils.randomOpenPort()
|
||||
OkHttpClient client = new OkHttpClient.Builder()
|
||||
// Uncomment when debugging:
|
||||
// .connectTimeout(1, TimeUnit.HOURS)
|
||||
// .writeTimeout(1, TimeUnit.HOURS)
|
||||
// .readTimeout(1, TimeUnit.HOURS)
|
||||
.build()
|
||||
|
||||
static Tomcat tomcatServer
|
||||
static Context appContext
|
||||
static final String JSP_WEBAPP_CONTEXT = "/jsptest-context"
|
||||
|
||||
@Shared
|
||||
static File baseDir
|
||||
static String expectedJspClassFilesDir = "/work/Tomcat/localhost$JSP_WEBAPP_CONTEXT/org/apache/jsp/"
|
||||
|
||||
def setupSpec() {
|
||||
tomcatServer = new Tomcat()
|
||||
tomcatServer.setPort(PORT)
|
||||
|
||||
baseDir = Files.createTempDir()
|
||||
baseDir.deleteOnExit()
|
||||
expectedJspClassFilesDir = baseDir.getCanonicalFile().getAbsolutePath() + expectedJspClassFilesDir
|
||||
tomcatServer.setBaseDir(baseDir.getAbsolutePath())
|
||||
|
||||
appContext = tomcatServer.addWebapp(JSP_WEBAPP_CONTEXT,
|
||||
JSPInstrumentationTest.getResource("/webapps/jsptest").getPath())
|
||||
// Speed up startup by disabling jar scanning:
|
||||
appContext.getJarScanner().setJarScanFilter(new JarScanFilter() {
|
||||
@Override
|
||||
boolean check(JarScanType jarScanType, String jarName) {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
tomcatServer.start()
|
||||
System.out.println(
|
||||
"Tomcat server: http://" + tomcatServer.getHost().getName() + ":" + PORT + "/")
|
||||
}
|
||||
|
||||
def cleanupSpec() {
|
||||
tomcatServer.stop()
|
||||
tomcatServer.destroy()
|
||||
}
|
||||
|
||||
def "basic looping jsp"() {
|
||||
setup:
|
||||
String url = "http://localhost:$PORT$JSP_WEBAPP_CONTEXT/loop.jsp"
|
||||
def req = new Request.Builder().url(new URL(url)).get().build()
|
||||
|
||||
when:
|
||||
Response res = client.newCall(req).execute()
|
||||
|
||||
then:
|
||||
assertTraces(TEST_WRITER, 1) {
|
||||
trace(0, 1) {
|
||||
span(0) {
|
||||
serviceName "jsptest-context"
|
||||
operationName "jsp.service"
|
||||
resourceName "GET /jsptest-context/loop.jsp"
|
||||
spanType DDSpanTypes.WEB_SERVLET
|
||||
errored false
|
||||
tags {
|
||||
"http.url" url
|
||||
"http.method" "GET"
|
||||
"span.kind" "server"
|
||||
"component" "tomcat-jsp-servlet"
|
||||
"span.type" DDSpanTypes.WEB_SERVLET
|
||||
"servlet.context" JSP_WEBAPP_CONTEXT
|
||||
"http.status_code" 200
|
||||
"jsp.classFQCN" "org.apache.jsp.loop_jsp"
|
||||
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
|
||||
"jsp.outputDir" expectedJspClassFilesDir
|
||||
"jsp.precompile" false
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
res.code() == HttpResponseStatus.OK.code()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<html>
|
||||
<head><title>BASIC JSP</title></head>
|
||||
<body>
|
||||
<%
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
%>
|
||||
<h2>number:<%= i %></h2><p></p>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
</body>
|
||||
</html>
|
|
@ -32,6 +32,7 @@ include ':dd-java-agent:instrumentation:jedis-1.4'
|
|||
include ':dd-java-agent:instrumentation:jetty-8'
|
||||
include ':dd-java-agent:instrumentation:jms-1'
|
||||
include ':dd-java-agent:instrumentation:jms-2'
|
||||
include ':dd-java-agent:instrumentation:jsp'
|
||||
include ':dd-java-agent:instrumentation:kafka-clients-0.11'
|
||||
include ':dd-java-agent:instrumentation:kafka-streams-0.11'
|
||||
include ':dd-java-agent:instrumentation:lettuce-5'
|
||||
|
|
Loading…
Reference in New Issue