Merge pull request #354 from DataDog/gary/jsp-integration-branch

Generic JSP instrumentation with Tomcat Jasper compilation instrumentation.
This commit is contained in:
Gary Huang 2018-06-26 18:35:09 -04:00 committed by GitHub
commit cc7bb7943f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1809 additions and 4 deletions

View File

@ -0,0 +1,56 @@
apply plugin: 'version-scan'
versionScan {
group = "org.apache.tomcat"
module = "tomcat-jasper"
versions = "[7.0.0,)"
scanDependencies = true
verifyPresent = [
"org.apache.jasper.servlet.JspServletWrapper": null,
]
}
apply from: "${rootDir}/gradle/java.gradle"
apply plugin: 'org.unbroken-dome.test-sets'
testSets {
latestDepTest {
dirName = 'test'
}
}
dependencies {
// compiling against tomcat 7.0.20 because there seems to be some issues with Tomcat's dependency < 7.0.20
compileOnly group: 'org.apache.tomcat', name: 'tomcat-jasper', version: '7.0.20'
compileOnly group: 'javax.servlet.jsp', name: 'javax.servlet.jsp-api', version: '2.3.0'
compileOnly group: 'javax.servlet', name: 'javax.servlet-api', version: '3.1.0'
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 project(':dd-java-agent:instrumentation:servlet-3')
// using tomcat 7.0.37 because there seems to be some issues with Tomcat's jar scanning in versions < 7.0.37
// https://stackoverflow.com/questions/23484098/org-apache-tomcat-util-bcel-classfile-classformatexception-invalid-byte-tag-in
testCompile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '7.0.37'
testCompile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-logging-juli', version: '7.0.37'
testCompile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '7.0.37'
testCompile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.10.0'
}
configurations.latestDepTestCompile {
resolutionStrategy {
force group: 'javax.servlet.jsp', name: 'javax.servlet.jsp-api', version: '+'
force group: 'javax.servlet', name: 'javax.servlet-api', version: '+'
force group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '+'
force group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '+'
force group: 'org.apache.tomcat.embed', name: 'tomcat-embed-logging-juli', version: '+'
}
}

View File

@ -0,0 +1,99 @@
package datadog.trace.instrumentation.jsp;
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.RequestDispatcher;
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 JSPInstrumentation extends Instrumenter.Configurable {
public JSPInstrumentation() {
super("jsp", "jsp-render");
}
@Override
protected boolean defaultEnabled() {
return false;
}
@Override
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(not(isInterface()).and(hasSuperType(named("javax.servlet.jsp.HttpJspPage"))))
.transform(DDTransformers.defaultTransformers())
.transform(
DDAdvice.create()
.advice(
named("_jspService")
.and(takesArgument(0, named("javax.servlet.http.HttpServletRequest")))
.and(takesArgument(1, named("javax.servlet.http.HttpServletResponse")))
.and(isPublic()),
HttpJspPageAdvice.class.getName()))
.asDecorator();
}
public static class HttpJspPageAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static Scope startSpan(@Advice.Argument(0) final HttpServletRequest req) {
final Scope scope =
GlobalTracer.get()
.buildSpan("jsp.render")
.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();
// get the JSP file name being rendered in an include action
Object includeServletPath = req.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH);
String resourceName = req.getServletPath();
if (includeServletPath != null && includeServletPath instanceof String) {
resourceName = includeServletPath.toString();
}
span.setTag(DDTags.RESOURCE_NAME, resourceName);
Object forwardOrigin = req.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH);
if (forwardOrigin != null && forwardOrigin instanceof String) {
span.setTag("jsp.forwardOrigin", forwardOrigin.toString());
}
// add the request URL as a tag to provide better context when looking at spans produced by
// actions
span.setTag("jsp.requestURL", req.getRequestURL().toString());
Tags.COMPONENT.set(span, "jsp-http-servlet");
return scope;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Argument(1) final HttpServletResponse resp,
@Advice.Enter final Scope scope,
@Advice.Thrown final Throwable throwable) {
final Span span = scope.span();
if (throwable != null) {
Tags.ERROR.set(span, Boolean.TRUE);
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
}
scope.close();
}
}
}

View File

@ -0,0 +1,95 @@
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 net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import org.apache.jasper.JspCompilationContext;
@AutoService(Instrumenter.class)
public final class JasperJSPCompilationContextInstrumentation extends Instrumenter.Configurable {
public JasperJSPCompilationContextInstrumentation() {
super("jsp", "jsp-compile");
}
@Override
protected boolean defaultEnabled() {
return false;
}
@Override
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
named("org.apache.jasper.JspCompilationContext"),
classLoaderHasClasses("org.apache.jasper.servlet.JspServletWrapper"))
.transform(DDTransformers.defaultTransformers())
.transform(
DDAdvice.create()
.advice(
named("compile").and(takesArguments(0)).and(isPublic()),
JasperJspCompilationContext.class.getName()))
.asDecorator();
}
public static class JasperJspCompilationContext {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static Scope startSpan(@Advice.This JspCompilationContext jspCompilationContext) {
final Scope scope =
GlobalTracer.get()
.buildSpan("jsp.compile")
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)
.withTag(DDTags.SPAN_TYPE, DDSpanTypes.WEB_SERVLET)
.startActive(true);
final Span span = scope.span();
if (jspCompilationContext.getServletContext() != null) {
span.setTag("servlet.context", jspCompilationContext.getServletContext().getContextPath());
}
span.setTag(DDTags.RESOURCE_NAME, jspCompilationContext.getJspFile());
Tags.COMPONENT.set(span, "jsp-http-servlet");
return scope;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.This final JspCompilationContext jspCompilationContext,
@Advice.Enter final Scope scope,
@Advice.Thrown final Throwable throwable) {
final Span span = scope.span();
if (jspCompilationContext != null) {
if (jspCompilationContext.getCompiler() != null) {
span.setTag("jsp.compiler", jspCompilationContext.getCompiler().getClass().getName());
}
span.setTag("jsp.classFQCN", jspCompilationContext.getFQCN());
if (throwable != null) {
span.setTag("jsp.javaFile", jspCompilationContext.getServletJavaFileName());
span.setTag("jsp.classpath", jspCompilationContext.getClassPath());
}
}
if (throwable != null) {
Tags.ERROR.set(span, Boolean.TRUE);
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));
}
scope.close();
}
}
}

View File

@ -0,0 +1,640 @@
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.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.Response
import org.apache.catalina.Context
import org.apache.catalina.startup.Tomcat
import org.apache.jasper.JasperException
import spock.lang.Unroll
import static datadog.trace.agent.test.ListWriterAssert.assertTraces
class JSPInstrumentationBasicTests extends AgentTestRunner {
static {
System.setProperty("dd.integration.jsp.enabled", "true")
// skip jar scanning using environment variables:
// http://tomcat.apache.org/tomcat-7.0-doc/config/systemprops.html#JAR_Scanning
// having this set allows us to test with old versions of the tomcat api since
// JarScanFilter did not exist in the tomcat 7 api
System.setProperty("org.apache.catalina.startup.ContextConfig.jarsToSkip", "*")
System.setProperty("org.apache.catalina.startup.TldConfig.jarsToSkip", "*")
}
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"
static File baseDir
static String baseUrl
static String expectedJspClassFilesDir = "/work/Tomcat/localhost/$JSP_WEBAPP_CONTEXT/org/apache/jsp/"
def setupSpec() {
tomcatServer = new Tomcat()
tomcatServer.setPort(PORT)
// comment to debug
tomcatServer.setSilent(true)
baseDir = Files.createTempDir()
baseDir.deleteOnExit()
expectedJspClassFilesDir = baseDir.getCanonicalFile().getAbsolutePath() + expectedJspClassFilesDir
baseUrl = "http://localhost:$PORT/$JSP_WEBAPP_CONTEXT"
tomcatServer.setBaseDir(baseDir.getAbsolutePath())
appContext = tomcatServer.addWebapp("/$JSP_WEBAPP_CONTEXT",
JSPInstrumentationBasicTests.getResource("/webapps/jsptest").getPath())
tomcatServer.start()
System.out.println(
"Tomcat server: http://" + tomcatServer.getHost().getName() + ":" + PORT + "/")
}
def cleanupSpec() {
tomcatServer.stop()
tomcatServer.destroy()
}
@Unroll
def "non-erroneous GET #test test"() {
setup:
String reqUrl = baseUrl + "/$jspFileName"
def req = new Request.Builder().url(new URL(reqUrl)).get().build()
when:
Response res = client.newCall(req).execute()
then:
assertTraces(TEST_WRITER, 1) {
trace(0, 3) {
span(0) {
parent()
serviceName JSP_WEBAPP_CONTEXT
operationName "servlet.request"
resourceName "GET /$JSP_WEBAPP_CONTEXT/$jspFileName"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"http.url" "http://localhost:$PORT/$JSP_WEBAPP_CONTEXT/$jspFileName"
"http.method" "GET"
"span.kind" "server"
"component" "java-web-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"http.status_code" 200
defaultTags()
}
}
span(1) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/$jspFileName"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.requestURL" reqUrl
defaultTags()
}
}
span(2) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/$jspFileName"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.$jspClassNamePrefix$jspClassName"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
}
}
res.code() == HttpResponseStatus.OK.code()
cleanup:
res.close()
where:
test | jspFileName | jspClassName | jspClassNamePrefix
"no java jsp" | "nojava.jsp" | "nojava_jsp" | ""
"basic loop jsp"|"common/loop.jsp" | "loop_jsp" | "common."
"invalid HTML markup"|"invalidMarkup.jsp" | "invalidMarkup_jsp" | ""
}
def "non-erroneous GET with query string"() {
setup:
String queryString = "HELLO"
String reqUrl = baseUrl + "/getQuery.jsp"
Request req = new Request.Builder().url(new URL(reqUrl + "?" + queryString)).get().build()
when:
Response res = client.newCall(req).execute()
then:
assertTraces(TEST_WRITER, 1) {
trace(0, 3) {
span(0) {
parent()
serviceName JSP_WEBAPP_CONTEXT
operationName "servlet.request"
resourceName "GET /$JSP_WEBAPP_CONTEXT/getQuery.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"http.url" "http://localhost:$PORT/$JSP_WEBAPP_CONTEXT/getQuery.jsp"
"http.method" "GET"
"span.kind" "server"
"component" "java-web-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"http.status_code" 200
defaultTags()
}
}
span(1) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/getQuery.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.requestURL" reqUrl
defaultTags()
}
}
span(2) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/getQuery.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.getQuery_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
}
}
res.code() == HttpResponseStatus.OK.code()
cleanup:
res.close()
}
def "non-erroneous POST"() {
setup:
String reqUrl = baseUrl + "/post.jsp"
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("name", "world")
.build()
Request req = new Request.Builder().url(new URL(reqUrl)).post(requestBody).build()
when:
Response res = client.newCall(req).execute()
then:
assertTraces(TEST_WRITER, 1) {
trace(0, 3) {
span(0) {
parent()
serviceName JSP_WEBAPP_CONTEXT
operationName "servlet.request"
resourceName "POST /$JSP_WEBAPP_CONTEXT/post.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"http.url" "http://localhost:$PORT/$JSP_WEBAPP_CONTEXT/post.jsp"
"http.method" "POST"
"span.kind" "server"
"component" "java-web-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"http.status_code" 200
defaultTags()
}
}
span(1) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/post.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.requestURL" reqUrl
defaultTags()
}
}
span(2) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/post.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.post_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
}
}
res.code() == HttpResponseStatus.OK.code()
cleanup:
res.close()
}
@Unroll
def "erroneous runtime errors GET jsp with #test test"() {
setup:
String reqUrl = baseUrl + "/$jspFileName"
def req = new Request.Builder().url(new URL(reqUrl)).get().build()
when:
Response res = client.newCall(req).execute()
then:
assertTraces(TEST_WRITER, 1) {
trace(0, 3) {
span(0) {
parent()
serviceName JSP_WEBAPP_CONTEXT
operationName "servlet.request"
resourceName "GET /$JSP_WEBAPP_CONTEXT/$jspFileName"
spanType DDSpanTypes.WEB_SERVLET
errored true
tags {
"http.url" "http://localhost:$PORT/$JSP_WEBAPP_CONTEXT/$jspFileName"
"http.method" "GET"
"span.kind" "server"
"component" "java-web-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"http.status_code" 500
errorTags(JasperException, String)
defaultTags()
}
}
span(1) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/$jspFileName"
spanType DDSpanTypes.WEB_SERVLET
errored true
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.requestURL" reqUrl
errorTags(exceptionClass, errorMessage)
defaultTags()
}
}
span(2) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/$jspFileName"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.$jspClassName"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
}
}
res.code() == HttpResponseStatus.INTERNAL_SERVER_ERROR.code()
cleanup:
res.close()
where:
test | jspFileName | jspClassName | exceptionClass | errorMessage
"java runtime error" | "runtimeError.jsp" | "runtimeError_jsp" | ArithmeticException | String
"invalid write" | "invalidWrite.jsp" | "invalidWrite_jsp" | StringIndexOutOfBoundsException | String
"missing query gives null" | "getQuery.jsp" | "getQuery_jsp" | NullPointerException | null
}
def "non-erroneous include plain HTML GET"() {
setup:
String reqUrl = baseUrl + "/includes/includeHtml.jsp"
Request req = new Request.Builder().url(new URL(reqUrl)).get().build()
when:
Response res = client.newCall(req).execute()
then:
assertTraces(TEST_WRITER, 1) {
trace(0, 3) {
span(0) {
parent()
serviceName JSP_WEBAPP_CONTEXT
operationName "servlet.request"
resourceName "GET /$JSP_WEBAPP_CONTEXT/includes/includeHtml.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"http.url" "http://localhost:$PORT/$JSP_WEBAPP_CONTEXT/includes/includeHtml.jsp"
"http.method" "GET"
"span.kind" "server"
"component" "java-web-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"http.status_code" 200
defaultTags()
}
}
span(1) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/includes/includeHtml.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.requestURL" reqUrl
defaultTags()
}
}
span(2) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/includes/includeHtml.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.includes.includeHtml_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
}
}
res.code() == HttpResponseStatus.OK.code()
cleanup:
res.close()
}
def "non-erroneous multi GET"() {
setup:
String reqUrl = baseUrl + "/includes/includeMulti.jsp"
Request req = new Request.Builder().url(new URL(reqUrl)).get().build()
when:
Response res = client.newCall(req).execute()
then:
assertTraces(TEST_WRITER, 1) {
trace(0, 7) {
span(0) {
parent()
serviceName JSP_WEBAPP_CONTEXT
operationName "servlet.request"
resourceName "GET /$JSP_WEBAPP_CONTEXT/includes/includeMulti.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"http.url" "http://localhost:$PORT/$JSP_WEBAPP_CONTEXT/includes/includeMulti.jsp"
"http.method" "GET"
"span.kind" "server"
"component" "java-web-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"http.status_code" 200
defaultTags()
}
}
span(1) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/includes/includeMulti.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.requestURL" reqUrl
defaultTags()
}
}
span(2) {
childOf span(1)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/common/javaLoopH2.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.requestURL" reqUrl
defaultTags()
}
}
span(3) {
childOf span(1)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/common/javaLoopH2.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.common.javaLoopH2_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
span(4) {
childOf span(1)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/common/javaLoopH2.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.requestURL" reqUrl
defaultTags()
}
}
span(5) {
childOf span(1)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/common/javaLoopH2.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.common.javaLoopH2_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
span(6) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/includes/includeMulti.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.includes.includeMulti_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
}
}
res.code() == HttpResponseStatus.OK.code()
cleanup:
res.close()
}
def "#test compile error should not produce render traces and spans"() {
setup:
String reqUrl = baseUrl + "/$jspFileName"
Request req = new Request.Builder().url(new URL(reqUrl)).get().build()
when:
Response res = client.newCall(req).execute()
then:
assertTraces(TEST_WRITER, 1) {
trace(0, 2) {
span(0) {
parent()
serviceName JSP_WEBAPP_CONTEXT
operationName "servlet.request"
resourceName "GET /$JSP_WEBAPP_CONTEXT/$jspFileName"
spanType DDSpanTypes.WEB_SERVLET
errored true
tags {
"http.url" "http://localhost:$PORT/$JSP_WEBAPP_CONTEXT/$jspFileName"
"http.method" "GET"
"span.kind" "server"
"component" "java-web-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"http.status_code" 500
errorTags(JasperException, String)
defaultTags()
}
}
span(1) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/$jspFileName"
spanType DDSpanTypes.WEB_SERVLET
errored true
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.$jspClassNamePrefix$jspClassName"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
"jsp.javaFile" expectedJspClassFilesDir + jspClassNamePrefix.replace('.', '/') + jspClassName + ".java"
"jsp.classpath" String
errorTags(JasperException, String)
defaultTags()
}
}
}
}
res.code() == HttpResponseStatus.INTERNAL_SERVER_ERROR.code()
cleanup:
res.close()
where:
test | jspFileName | jspClassName | jspClassNamePrefix
"normal" | "compileError.jsp" | "compileError_jsp" | ""
"forward"|"forwards/forwardWithCompileError.jsp" | "forwardWithCompileError_jsp" | "forwards."
}
}

View File

@ -0,0 +1,718 @@
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.*
import org.apache.catalina.Context
import org.apache.catalina.startup.Tomcat
import org.apache.jasper.JasperException
import spock.lang.Unroll
import static datadog.trace.agent.test.ListWriterAssert.assertTraces
class JSPInstrumentationForwardTests extends AgentTestRunner {
static {
System.setProperty("dd.integration.jsp.enabled", "true")
// skip jar scanning using environment variables:
// http://tomcat.apache.org/tomcat-7.0-doc/config/systemprops.html#JAR_Scanning
// having this set allows us to test with old versions of the tomcat api since
// JarScanFilter did not exist in the tomcat 7 api
System.setProperty("org.apache.catalina.startup.ContextConfig.jarsToSkip", "*")
System.setProperty("org.apache.catalina.startup.TldConfig.jarsToSkip", "*")
}
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"
static File baseDir
static String baseUrl
static String expectedJspClassFilesDir = "/work/Tomcat/localhost/$JSP_WEBAPP_CONTEXT/org/apache/jsp/"
def setupSpec() {
tomcatServer = new Tomcat()
tomcatServer.setPort(PORT)
// comment to debug
tomcatServer.setSilent(true)
baseDir = Files.createTempDir()
baseDir.deleteOnExit()
expectedJspClassFilesDir = baseDir.getCanonicalFile().getAbsolutePath() + expectedJspClassFilesDir
baseUrl = "http://localhost:$PORT/$JSP_WEBAPP_CONTEXT"
tomcatServer.setBaseDir(baseDir.getAbsolutePath())
appContext = tomcatServer.addWebapp("/$JSP_WEBAPP_CONTEXT",
JSPInstrumentationForwardTests.getResource("/webapps/jsptest").getPath())
tomcatServer.start()
System.out.println(
"Tomcat server: http://" + tomcatServer.getHost().getName() + ":" + PORT + "/")
}
def cleanupSpec() {
tomcatServer.stop()
tomcatServer.destroy()
}
@Unroll
def "non-erroneous GET forward to #forwardTo"() {
setup:
String reqUrl = baseUrl + "/$forwardFromFileName"
Request req = new Request.Builder().url(new URL(reqUrl)).get().build()
when:
Response res = client.newCall(req).execute()
then:
assertTraces(TEST_WRITER, 1) {
trace(0, 5) {
span(0) {
parent()
serviceName JSP_WEBAPP_CONTEXT
operationName "servlet.request"
resourceName "GET /$JSP_WEBAPP_CONTEXT/$forwardFromFileName"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"http.url" "http://localhost:$PORT/$JSP_WEBAPP_CONTEXT/$forwardFromFileName"
"http.method" "GET"
"span.kind" "server"
"component" "java-web-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"http.status_code" 200
defaultTags()
}
}
span(1) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/$forwardFromFileName"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.requestURL" reqUrl
defaultTags()
}
}
span(2) {
childOf span(1)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/$forwardDestFileName"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.forwardOrigin" "/$forwardFromFileName"
"jsp.requestURL" baseUrl + "/$forwardDestFileName"
defaultTags()
}
}
span(3) {
childOf span(1)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/$forwardDestFileName"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.$jspForwardDestClassPrefix$jspForwardDestClassName"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
span(4) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/$forwardFromFileName"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.$jspForwardFromClassPrefix$jspForwardFromClassName"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
}
}
res.code() == HttpResponseStatus.OK.code()
cleanup:
res.close()
where:
forwardTo | forwardFromFileName | forwardDestFileName | jspForwardFromClassName | jspForwardFromClassPrefix | jspForwardDestClassName | jspForwardDestClassPrefix
"no java jsp" | "forwards/forwardToNoJavaJsp.jsp" | "nojava.jsp" | "forwardToNoJavaJsp_jsp" | "forwards." | "nojava_jsp" | ""
"normal java jsp" | "forwards/forwardToSimpleJava.jsp" | "common/loop.jsp" | "forwardToSimpleJava_jsp" | "forwards." | "loop_jsp" | "common."
}
def "non-erroneous GET forward to plain HTML"() {
setup:
String reqUrl = baseUrl + "/forwards/forwardToHtml.jsp"
Request req = new Request.Builder().url(new URL(reqUrl)).get().build()
when:
Response res = client.newCall(req).execute()
then:
assertTraces(TEST_WRITER, 1) {
trace(0, 3) {
span(0) {
parent()
serviceName JSP_WEBAPP_CONTEXT
operationName "servlet.request"
resourceName "GET /$JSP_WEBAPP_CONTEXT/forwards/forwardToHtml.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"http.url" "http://localhost:$PORT/$JSP_WEBAPP_CONTEXT/forwards/forwardToHtml.jsp"
"http.method" "GET"
"span.kind" "server"
"component" "java-web-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"http.status_code" 200
defaultTags()
}
}
span(1) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/forwards/forwardToHtml.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.requestURL" reqUrl
defaultTags()
}
}
span(2) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/forwards/forwardToHtml.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.forwards.forwardToHtml_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
}
}
res.code() == HttpResponseStatus.OK.code()
cleanup:
res.close()
}
def "non-erroneous GET forwarded to jsp with multiple includes"() {
setup:
String reqUrl = baseUrl + "/forwards/forwardToIncludeMulti.jsp"
Request req = new Request.Builder().url(new URL(reqUrl)).get().build()
when:
Response res = client.newCall(req).execute()
then:
assertTraces(TEST_WRITER, 1) {
trace(0, 9) {
span(0) {
parent()
serviceName JSP_WEBAPP_CONTEXT
operationName "servlet.request"
resourceName "GET /$JSP_WEBAPP_CONTEXT/forwards/forwardToIncludeMulti.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"http.url" "http://localhost:$PORT/$JSP_WEBAPP_CONTEXT/forwards/forwardToIncludeMulti.jsp"
"http.method" "GET"
"span.kind" "server"
"component" "java-web-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"http.status_code" 200
defaultTags()
}
}
span(1) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/forwards/forwardToIncludeMulti.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.requestURL" reqUrl
defaultTags()
}
}
span(2) {
childOf span(1)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/includes/includeMulti.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.forwardOrigin" "/forwards/forwardToIncludeMulti.jsp"
"jsp.requestURL" baseUrl + "/includes/includeMulti.jsp"
defaultTags()
}
}
span(3) {
childOf span(2)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/common/javaLoopH2.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.forwardOrigin" "/forwards/forwardToIncludeMulti.jsp"
"jsp.requestURL" baseUrl + "/includes/includeMulti.jsp"
defaultTags()
}
}
span(4) {
childOf span(2)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/common/javaLoopH2.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.common.javaLoopH2_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
span(5) {
childOf span(2)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/common/javaLoopH2.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.forwardOrigin" "/forwards/forwardToIncludeMulti.jsp"
"jsp.requestURL" baseUrl + "/includes/includeMulti.jsp"
defaultTags()
}
}
span(6) {
childOf span(2)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/common/javaLoopH2.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.common.javaLoopH2_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
span(7) {
childOf span(1)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/includes/includeMulti.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.includes.includeMulti_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
span(8) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/forwards/forwardToIncludeMulti.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.forwards.forwardToIncludeMulti_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
}
}
res.code() == HttpResponseStatus.OK.code()
cleanup:
res.close()
}
def "non-erroneous GET forward to another forward (2 forwards)"() {
setup:
String reqUrl = baseUrl + "/forwards/forwardToJspForward.jsp"
Request req = new Request.Builder().url(new URL(reqUrl)).get().build()
when:
Response res = client.newCall(req).execute()
then:
assertTraces(TEST_WRITER, 1) {
trace(0, 7) {
span(0) {
parent()
serviceName JSP_WEBAPP_CONTEXT
operationName "servlet.request"
resourceName "GET /$JSP_WEBAPP_CONTEXT/forwards/forwardToJspForward.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"http.url" "http://localhost:$PORT/$JSP_WEBAPP_CONTEXT/forwards/forwardToJspForward.jsp"
"http.method" "GET"
"span.kind" "server"
"component" "java-web-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"http.status_code" 200
defaultTags()
}
}
span(1) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/forwards/forwardToJspForward.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.requestURL" reqUrl
defaultTags()
}
}
span(2) {
childOf span(1)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/forwards/forwardToSimpleJava.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.forwardOrigin" "/forwards/forwardToJspForward.jsp"
"jsp.requestURL" baseUrl + "/forwards/forwardToSimpleJava.jsp"
defaultTags()
}
}
span(3) {
childOf span(2)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/common/loop.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.forwardOrigin" "/forwards/forwardToJspForward.jsp"
"jsp.requestURL" baseUrl + "/common/loop.jsp"
defaultTags()
}
}
span(4) {
childOf span(2)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/common/loop.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.common.loop_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
span(5) {
childOf span(1)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/forwards/forwardToSimpleJava.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.forwards.forwardToSimpleJava_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
span(6) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/forwards/forwardToJspForward.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.forwards.forwardToJspForward_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
}
}
res.code() == HttpResponseStatus.OK.code()
cleanup:
res.close()
}
def "forward to jsp with compile error should not produce a 2nd render span"() {
setup:
String reqUrl = baseUrl + "/forwards/forwardToCompileError.jsp"
Request req = new Request.Builder().url(new URL(reqUrl)).get().build()
when:
Response res = client.newCall(req).execute()
then:
assertTraces(TEST_WRITER, 1) {
trace(0, 4) {
span(0) {
parent()
serviceName JSP_WEBAPP_CONTEXT
operationName "servlet.request"
resourceName "GET /$JSP_WEBAPP_CONTEXT/forwards/forwardToCompileError.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored true
tags {
"http.url" "http://localhost:$PORT/$JSP_WEBAPP_CONTEXT/forwards/forwardToCompileError.jsp"
"http.method" "GET"
"span.kind" "server"
"component" "java-web-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"http.status_code" 500
errorTags(JasperException, String)
defaultTags()
}
}
span(1) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/forwards/forwardToCompileError.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored true
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.requestURL" reqUrl
errorTags(JasperException, String)
defaultTags()
}
}
span(2) {
childOf span(1)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/compileError.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored true
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.compileError_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
"jsp.javaFile" expectedJspClassFilesDir + "compileError_jsp.java"
"jsp.classpath" String
errorTags(JasperException, String)
defaultTags()
}
}
span(3) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/forwards/forwardToCompileError.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.forwards.forwardToCompileError_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
}
}
res.code() == HttpResponseStatus.INTERNAL_SERVER_ERROR.code()
cleanup:
res.close()
}
def "forward to non existent jsp should be 404"() {
setup:
String reqUrl = baseUrl + "/forwards/forwardToNonExistent.jsp"
Request req = new Request.Builder().url(new URL(reqUrl)).get().build()
when:
Response res = client.newCall(req).execute()
then:
assertTraces(TEST_WRITER, 1) {
trace(0, 3) {
span(0) {
parent()
serviceName JSP_WEBAPP_CONTEXT
operationName "servlet.request"
resourceName "404"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"http.url" "http://localhost:$PORT/$JSP_WEBAPP_CONTEXT/forwards/forwardToNonExistent.jsp"
"http.method" "GET"
"span.kind" "server"
"component" "java-web-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"http.status_code" 404
defaultTags()
}
}
span(1) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.render"
resourceName "/forwards/forwardToNonExistent.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.requestURL" reqUrl
defaultTags()
}
}
span(2) {
childOf span(0)
serviceName JSP_WEBAPP_CONTEXT
operationName "jsp.compile"
resourceName "/forwards/forwardToNonExistent.jsp"
spanType DDSpanTypes.WEB_SERVLET
errored false
tags {
"span.kind" "server"
"component" "jsp-http-servlet"
"span.type" DDSpanTypes.WEB_SERVLET
"servlet.context" "/$JSP_WEBAPP_CONTEXT"
"jsp.classFQCN" "org.apache.jsp.forwards.forwardToNonExistent_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
defaultTags()
}
}
}
}
res.code() == HttpResponseStatus.NOT_FOUND.code()
cleanup:
res.close()
}
}

View File

@ -0,0 +1,9 @@
<html>
<head>
<title>PLAIN HTML</title>
</head>
<body>
<h1>HELLO DATADOG</h1>
</body>
</html>

View File

@ -0,0 +1,7 @@
<%
for (int i = 0; i < 3; ++i) {
%>
<h2>number:<%= i %></h2><p></p>
<%
}
%>

View File

@ -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>

View File

@ -0,0 +1,9 @@
<html>
<head><title>COMPILE ERROR JSP</title></head>
<body>
<%
FakeClassThatDontExist thingyWithNoSemiColon = abcd
%>
<h2>This will fail</h2>
</body>
</html>

View File

@ -0,0 +1,9 @@
<jsp:forward page = "../compileError.jsp" />
<html>
<head>
<title>FORWARD TO JSP WITH COMPILE ERROR</title>
</head>
<body>
<h1>BYE!</h1>
</body>
</html>

View File

@ -0,0 +1,10 @@
<jsp:forward page = "../common/hello.html" />
<html>
<head>
<title>FORWARD TO PLAIN HTML</title>
</head>
<body>
<h1>BYE!</h1>
</body>
</html>

View File

@ -0,0 +1,10 @@
<jsp:forward page = "../includes/includeMulti.jsp" />
<html>
<head>
<title>FORWARD TO JSP WITH MULTIPLE INCLUDES</title>
</head>
<body>
<h1>BYE!</h1>
</body>
</html>

View File

@ -0,0 +1,10 @@
<jsp:forward page = "forwardToSimpleJava.jsp" />
<html>
<head>
<title>FORWARD TO ANOTHER JSP FORWARD THAT FORWARDS TO SIMPLE JAVA</title>
</head>
<body>
<h1>BYE!</h1>
</body>
</html>

View File

@ -0,0 +1,9 @@
<jsp:forward page = "../nojava.jsp" />
<html>
<head>
<title>FORWARD TO NO JAVA</title>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,10 @@
<jsp:forward page = "nonExistentFile.jsp" />
<html>
<head>
<title>FORWARD TO NON EXISTENT FILE</title>
</head>
<body>
<h1>BYE!</h1>
</body>
</html>

View File

@ -0,0 +1,10 @@
<jsp:forward page = "../common/loop.jsp" />
<html>
<head>
<title>FORWARD TO SIMPLE JAVA</title>
</head>
<body>
<h1>BYE!</h1>
</body>
</html>

View File

@ -0,0 +1,12 @@
<jsp:forward page = "../common/loop.jsp" />
<html>
<head>
<title>FORWARD WITH COMPILE ERROR</title>
</head>
<%
FakeNonExistentClass fec = new FakeNonExistentClass()
%>
<body>
<h1>BYE!</h1>
</body>
</html>

View File

@ -0,0 +1,14 @@
<html>
<head><title>GET QUERY JSP</title></head>
<body>
<%
String query = request.getQueryString();
%>
<h2><%= query %></h2><p></p>
<%
if (query.equals("HELLO")) {
out.print("WORLD");
}
%>
</body>
</html>

View File

@ -0,0 +1,12 @@
<html>
<head>
<title>INCLUDE HTML JSP</title>
</head>
<body>
<center>
<h2>INCLUDE HTML</h2>
<jsp:include page = "../common/hello.html"/>
</center>
</body>
</html>

View File

@ -0,0 +1,13 @@
<html>
<head>
<title>MULTIPLE INCLUDE ACTION JSP</title>
</head>
<body>
<center>
<h2>INCLUDE MULTI</h2>
<jsp:include page = "../common/javaLoopH2.jsp" flush = "true" />
<jsp:include page = "../common/javaLoopH2.jsp" flush = "true" />
</center>
</body>
</html>

View File

@ -0,0 +1,12 @@
<html>
<head><title>INVALID MARKUP JSP
<body>
<%
for (int i = 0; i < 3; ++i) {
%>
<h2>number:<%= i %></h2><p></p>
<%
}
%>
</boody>
</html>

View File

@ -0,0 +1,8 @@
<html>
<head><title>RUNTIME ERROR JSP: INVALID WRITE</title></head>
<body>
<%
response.getWriter().write("hello world", 0, 2147483647);
%>
</body>
</html>

View File

@ -0,0 +1,6 @@
<html>
<head><title>NO JAVA JSP</title></head>
<body>
<h2>there's no java code here</h2><p></p>
</body>
</html>

View File

@ -0,0 +1,6 @@
<html>
<head><title>POST JSP</title></head>
<body>
<h1>Hello <%= request.getParameter("name") %>!</h2><p></p>
</body>
</html>

View File

@ -0,0 +1,9 @@
<html>
<head><title>RUNTIME ERROR JSP: DIVISION BY ZERO</title></head>
<body>
<h2>This will fail ... </h2><p>
<%
int k = 9 / 0;
%>
</body>
</html>

View File

@ -1,6 +1,5 @@
package datadog.trace.instrumentation.servlet3;
import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static net.bytebuddy.matcher.ElementMatchers.failSafe;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
@ -41,13 +40,13 @@ public final class HttpServlet3Instrumentation extends Instrumenter.Configurable
public HttpServlet3Instrumentation() {
super("servlet", "servlet-3");
}
//,
// classLoaderHasClasses("javax.servlet.AsyncEvent", "javax.servlet.AsyncListener")
@Override
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(failSafe(hasSuperType(named("javax.servlet.http.HttpServlet")))),
classLoaderHasClasses("javax.servlet.AsyncEvent", "javax.servlet.AsyncListener"))
not(isInterface()).and(failSafe(hasSuperType(named("javax.servlet.http.HttpServlet")))))
.transform(
new HelperInjector(
"datadog.trace.instrumentation.servlet3.HttpServletRequestExtractAdapter",

View File

@ -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-2.3'
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'