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