diff --git a/dd-java-agent/instrumentation/jsp/jsp.gradle b/dd-java-agent/instrumentation/jsp/jsp.gradle index 3caeb1c693..a6f899b766 100644 --- a/dd-java-agent/instrumentation/jsp/jsp.gradle +++ b/dd-java-agent/instrumentation/jsp/jsp.gradle @@ -1,3 +1,15 @@ +apply plugin: 'version-scan' + +versionScan { + group = "org.apache.tomcat" + module = "tomcat-jasper" + versions = "[8.0.1,)" + scanDependencies = true + verifyPresent = [ + "org.apache.jasper.servlet.JspServletWrapper": null, + ] +} + apply from: "${rootDir}/gradle/java.gradle" apply plugin: 'org.unbroken-dome.test-sets' @@ -9,6 +21,7 @@ testSets { } dependencies { + compileOnly group: 'org.apache.tomcat', name: 'tomcat-jasper', version: '7.0.42' 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' @@ -19,10 +32,13 @@ dependencies { 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 project(':dd-java-agent:testing') + testCompile project(':dd-java-agent:instrumentation:servlet-3') + testCompile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '7.0.42' + testCompile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '7.0.42' + testCompile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-logging-juli', version: '7.0.42' + testCompile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '7.0.42' testCompile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.10.0' } diff --git a/dd-java-agent/instrumentation/jsp/src/main/java/datadog/trace/instrumentation/jsp/JSPInstrumentation.java b/dd-java-agent/instrumentation/jsp/src/main/java/datadog/trace/instrumentation/jsp/JSPInstrumentation.java index 8be9d68843..6094bcc21e 100644 --- a/dd-java-agent/instrumentation/jsp/src/main/java/datadog/trace/instrumentation/jsp/JSPInstrumentation.java +++ b/dd-java-agent/instrumentation/jsp/src/main/java/datadog/trace/instrumentation/jsp/JSPInstrumentation.java @@ -78,7 +78,6 @@ public final class JSPInstrumentation extends Instrumenter.Configurable { // actions span.setTag("jsp.requestURL", req.getRequestURL().toString()); Tags.COMPONENT.set(span, "jsp-http-servlet"); - Tags.HTTP_METHOD.set(span, req.getMethod()); return scope; } @@ -91,14 +90,8 @@ public final class JSPInstrumentation extends Instrumenter.Configurable { final Span span = scope.span(); 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(); } diff --git a/dd-java-agent/instrumentation/jsp/src/main/java/datadog/trace/instrumentation/jsp/JasperJSPCompilationContextInstrumentation.java b/dd-java-agent/instrumentation/jsp/src/main/java/datadog/trace/instrumentation/jsp/JasperJSPCompilationContextInstrumentation.java new file mode 100644 index 0000000000..b492c9f483 --- /dev/null +++ b/dd-java-agent/instrumentation/jsp/src/main/java/datadog/trace/instrumentation/jsp/JasperJSPCompilationContextInstrumentation.java @@ -0,0 +1,91 @@ +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) + .withTag( + "servlet.context", jspCompilationContext.getServletContext().getContextPath()) + .startActive(true); + + final Span span = scope.span(); + 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) { + 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(); + } + } +} diff --git a/dd-java-agent/instrumentation/jsp/src/test/groovy/JSPInstrumentationTest.groovy b/dd-java-agent/instrumentation/jsp/src/test/groovy/JSPInstrumentationBasicTests.groovy similarity index 58% rename from dd-java-agent/instrumentation/jsp/src/test/groovy/JSPInstrumentationTest.groovy rename to dd-java-agent/instrumentation/jsp/src/test/groovy/JSPInstrumentationBasicTests.groovy index 98c1093f86..3f5ff7def4 100644 --- a/dd-java-agent/instrumentation/jsp/src/test/groovy/JSPInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/jsp/src/test/groovy/JSPInstrumentationBasicTests.groovy @@ -10,14 +10,14 @@ import okhttp3.RequestBody 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 org.apache.jasper.JasperException +//import org.apache.tomcat.JarScanFilter +//import org.apache.tomcat.JarScanType import spock.lang.Unroll import static datadog.trace.agent.test.ListWriterAssert.assertTraces -class JSPInstrumentationTest extends AgentTestRunner { +class JSPInstrumentationBasicTests extends AgentTestRunner { static { System.setProperty("dd.integration.jsp.enabled", "true") @@ -35,7 +35,6 @@ class JSPInstrumentationTest extends AgentTestRunner { static Context appContext static final String JSP_WEBAPP_CONTEXT = "jsptest-context" - @Shared static File baseDir static String baseUrl static String expectedJspClassFilesDir = "/work/Tomcat/localhost/$JSP_WEBAPP_CONTEXT/org/apache/jsp/" @@ -53,15 +52,15 @@ class JSPInstrumentationTest extends AgentTestRunner { tomcatServer.setBaseDir(baseDir.getAbsolutePath()) appContext = tomcatServer.addWebapp("/$JSP_WEBAPP_CONTEXT", - JSPInstrumentationTest.getResource("/webapps/jsptest").getPath()) + JSPInstrumentationBasicTests.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 - } - }) +// appContext.getJarScanner().setJarScanFilter(new JarScanFilter() { +// @Override +// boolean check(JarScanType jarScanType, String jarName) { +// return false +// } +// }) tomcatServer.start() System.out.println( @@ -84,21 +83,55 @@ class JSPInstrumentationTest extends AgentTestRunner { then: assertTraces(TEST_WRITER, 1) { - trace(0, 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 { - "http.method" "GET" "span.kind" "server" "component" "jsp-http-servlet" "span.type" DDSpanTypes.WEB_SERVLET "servlet.context" "/$JSP_WEBAPP_CONTEXT" "jsp.requestURL" reqUrl - "http.status_code" 200 + 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() } } @@ -106,11 +139,14 @@ class JSPInstrumentationTest extends AgentTestRunner { } res.code() == HttpResponseStatus.OK.code() + cleanup: + res.close() + where: - test | jspFileName - "no java jsp" | "nojava.jsp" - "basic loop jsp"|"common/loop.jsp" - "invalid HTML markup"|"invalidMarkup.jsp" + 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"() { @@ -124,27 +160,64 @@ class JSPInstrumentationTest extends AgentTestRunner { then: assertTraces(TEST_WRITER, 1) { - trace(0, 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 { - "http.method" "GET" "span.kind" "server" "component" "jsp-http-servlet" "span.type" DDSpanTypes.WEB_SERVLET "servlet.context" "/$JSP_WEBAPP_CONTEXT" "jsp.requestURL" reqUrl - "http.status_code" 200 + 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"() { @@ -161,27 +234,64 @@ class JSPInstrumentationTest extends AgentTestRunner { then: assertTraces(TEST_WRITER, 1) { - trace(0, 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 { - "http.method" "POST" "span.kind" "server" "component" "jsp-http-servlet" "span.type" DDSpanTypes.WEB_SERVLET "servlet.context" "/$JSP_WEBAPP_CONTEXT" "jsp.requestURL" reqUrl - "http.status_code" 200 + 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 @@ -195,34 +305,142 @@ class JSPInstrumentationTest extends AgentTestRunner { then: assertTraces(TEST_WRITER, 1) { - trace(0, 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 { - "http.method" "GET" "span.kind" "server" "component" "jsp-http-servlet" "span.type" DDSpanTypes.WEB_SERVLET "servlet.context" "/$JSP_WEBAPP_CONTEXT" "jsp.requestURL" reqUrl - "http.status_code" 500 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 | exceptionClass | errorMessage - "java runtime error" | "runtimeError.jsp" | ArithmeticException | String - "invalid write" | "invalidWrite.jsp" | StringIndexOutOfBoundsException | String - "missing query gives null" | "getQuery.jsp" | NullPointerException | null + 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"() { @@ -234,312 +452,193 @@ class JSPInstrumentationTest extends AgentTestRunner { Response res = client.newCall(req).execute() then: - println(res.body().string()) assertTraces(TEST_WRITER, 1) { - trace(0, 3) { + 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 { - "http.method" "GET" "span.kind" "server" "component" "jsp-http-servlet" "span.type" DDSpanTypes.WEB_SERVLET "servlet.context" "/$JSP_WEBAPP_CONTEXT" "jsp.requestURL" reqUrl - "http.status_code" 200 - defaultTags() - } - } - span(1) { - serviceName JSP_WEBAPP_CONTEXT - operationName "jsp.render" - resourceName "/common/javaLoopH2.jsp" - spanType DDSpanTypes.WEB_SERVLET - errored false - tags { - "http.method" "GET" - "span.kind" "server" - "component" "jsp-http-servlet" - "span.type" DDSpanTypes.WEB_SERVLET - "servlet.context" "/$JSP_WEBAPP_CONTEXT" - "jsp.requestURL" reqUrl - "http.status_code" 200 defaultTags() } } span(2) { + childOf span(1) serviceName JSP_WEBAPP_CONTEXT operationName "jsp.render" resourceName "/common/javaLoopH2.jsp" spanType DDSpanTypes.WEB_SERVLET errored false tags { - "http.method" "GET" "span.kind" "server" "component" "jsp-http-servlet" "span.type" DDSpanTypes.WEB_SERVLET "servlet.context" "/$JSP_WEBAPP_CONTEXT" "jsp.requestURL" reqUrl - "http.status_code" 200 - defaultTags() - } - } - } - } - res.code() == HttpResponseStatus.OK.code() - } - - @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: - println(res.body().string()) - assertTraces(TEST_WRITER, 1) { - trace(0, 2) { - span(0) { - serviceName JSP_WEBAPP_CONTEXT - operationName "jsp.render" - resourceName "/$forwardFromFileName" - spanType DDSpanTypes.WEB_SERVLET - errored false - tags { - "http.method" "GET" - "span.kind" "server" - "component" "jsp-http-servlet" - "span.type" DDSpanTypes.WEB_SERVLET - "servlet.context" "/$JSP_WEBAPP_CONTEXT" - "jsp.requestURL" reqUrl - "http.status_code" 200 - defaultTags() - } - } - span(1) { - serviceName JSP_WEBAPP_CONTEXT - operationName "jsp.render" - resourceName "/$forwardDestFileName" - spanType DDSpanTypes.WEB_SERVLET - errored false - tags { - "http.method" "GET" - "span.kind" "server" - "component" "jsp-http-servlet" - "span.type" DDSpanTypes.WEB_SERVLET - "servlet.context" "/$JSP_WEBAPP_CONTEXT" - "jsp.forwardOrigin" "/$forwardFromFileName" - "jsp.requestURL" baseUrl + "/$forwardDestFileName" - "http.status_code" 200 - defaultTags() - } - } - } - } - res.code() == HttpResponseStatus.OK.code() - - where: - forwardTo | forwardFromFileName | forwardDestFileName - "no java jsp" | "forwards/forwardToNoJavaJsp.jsp" | "nojava.jsp" - "normal java jsp" | "forwards/forwardToSimpleJava.jsp" | "common/loop.jsp" - } - - 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: - println(res.body().string()) - assertTraces(TEST_WRITER, 1) { - trace(0, 1) { - span(0) { - serviceName JSP_WEBAPP_CONTEXT - operationName "jsp.render" - resourceName "/forwards/forwardToHtml.jsp" - spanType DDSpanTypes.WEB_SERVLET - errored false - tags { - "http.method" "GET" - "span.kind" "server" - "component" "jsp-http-servlet" - "span.type" DDSpanTypes.WEB_SERVLET - "servlet.context" "/$JSP_WEBAPP_CONTEXT" - "jsp.requestURL" reqUrl - "http.status_code" 200 - defaultTags() - } - } - } - } - res.code() == HttpResponseStatus.OK.code() - } - - 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: - println(res.body().string()) - assertTraces(TEST_WRITER, 1) { - trace(0, 4) { - span(0) { - serviceName JSP_WEBAPP_CONTEXT - operationName "jsp.render" - resourceName "/forwards/forwardToIncludeMulti.jsp" - spanType DDSpanTypes.WEB_SERVLET - errored false - tags { - "http.method" "GET" - "span.kind" "server" - "component" "jsp-http-servlet" - "span.type" DDSpanTypes.WEB_SERVLET - "servlet.context" "/$JSP_WEBAPP_CONTEXT" - "jsp.requestURL" reqUrl - "http.status_code" 200 - defaultTags() - } - } - span(1) { - serviceName JSP_WEBAPP_CONTEXT - operationName "jsp.render" - resourceName "/includes/includeMulti.jsp" - spanType DDSpanTypes.WEB_SERVLET - errored false - tags { - "http.method" "GET" - "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" - "http.status_code" 200 - defaultTags() - } - } - span(2) { - serviceName JSP_WEBAPP_CONTEXT - operationName "jsp.render" - resourceName "/common/javaLoopH2.jsp" - spanType DDSpanTypes.WEB_SERVLET - errored false - tags { - "http.method" "GET" - "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" - "http.status_code" 200 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 { - "http.method" "GET" "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" - "http.status_code" 200 + "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 "non-erroneous GET forward to another forward (2 forwards)"() { + def "#test compile error should not produce render traces and spans"() { setup: - String reqUrl = baseUrl + "/forwards/forwardToJspForward.jsp" + String reqUrl = baseUrl + "/$jspFileName" Request req = new Request.Builder().url(new URL(reqUrl)).get().build() when: Response res = client.newCall(req).execute() then: - println(res.body().string()) assertTraces(TEST_WRITER, 1) { - trace(0, 3) { + trace(0, 2) { span(0) { + parent() serviceName JSP_WEBAPP_CONTEXT - operationName "jsp.render" - resourceName "/forwards/forwardToJspForward.jsp" + operationName "servlet.request" + resourceName "GET /$JSP_WEBAPP_CONTEXT/$jspFileName" spanType DDSpanTypes.WEB_SERVLET - errored false + errored true tags { + "http.url" "http://localhost:$PORT/$JSP_WEBAPP_CONTEXT/$jspFileName" "http.method" "GET" "span.kind" "server" - "component" "jsp-http-servlet" + "component" "java-web-servlet" "span.type" DDSpanTypes.WEB_SERVLET "servlet.context" "/$JSP_WEBAPP_CONTEXT" - "jsp.requestURL" reqUrl - "http.status_code" 200 + "http.status_code" 500 + errorTags(JasperException, String) defaultTags() } } span(1) { + childOf span(0) serviceName JSP_WEBAPP_CONTEXT - operationName "jsp.render" - resourceName "/forwards/forwardToSimpleJava.jsp" + operationName "jsp.compile" + resourceName "/$jspFileName" spanType DDSpanTypes.WEB_SERVLET - errored false + errored true tags { - "http.method" "GET" "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" - "http.status_code" 200 - defaultTags() - } - } - span(2) { - serviceName JSP_WEBAPP_CONTEXT - operationName "jsp.render" - resourceName "/common/loop.jsp" - spanType DDSpanTypes.WEB_SERVLET - errored false - tags { - "http.method" "GET" - "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" - "http.status_code" 200 + "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.OK.code() + 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." } } diff --git a/dd-java-agent/instrumentation/jsp/src/test/groovy/JSPInstrumentationForwardTests.groovy b/dd-java-agent/instrumentation/jsp/src/test/groovy/JSPInstrumentationForwardTests.groovy new file mode 100644 index 0000000000..124c3d483b --- /dev/null +++ b/dd-java-agent/instrumentation/jsp/src/test/groovy/JSPInstrumentationForwardTests.groovy @@ -0,0 +1,722 @@ +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 org.apache.tomcat.JarScanFilter +//import org.apache.tomcat.JarScanType +import spock.lang.Unroll + +import static datadog.trace.agent.test.ListWriterAssert.assertTraces + +class JSPInstrumentationForwardTests extends AgentTestRunner { + + static { + System.setProperty("dd.integration.jsp.enabled", "true") + } + + 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()) + + // 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() + } + + @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() + } +} diff --git a/dd-java-agent/instrumentation/jsp/src/test/resources/webapps/jsptest/compileError.jsp b/dd-java-agent/instrumentation/jsp/src/test/resources/webapps/jsptest/compileError.jsp new file mode 100644 index 0000000000..1929ec35df --- /dev/null +++ b/dd-java-agent/instrumentation/jsp/src/test/resources/webapps/jsptest/compileError.jsp @@ -0,0 +1,9 @@ + +