Fix ServletContextPath.prepend for app server spans (#2089)

* ServletContextPath.prepend doesn't work when server span is created from app server integration

* move ServletContextPath context creation to servlet-common, make servlet2&3 depend on servlet-common so that it would be used in tests that depend on servlet3

* fix failing test

* add servlet-common dependency to modules that depend on servlet3

* add servlet-common dependency to mojarra and myfaces

* run context path instrumentation after servlet instrumentation

* add servlet-common dependency to wicket

* move servlet context path handling

* enable jetty instrumentation for all handlers

* run springmvc tests with tomcat integration, fix peer port and peer ip reporting on tomcat

* jetty integration is now enabled for all handlers

* update expected span name

* Revert "jetty integration is now enabled for all handlers"

This reverts commit 82cbb663f5.

* Revert "update expected span name"

This reverts commit c034496fc5.

* Revert "enable jetty instrumentation for all handlers"

This reverts commit 8a3d077600.

* Trigger Build
This commit is contained in:
Lauri Tulmin 2021-02-16 09:34:15 +02:00 committed by GitHub
parent 1311bd1ed8
commit e76d3b19d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 568 additions and 95 deletions

View File

@ -27,9 +27,13 @@ public abstract class ServletHttpServerTracer<RESPONSE>
public Context startSpan(HttpServletRequest request) {
Context context = startSpan(request, request, request, getSpanName(request));
return addServletContextPath(context, request);
}
private static Context addServletContextPath(Context context, HttpServletRequest request) {
String contextPath = request.getContextPath();
if (contextPath != null && !contextPath.isEmpty() && !contextPath.equals("/")) {
context = context.with(ServletContextPath.CONTEXT_KEY, contextPath);
return context.with(ServletContextPath.CONTEXT_KEY, contextPath);
}
return context;
}
@ -159,11 +163,13 @@ public abstract class ServletHttpServerTracer<RESPONSE>
* forward and other scenarios, where servlet path may change, but we don't want this to be
* reflected in the span name.
*/
public void updateServerSpanNameOnce(Context attachedContext, HttpServletRequest request) {
if (AppServerBridge.shouldUpdateServerSpanName(attachedContext)) {
updateSpanName(Span.fromContext(attachedContext), request);
AppServerBridge.setServletUpdatedServerSpanName(attachedContext, true);
public Context runOnceUnderAppServer(Context context, HttpServletRequest request) {
if (AppServerBridge.shouldUpdateServerSpanName(context)) {
updateSpanName(Span.fromContext(context), request);
AppServerBridge.setServletUpdatedServerSpanName(context, true);
return addServletContextPath(context, request);
}
return context;
}
public void updateSpanName(HttpServletRequest request) {

View File

@ -17,6 +17,7 @@ dependencies {
implementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-common:javaagent')
testInstrumentation project(':instrumentation:servlet:servlet-3.0:javaagent')
testInstrumentation project(':instrumentation:servlet:servlet-common:javaagent')
testImplementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-testing')

View File

@ -30,4 +30,9 @@ class JerseyHttpServerTest extends JaxRsHttpServerTest<Server> {
void stopServer(Server httpServer) {
httpServer.stop()
}
@Override
boolean asyncCancelHasSendError() {
true
}
}

View File

@ -27,6 +27,7 @@ dependencies {
implementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-common:javaagent')
testInstrumentation project(':instrumentation:servlet:servlet-3.0:javaagent')
testInstrumentation project(':instrumentation:servlet:servlet-common:javaagent')
testImplementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-testing')

View File

@ -27,6 +27,7 @@ dependencies {
implementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-common:javaagent')
testInstrumentation project(':instrumentation:servlet:servlet-3.0:javaagent')
testInstrumentation project(':instrumentation:servlet:servlet-common:javaagent')
testImplementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-testing')

View File

@ -49,10 +49,18 @@ abstract class JaxRsHttpServerTest<S> extends HttpServerTest<S> {
assert response.code() == statusCode
assert bodyPredicate(response.body().string())
def spanCount = 2
def hasSendError = asyncCancelHasSendError() && action == "cancel"
if (hasSendError) {
spanCount++
}
assertTraces(1) {
trace(0, 2) {
trace(0, spanCount) {
asyncServerSpan(it, 0, url, statusCode)
handlerSpan(it, 1, span(0), "asyncOp", isCancelled, isError, errorMessage)
if (hasSendError) {
sendErrorSpan(it, 2, span(1))
}
}
}
@ -117,6 +125,10 @@ abstract class JaxRsHttpServerTest<S> extends HttpServerTest<S> {
true
}
boolean asyncCancelHasSendError() {
false
}
private static boolean shouldTestCompletableStageAsync() {
Boolean.getBoolean("testLatestDeps")
}

View File

@ -13,6 +13,7 @@ muzzle {
dependencies {
library group: 'org.eclipse.jetty', name: 'jetty-server', version: '8.0.0.v20110901'
implementation project(':instrumentation:servlet:servlet-3.0:javaagent')
testInstrumentation project(':instrumentation:servlet:servlet-common:javaagent')
// Don't want to conflict with jetty from the test server.
testImplementation(project(':testing-common')) {

View File

@ -10,6 +10,7 @@ import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEn
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.REDIRECT
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.SUCCESS
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
import io.opentelemetry.instrumentation.test.base.HttpServerTest
import javax.servlet.DispatcherType
import javax.servlet.ServletException
@ -62,6 +63,23 @@ class JettyHandlerTest extends HttpServerTest<Server> {
false
}
@Override
boolean hasResponseSpan(ServerEndpoint endpoint) {
endpoint == REDIRECT || endpoint == ERROR
}
@Override
void responseSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) {
switch (endpoint) {
case REDIRECT:
redirectSpan(trace, index, parent)
break
case ERROR:
sendErrorSpan(trace, index, parent)
break
}
}
static void handleRequest(Request request, HttpServletResponse response) {
ServerEndpoint endpoint = ServerEndpoint.forPath(request.requestURI)
controller(endpoint) {

View File

@ -68,6 +68,7 @@ dependencies {
testImplementation project(':instrumentation:jsf:jsf-testing-common')
testInstrumentation project(':instrumentation:servlet:servlet-3.0:javaagent')
testInstrumentation project(':instrumentation:servlet:servlet-common:javaagent')
mojarra12TestImplementation group: 'javax.faces', name: 'jsf-impl', version: '1.2-20'
mojarra12TestImplementation group: 'javax.faces', name: 'jsf-api', version: '1.2'

View File

@ -30,6 +30,7 @@ dependencies {
testImplementation project(':instrumentation:jsf:jsf-testing-common')
testInstrumentation project(':instrumentation:servlet:servlet-3.0:javaagent')
testInstrumentation project(':instrumentation:servlet:servlet-common:javaagent')
myfaces12TestImplementation group: 'org.apache.myfaces.core', name: 'myfaces-impl', version: '1.2.12'
myfaces12TestImplementation group: 'com.sun.facelets', name: 'jsf-facelets', version: '1.1.14'

View File

@ -18,6 +18,8 @@ dependencies {
compileOnly group: 'javax.servlet', name: 'javax.servlet-api', version: '3.1.0'
testInstrumentation project(':instrumentation:servlet:servlet-3.0:javaagent')
testInstrumentation project(':instrumentation:servlet:servlet-common:javaagent')
// 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
testLibrary group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '7.0.37'

View File

@ -332,7 +332,7 @@ class JspInstrumentationBasicTests extends AgentInstrumentationSpecification {
then:
assertTraces(1) {
trace(0, 3) {
trace(0, 4) {
span(0) {
hasNoParent()
name "/$jspWebappContext/includes/includeHtml.jsp"
@ -366,6 +366,11 @@ class JspInstrumentationBasicTests extends AgentInstrumentationSpecification {
"jsp.requestURL" reqUrl
}
}
span(3) {
childOf span(2)
name "ApplicationDispatcher.include"
errored false
}
}
}
res.code() == 200
@ -384,7 +389,7 @@ class JspInstrumentationBasicTests extends AgentInstrumentationSpecification {
then:
assertTraces(1) {
trace(0, 7) {
trace(0, 9) {
span(0) {
hasNoParent()
name "/$jspWebappContext/includes/includeMulti.jsp"
@ -420,6 +425,11 @@ class JspInstrumentationBasicTests extends AgentInstrumentationSpecification {
}
span(3) {
childOf span(2)
name "ApplicationDispatcher.include"
errored false
}
span(4) {
childOf span(3)
name "Compile /common/javaLoopH2.jsp"
errored false
attributes {
@ -427,16 +437,21 @@ class JspInstrumentationBasicTests extends AgentInstrumentationSpecification {
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
}
}
span(4) {
childOf span(2)
span(5) {
childOf span(3)
name "Render /common/javaLoopH2.jsp"
errored false
attributes {
"jsp.requestURL" reqUrl
}
}
span(5) {
span(6) {
childOf span(2)
name "ApplicationDispatcher.include"
errored false
}
span(7) {
childOf span(6)
name "Compile /common/javaLoopH2.jsp"
errored false
attributes {
@ -444,8 +459,8 @@ class JspInstrumentationBasicTests extends AgentInstrumentationSpecification {
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
}
}
span(6) {
childOf span(2)
span(8) {
childOf span(6)
name "Render /common/javaLoopH2.jsp"
errored false
attributes {

View File

@ -80,7 +80,7 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
then:
assertTraces(1) {
trace(0, 5) {
trace(0, 6) {
span(0) {
hasNoParent()
name "/$jspWebappContext/$forwardFromFileName"
@ -116,6 +116,11 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
}
span(3) {
childOf span(2)
name "ApplicationDispatcher.forward"
errored false
}
span(4) {
childOf span(3)
name "Compile /$forwardDestFileName"
errored false
attributes {
@ -123,8 +128,8 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
}
}
span(4) {
childOf span(2)
span(5) {
childOf span(3)
name "Render /$forwardDestFileName"
errored false
attributes {
@ -155,7 +160,7 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
then:
assertTraces(1) {
trace(0, 3) {
trace(0, 4) {
span(0) {
hasNoParent()
name "/$jspWebappContext/forwards/forwardToHtml.jsp"
@ -189,6 +194,11 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
"jsp.requestURL" reqUrl
}
}
span(3) {
childOf span(2)
name "ApplicationDispatcher.forward"
errored false
}
}
}
res.code() == 200
@ -207,7 +217,7 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
then:
assertTraces(1) {
trace(0, 9) {
trace(0, 12) {
span(0) {
hasNoParent()
name "/$jspWebappContext/forwards/forwardToIncludeMulti.jsp"
@ -243,6 +253,11 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
}
span(3) {
childOf span(2)
name "ApplicationDispatcher.forward"
errored false
}
span(4) {
childOf span(3)
name "Compile /includes/includeMulti.jsp"
errored false
attributes {
@ -250,8 +265,8 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
}
}
span(4) {
childOf span(2)
span(5) {
childOf span(3)
name "Render /includes/includeMulti.jsp"
errored false
attributes {
@ -259,26 +274,13 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
"jsp.requestURL" baseUrl + "/includes/includeMulti.jsp"
}
}
span(5) {
childOf span(4)
name "Compile /common/javaLoopH2.jsp"
errored false
attributes {
"jsp.classFQCN" "org.apache.jsp.common.javaLoopH2_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
}
}
span(6) {
childOf span(4)
name "Render /common/javaLoopH2.jsp"
childOf span(5)
name "ApplicationDispatcher.include"
errored false
attributes {
"jsp.forwardOrigin" "/forwards/forwardToIncludeMulti.jsp"
"jsp.requestURL" baseUrl + "/includes/includeMulti.jsp"
}
}
span(7) {
childOf span(4)
childOf span(6)
name "Compile /common/javaLoopH2.jsp"
errored false
attributes {
@ -287,7 +289,30 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
}
}
span(8) {
childOf span(4)
childOf span(6)
name "Render /common/javaLoopH2.jsp"
errored false
attributes {
"jsp.forwardOrigin" "/forwards/forwardToIncludeMulti.jsp"
"jsp.requestURL" baseUrl + "/includes/includeMulti.jsp"
}
}
span(9) {
childOf span(5)
name "ApplicationDispatcher.include"
errored false
}
span(10) {
childOf span(9)
name "Compile /common/javaLoopH2.jsp"
errored false
attributes {
"jsp.classFQCN" "org.apache.jsp.common.javaLoopH2_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
}
}
span(11) {
childOf span(9)
name "Render /common/javaLoopH2.jsp"
errored false
attributes {
@ -313,7 +338,7 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
then:
assertTraces(1) {
trace(0, 7) {
trace(0, 9) {
span(0) {
hasNoParent()
name "/$jspWebappContext/forwards/forwardToJspForward.jsp"
@ -349,6 +374,11 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
}
span(3) {
childOf span(2)
name "ApplicationDispatcher.forward"
errored false
}
span(4) {
childOf span(3)
name "Compile /forwards/forwardToSimpleJava.jsp"
errored false
attributes {
@ -356,8 +386,8 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
}
}
span(4) {
childOf span(2)
span(5) {
childOf span(3)
name "Render /forwards/forwardToSimpleJava.jsp"
errored false
attributes {
@ -365,8 +395,13 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
"jsp.requestURL" baseUrl + "/forwards/forwardToSimpleJava.jsp"
}
}
span(5) {
childOf span(4)
span(6) {
childOf span(5)
name "ApplicationDispatcher.forward"
errored false
}
span(7) {
childOf span(6)
name "Compile /common/loop.jsp"
errored false
attributes {
@ -374,8 +409,8 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
}
}
span(6) {
childOf span(4)
span(8) {
childOf span(6)
name "Render /common/loop.jsp"
errored false
attributes {
@ -401,7 +436,7 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
then:
assertTraces(1) {
trace(0, 4) {
trace(0, 5) {
span(0) {
hasNoParent()
name "/$jspWebappContext/forwards/forwardToCompileError.jsp"
@ -439,6 +474,12 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
}
span(3) {
childOf span(2)
name "ApplicationDispatcher.forward"
errored true
errorEvent(JasperException, String)
}
span(4) {
childOf span(3)
name "Compile /compileError.jsp"
errored true
errorEvent(JasperException, String)
@ -465,7 +506,7 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
then:
assertTraces(1) {
trace(0, 3) {
trace(0, 5) {
span(0) {
hasNoParent()
name "/$jspWebappContext/forwards/forwardToNonExistent.jsp"
@ -499,6 +540,14 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification {
"jsp.requestURL" reqUrl
}
}
span(3) {
childOf span(2)
name "ApplicationDispatcher.forward"
}
span(4) {
childOf span(3)
name "ResponseFacade.sendError"
}
}
}
res.code() == 404

View File

@ -6,7 +6,6 @@
package io.opentelemetry.javaagent.instrumentation.liberty;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.servlet.ServletContextPath;
import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3HttpServerTracer;
import javax.servlet.http.HttpServletRequest;
@ -22,12 +21,7 @@ public class LibertyHttpServerTracer extends Servlet3HttpServerTracer {
// using request method as span name as server isn't ready for calling request.getServletPath()
// span name will be updated a bit later when calling request.getServletPath() works
// see LibertyUpdateSpanAdvice
Context context = startSpan(request, request, request, "HTTP " + request.getMethod());
String contextPath = request.getContextPath();
if (contextPath != null && !contextPath.isEmpty() && !contextPath.equals("/")) {
context = context.with(ServletContextPath.CONTEXT_KEY, contextPath);
}
return context;
return startSpan(request, request, request, "HTTP " + request.getMethod());
}
@Override

View File

@ -8,6 +8,7 @@ apply from: "$rootDir/gradle/instrumentation.gradle"
dependencies {
testInstrumentation project(':instrumentation:servlet:servlet-3.0:javaagent')
testInstrumentation project(':instrumentation:servlet:servlet-common:javaagent')
testInstrumentation project(':instrumentation:grizzly-2.0:javaagent')
testLibrary group: 'org.glassfish.main.extras', name: 'glassfish-embedded-all', version: '4.0'

View File

@ -3,6 +3,11 @@
* SPDX-License-Identifier: Apache-2.0
*/
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.ERROR
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.NOT_FOUND
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.REDIRECT
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
import io.opentelemetry.instrumentation.test.base.HttpServerTest
import org.glassfish.embeddable.BootstrapProperties
import org.glassfish.embeddable.Deployer
@ -63,4 +68,22 @@ class GlassFishServerTest extends HttpServerTest<GlassFish> {
boolean redirectHasBody() {
true
}
@Override
boolean hasResponseSpan(ServerEndpoint endpoint) {
endpoint == REDIRECT || endpoint == ERROR || endpoint == NOT_FOUND
}
@Override
void responseSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) {
switch (endpoint) {
case REDIRECT:
redirectSpan(trace, index, parent)
break
case ERROR:
case NOT_FOUND:
sendErrorSpan(trace, index, parent)
break
}
}
}

View File

@ -19,6 +19,8 @@ dependencies {
compileOnly group: 'javax.servlet', name: 'servlet-api', version: '2.2'
api(project(':instrumentation-core:servlet-2.2'))
testInstrumentation project(':instrumentation:servlet:servlet-common:javaagent')
testImplementation(project(':testing-common')) {
exclude group: 'org.eclipse.jetty', module: 'jetty-server'
}

View File

@ -37,7 +37,11 @@ public class Servlet2Advice {
Context serverContext = tracer().getServerContext(httpServletRequest);
if (serverContext != null) {
tracer().updateServerSpanNameOnce(serverContext, httpServletRequest);
Context updatedContext = tracer().runOnceUnderAppServer(serverContext, httpServletRequest);
if (updatedContext != serverContext) {
// runOnceUnderAppServer updated context, need to re-scope
scope = updatedContext.makeCurrent();
}
return;
}

View File

@ -70,10 +70,15 @@ class JettyServlet2Test extends HttpServerTest<Server> {
false
}
@Override
boolean hasResponseSpan(ServerEndpoint endpoint) {
endpoint == REDIRECT || endpoint == ERROR
}
@Override
void responseSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
trace.span(index) {
name endpoint == REDIRECT ? "HttpServletResponse.sendRedirect" : "HttpServletResponse.sendError"
name endpoint == REDIRECT ? "Response.sendRedirect" : "Response.sendError"
kind INTERNAL
errored false
childOf((SpanData) parent)

View File

@ -19,6 +19,7 @@ dependencies {
api(project(':instrumentation-core:servlet-2.2'))
testInstrumentation project(':instrumentation:jetty-8.0:javaagent')
testInstrumentation project(':instrumentation:servlet:servlet-common:javaagent')
testImplementation(project(':testing-common')) {
exclude group: 'org.eclipse.jetty', module: 'jetty-server'

View File

@ -36,19 +36,35 @@ public class Servlet3Advice {
Context attachedContext = tracer().getServerContext(httpServletRequest);
if (attachedContext != null) {
// We are inside nested servlet/filter/app-server span, don't create new span
if (Servlet3HttpServerTracer.needsRescoping(attachedContext)) {
attachedContext = tracer().runOnceUnderAppServer(attachedContext, httpServletRequest);
scope = attachedContext.makeCurrent();
return;
}
tracer().updateServerSpanNameOnce(attachedContext, httpServletRequest);
// We are inside nested servlet/filter/app-server span, don't create new span
// We already have attached context to request but this could have been done by app server
// instrumentation, if needed update span with info from current request.
Context currentContext = Java8BytecodeBridge.currentContext();
Context updatedContext = tracer().runOnceUnderAppServer(currentContext, httpServletRequest);
if (updatedContext != currentContext) {
// runOnceUnderAppServer updated context, need to re-scope
scope = updatedContext.makeCurrent();
}
return;
}
Context parentContext = Java8BytecodeBridge.currentContext();
if (parentContext != null && Java8BytecodeBridge.spanFromContext(parentContext).isRecording()) {
tracer().updateServerSpanNameOnce(parentContext, httpServletRequest);
// We are inside nested servlet/filter/app-server span, don't create new span
Context currentContext = Java8BytecodeBridge.currentContext();
if (currentContext != null
&& Java8BytecodeBridge.spanFromContext(currentContext).isRecording()) {
// We already have a span but it was not created by servlet instrumentation.
// In case it was created by app server integration we need to update it with info from
// current request.
Context updatedContext = tracer().runOnceUnderAppServer(currentContext, httpServletRequest);
if (currentContext != updatedContext) {
// runOnceUnderAppServer updated context, need to re-scope
scope = updatedContext.makeCurrent();
}
return;
}

View File

@ -10,6 +10,7 @@ import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEn
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.REDIRECT
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.SUCCESS
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
import io.opentelemetry.instrumentation.test.base.HttpServerTest
import javax.servlet.Servlet
import okhttp3.Request
@ -49,4 +50,25 @@ abstract class AbstractServlet3Test<SERVER, CONTEXT> extends HttpServerTest<SERV
lastRequest = uri
super.request(uri, method, body)
}
boolean errorEndpointUsesSendError() {
true
}
@Override
boolean hasResponseSpan(ServerEndpoint endpoint) {
endpoint == REDIRECT || (endpoint == ERROR && errorEndpointUsesSendError())
}
@Override
void responseSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) {
switch (endpoint) {
case REDIRECT:
redirectSpan(trace, index, parent)
break
case ERROR:
sendErrorSpan(trace, index, parent)
break
}
}
}

View File

@ -108,6 +108,11 @@ class JettyServlet3TestAsync extends JettyServlet3Test {
TestServlet3.Async
}
@Override
boolean errorEndpointUsesSendError() {
false
}
@Override
boolean testException() {
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/807
@ -135,6 +140,11 @@ class JettyServlet3TestForward extends JettyDispatchTest {
TestServlet3.Sync // dispatch to sync servlet
}
@Override
boolean hasForwardSpan() {
true
}
@Override
protected void setupServlets(ServletContextHandler context) {
super.setupServlets(context)
@ -164,6 +174,11 @@ class JettyServlet3TestInclude extends JettyDispatchTest {
false
}
@Override
boolean hasIncludeSpan() {
true
}
@Override
protected void setupServlets(ServletContextHandler context) {
super.setupServlets(context)
@ -223,6 +238,11 @@ class JettyServlet3TestDispatchAsync extends JettyDispatchTest {
addServlet(context, "/dispatch/recursive", TestServlet3.DispatchRecursive)
}
@Override
boolean errorEndpointUsesSendError() {
false
}
@Override
boolean testException() {
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/807

View File

@ -6,11 +6,13 @@
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.AUTH_REQUIRED
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.ERROR
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.NOT_FOUND
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.QUERY_PARAM
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.REDIRECT
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.SUCCESS
import static org.junit.Assume.assumeTrue
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
import java.nio.file.Files
import javax.servlet.Servlet
import javax.servlet.ServletException
@ -40,6 +42,21 @@ abstract class TomcatServlet3Test extends AbstractServlet3Test<Tomcat, Context>
ServletException
}
@Override
boolean hasResponseSpan(ServerEndpoint endpoint) {
endpoint == NOT_FOUND || super.hasResponseSpan(endpoint)
}
@Override
void responseSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) {
switch (endpoint) {
case NOT_FOUND:
sendErrorSpan(trace, index, parent)
break
}
super.responseSpan(trace, index, parent, method, endpoint)
}
@Shared
def accessLogValue = new TestAccessLogValve()
@ -122,10 +139,26 @@ abstract class TomcatServlet3Test extends AbstractServlet3Test<Tomcat, Context>
def loggedTraces = accessLogValue.loggedIds*.first
def loggedSpans = accessLogValue.loggedIds*.second
def expectedCount = 2
if (hasIncludeSpan()) {
expectedCount++
}
if (hasForwardSpan()) {
expectedCount++
}
(0..count - 1).each {
trace(it, 2) {
trace(it, expectedCount) {
serverSpan(it, 0, null, null, "GET", SUCCESS.body.length())
controllerSpan(it, 1, span(0))
def controllerIndex = 1
if (hasIncludeSpan()) {
includeSpan(it, 1, span(0))
controllerIndex++
}
if (hasForwardSpan()) {
forwardSpan(it, 1, span(0))
controllerIndex++
}
controllerSpan(it, controllerIndex, span(controllerIndex - 1))
}
assert loggedTraces.contains(traces[it][0].traceId)
@ -150,10 +183,27 @@ abstract class TomcatServlet3Test extends AbstractServlet3Test<Tomcat, Context>
response.body().string() == ERROR.body
and:
def spanCount = 2
if (errorEndpointUsesSendError()) {
spanCount++
}
if (hasForwardSpan()) {
spanCount++
}
assertTraces(1) {
trace(0, 2) {
trace(0, spanCount) {
serverSpan(it, 0, null, null, method, response.body().contentLength(), ERROR)
controllerSpan(it, 1, span(0))
def spanIndex = 1
if (hasForwardSpan()) {
forwardSpan(it, spanIndex, span(spanIndex - 1))
spanIndex++
}
controllerSpan(it, spanIndex, span(spanIndex - 1))
spanIndex++
if (errorEndpointUsesSendError()) {
sendErrorSpan(it, spanIndex, span(spanIndex - 1))
spanIndex++
}
}
def (String traceId, String spanId) = accessLogValue.loggedIds[0]
@ -256,6 +306,11 @@ class TomcatServlet3TestAsync extends TomcatServlet3Test {
TestServlet3.Async
}
@Override
boolean errorEndpointUsesSendError() {
false
}
@Override
boolean testException() {
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/807
@ -288,6 +343,11 @@ class TomcatServlet3TestForward extends TomcatDispatchTest {
false
}
@Override
boolean hasForwardSpan() {
true
}
@Override
protected void setupServlets(Context context) {
super.setupServlets(context)
@ -322,6 +382,11 @@ class TomcatServlet3TestInclude extends TomcatDispatchTest {
false
}
@Override
boolean hasIncludeSpan() {
true
}
@Override
protected void setupServlets(Context context) {
super.setupServlets(context)
@ -379,6 +444,11 @@ class TomcatServlet3TestDispatchAsync extends TomcatDispatchTest {
addServlet(context, "/dispatch/recursive", TestServlet3.DispatchRecursive)
}
@Override
boolean errorEndpointUsesSendError() {
false
}
@Override
boolean testException() {
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/807

View File

@ -33,6 +33,8 @@ dependencies {
// Include servlet instrumentation for verifying the tomcat requests
testInstrumentation project(':instrumentation:servlet:servlet-3.0:javaagent')
testInstrumentation project(':instrumentation:servlet:servlet-common:javaagent')
testInstrumentation project(':instrumentation:tomcat-7.0:javaagent')
testImplementation group: 'javax.validation', name: 'validation-api', version: '1.1.0.Final'
testImplementation group: 'org.hibernate', name: 'hibernate-validator', version: '5.4.2.Final'

View File

@ -58,6 +58,11 @@ class SpringBootBasedTest extends HttpServerTest<ConfigurableApplicationContext>
endpoint == REDIRECT
}
@Override
boolean hasResponseSpan(ServerEndpoint endpoint) {
endpoint == REDIRECT
}
@Override
boolean testNotFound() {
// FIXME: the instrumentation adds an extra controller span which is not consistent.
@ -84,9 +89,11 @@ class SpringBootBasedTest extends HttpServerTest<ConfigurableApplicationContext>
and:
assertTraces(1) {
trace(0, 2) {
trace(0, 4) {
serverSpan(it, 0, null, null, "GET", null, AUTH_ERROR)
errorPageSpans(it, 1, null)
sendErrorSpan(it, 1, span(0))
forwardSpan(it, 2, span(0))
errorPageSpans(it, 3, null)
}
}
}
@ -111,8 +118,9 @@ class SpringBootBasedTest extends HttpServerTest<ConfigurableApplicationContext>
and:
assertTraces(1) {
trace(0, 1) {
trace(0, 2) {
serverSpan(it, 0, null, null, "POST", response.body()?.contentLength(), LOGIN)
redirectSpan(it, 1, span(0))
}
}
@ -134,7 +142,7 @@ class SpringBootBasedTest extends HttpServerTest<ConfigurableApplicationContext>
@Override
void responseSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
trace.span(index) {
name "HttpServletResponse.sendRedirect"
name "OnCommittedResponseWrapper.sendRedirect"
kind INTERNAL
errored false
attributes {
@ -173,7 +181,13 @@ class SpringBootBasedTest extends HttpServerTest<ConfigurableApplicationContext>
void serverSpan(TraceAssert trace, int index, String traceID = null, String parentID = null, String method = "GET", Long responseContentLength = null, ServerEndpoint endpoint = SUCCESS) {
trace.span(index) {
name endpoint == PATH_PARAM ? getContextPath() + "/path/{id}/param" : endpoint.resolvePath(address).path
if (endpoint == PATH_PARAM) {
name getContextPath() + "/path/{id}/param"
} else if (endpoint == AUTH_ERROR) {
name "/error"
} else {
name endpoint.resolvePath(address).path
}
kind SERVER
errored endpoint.errored
if (parentID != null) {
@ -186,7 +200,7 @@ class SpringBootBasedTest extends HttpServerTest<ConfigurableApplicationContext>
errorEvent(Exception, EXCEPTION.body)
}
attributes {
"${SemanticAttributes.NET_PEER_IP.key}" { it == null || it == "127.0.0.1" } // Optional
"${SemanticAttributes.NET_PEER_IP.key}" "127.0.0.1"
"${SemanticAttributes.NET_PEER_PORT.key}" Long
"${SemanticAttributes.HTTP_URL.key}" { it == "${endpoint.resolve(address)}" || it == "${endpoint.resolveWithoutFragment(address)}" }
"${SemanticAttributes.HTTP_METHOD.key}" method

View File

@ -9,6 +9,7 @@ import static io.opentelemetry.api.trace.SpanKind.INTERNAL
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.ERROR
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.PATH_PARAM
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.REDIRECT
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.SUCCESS
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
@ -43,6 +44,28 @@ class ServletFilterTest extends HttpServerTest<ConfigurableApplicationContext> {
endpoint == ERROR || endpoint == EXCEPTION
}
@Override
int getErrorPageSpansCount(ServerEndpoint endpoint) {
2
}
@Override
boolean hasResponseSpan(ServerEndpoint endpoint) {
endpoint == REDIRECT || endpoint == ERROR
}
@Override
void responseSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) {
switch (endpoint) {
case REDIRECT:
redirectSpan(trace, index, parent)
break
case ERROR:
sendErrorSpan(trace, index, parent)
break
}
}
@Override
boolean testPathParam() {
true
@ -75,18 +98,31 @@ class ServletFilterTest extends HttpServerTest<ConfigurableApplicationContext> {
@Override
String expectedServerSpanName(ServerEndpoint endpoint) {
return endpoint == PATH_PARAM ? "/path/{id}/param" : endpoint.resolvePath(address).path
if (endpoint == PATH_PARAM) {
return "/path/{id}/param"
} else if (endpoint == ERROR || endpoint == EXCEPTION) {
return "/error"
}
return endpoint.resolvePath(address).path
}
@Override
void errorPageSpans(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
trace.span(index) {
name "BasicErrorController.error"
name "ApplicationDispatcher.forward"
kind INTERNAL
errored false
childOf((SpanData) parent)
attributes {
}
}
trace.span(index + 1) {
name "BasicErrorController.error"
kind INTERNAL
errored false
childOf(trace.span(index))
attributes {
}
}
}
}

View File

@ -3,8 +3,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.ERROR
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.PATH_PARAM
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.REDIRECT
import io.opentelemetry.api.trace.SpanKind
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
@ -14,6 +16,7 @@ import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import javax.servlet.DispatcherType
import org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.handler.HandlerCollection
import org.eclipse.jetty.servlet.DefaultServlet
import org.eclipse.jetty.servlet.ServletContextHandler
import org.eclipse.jetty.util.resource.FileResource
@ -45,6 +48,24 @@ class Struts2ActionSpanTest extends HttpServerTest<Server> {
return true
}
@Override
boolean hasResponseSpan(ServerEndpoint endpoint) {
endpoint == REDIRECT || endpoint == ERROR || endpoint == EXCEPTION
}
@Override
void responseSpan(TraceAssert trace, int index, Object controllerSpan, Object handlerSpan, String method, ServerEndpoint endpoint) {
switch (endpoint) {
case REDIRECT:
redirectSpan(trace, index, handlerSpan)
break
case ERROR:
case EXCEPTION:
sendErrorSpan(trace, index, handlerSpan)
break
}
}
String expectedServerSpanName(ServerEndpoint endpoint) {
return endpoint == PATH_PARAM ? getContextPath() + "/path/{id}/param" : endpoint.resolvePath(address).path
}
@ -79,7 +100,11 @@ class Struts2ActionSpanTest extends HttpServerTest<Server> {
context.setContextPath(getContextPath())
def resource = new FileResource(getClass().getResource("/"))
context.setBaseResource(resource)
server.setHandler(context)
// jetty integration is disabled for some handler classes, using HandlerCollection here
// enables jetty integration
HandlerCollection handlerCollection = new HandlerCollection()
handlerCollection.addHandler(context)
server.setHandler(handlerCollection)
context.addServlet(DefaultServlet, "/")
context.addFilter(StrutsPrepareAndExecuteFilter, "/*", EnumSet.of(DispatcherType.REQUEST))

View File

@ -24,6 +24,7 @@ dependencies {
testRuntimeOnly group: 'javax.servlet', name: 'jsp-api', version: '2.0'
testInstrumentation project(":instrumentation:servlet:servlet-3.0:javaagent")
testInstrumentation project(':instrumentation:servlet:servlet-common:javaagent')
testInstrumentation project(':instrumentation:jetty-8.0:javaagent')
}

View File

@ -12,6 +12,7 @@ import io.opentelemetry.instrumentation.api.tracer.HttpServerTracer;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Collections;
import org.apache.coyote.ActionCode;
import org.apache.coyote.Request;
import org.apache.coyote.Response;
import org.apache.tomcat.util.buf.MessageBytes;
@ -51,12 +52,14 @@ public class TomcatTracer extends HttpServerTracer<Request, Response, Request, R
@Override
protected Integer peerPort(Request connection) {
connection.action(ActionCode.REQ_REMOTEPORT_ATTRIBUTE, connection);
return connection.getRemotePort();
}
@Override
protected String peerHostIP(Request connection) {
return connection.remoteHost().getString();
connection.action(ActionCode.REQ_HOST_ADDR_ATTRIBUTE, connection);
return connection.remoteAddr().toString();
}
@Override

View File

@ -19,21 +19,22 @@ public class TestServlet extends HttpServlet {
HttpServerTest.ServerEndpoint serverEndpoint = HttpServerTest.ServerEndpoint.forPath(path);
if (serverEndpoint != null) {
if (serverEndpoint == HttpServerTest.ServerEndpoint.EXCEPTION) {
HttpServerTest.controller(
serverEndpoint,
() -> {
HttpServerTest.controller(
serverEndpoint,
() -> {
if (serverEndpoint == HttpServerTest.ServerEndpoint.EXCEPTION) {
throw new Exception(serverEndpoint.getBody());
});
} else {
resp.getWriter().print(HttpServerTest.controller(serverEndpoint, serverEndpoint::getBody));
}
if (serverEndpoint == HttpServerTest.ServerEndpoint.REDIRECT) {
resp.sendRedirect(serverEndpoint.getBody());
} else {
resp.setStatus(serverEndpoint.getStatus());
}
}
resp.getWriter().print(serverEndpoint.getBody());
if (serverEndpoint == HttpServerTest.ServerEndpoint.REDIRECT) {
resp.sendRedirect(serverEndpoint.getBody());
} else if (serverEndpoint == HttpServerTest.ServerEndpoint.ERROR) {
resp.sendError(serverEndpoint.getStatus());
} else {
resp.setStatus(serverEndpoint.getStatus());
}
return null;
});
} else if ("/errorPage".equals(path)) {
resp.getWriter().print(HttpServerTest.ServerEndpoint.EXCEPTION.getBody());
resp.setStatus(500);

View File

@ -5,7 +5,15 @@
package io.opentelemetry.javaagent.instrumentation.tomcat7
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.ERROR
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.REDIRECT
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.SUCCESS
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
import io.opentelemetry.instrumentation.test.base.HttpServerTest
import io.opentelemetry.sdk.trace.data.SpanData
import org.apache.catalina.Context
import org.apache.catalina.startup.Tomcat
import org.apache.tomcat.util.descriptor.web.ErrorPage
@ -53,4 +61,42 @@ class TomcatHandlerTest extends HttpServerTest<Tomcat> {
tomcat.getServer().stop()
}
@Override
boolean testExceptionBody() {
false
}
@Override
boolean hasErrorPageSpans(ServerEndpoint endpoint) {
endpoint == ERROR || endpoint == EXCEPTION
}
@Override
void errorPageSpans(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
trace.span(index) {
name "ApplicationDispatcher.forward"
kind INTERNAL
errored false
childOf((SpanData) parent)
attributes {
}
}
}
@Override
boolean hasResponseSpan(ServerEndpoint endpoint) {
endpoint == REDIRECT || endpoint == ERROR
}
@Override
void responseSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) {
switch (endpoint) {
case REDIRECT:
redirectSpan(trace, index, parent)
break
case ERROR:
sendErrorSpan(trace, index, parent)
break
}
}
}

View File

@ -13,6 +13,7 @@ muzzle {
dependencies {
library group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '7.0.4'
implementation project(':instrumentation:servlet:servlet-3.0:javaagent')
testInstrumentation project(':instrumentation:servlet:servlet-common:javaagent')
// Tests need at least version 9 to have necessary classes to configure the embedded tomcat...
// ... but not newer that version 10, because its servlet 5.

View File

@ -18,4 +18,5 @@ dependencies {
testImplementation group: 'org.eclipse.jetty', name: 'jetty-servlet', version: '8.0.0.v20110901'
testInstrumentation project(":instrumentation:servlet:servlet-3.0:javaagent")
testInstrumentation project(":instrumentation:servlet:servlet-common:javaagent")
}

View File

@ -51,6 +51,18 @@ abstract class HttpServerTest<SERVER> extends AgentInstrumentationSpecification
false
}
boolean hasForwardSpan() {
false
}
boolean hasIncludeSpan() {
false
}
int getErrorPageSpansCount(ServerEndpoint endpoint) {
1
}
boolean hasErrorPageSpans(ServerEndpoint endpoint) {
false
}
@ -341,16 +353,22 @@ abstract class HttpServerTest<SERVER> extends AgentInstrumentationSpecification
if (hasHandlerSpan()) {
spanCount++
}
if (hasResponseSpan(endpoint)) {
spanCount++
}
if (endpoint != NOT_FOUND) {
spanCount++ // controller span
if (hasRenderSpan(endpoint)) {
spanCount++
}
if (hasResponseSpan(endpoint)) {
if (hasForwardSpan()) {
spanCount++
}
if (hasIncludeSpan()) {
spanCount++
}
if (hasErrorPageSpans(endpoint)) {
spanCount++
spanCount += getErrorPageSpansCount(endpoint)
}
}
assertTraces(size) {
@ -362,21 +380,31 @@ abstract class HttpServerTest<SERVER> extends AgentInstrumentationSpecification
handlerSpan(it, spanIndex++, span(0), method, endpoint)
}
if (endpoint != NOT_FOUND) {
def controllerSpanIndex = 0
if (hasHandlerSpan()) {
controllerSpan(it, spanIndex++, span(1), errorMessage, expectedExceptionClass())
} else {
controllerSpan(it, spanIndex++, span(0), errorMessage, expectedExceptionClass())
controllerSpanIndex++
}
if (hasForwardSpan()) {
forwardSpan(it, spanIndex++, span(0), errorMessage, expectedExceptionClass())
controllerSpanIndex++
}
if (hasIncludeSpan()) {
includeSpan(it, spanIndex++, span(0), errorMessage, expectedExceptionClass())
controllerSpanIndex++
}
controllerSpan(it, spanIndex++, span(controllerSpanIndex), errorMessage, expectedExceptionClass())
if (hasRenderSpan(endpoint)) {
renderSpan(it, spanIndex++, span(0), method, endpoint)
}
if (hasResponseSpan(endpoint)) {
responseSpan(it, spanIndex, span(spanIndex - 1), method, endpoint)
responseSpan(it, spanIndex, span(spanIndex - 1), span(0), method, endpoint)
spanIndex++
}
if (hasErrorPageSpans(endpoint)) {
errorPageSpans(it, spanIndex, span(0), method, endpoint)
}
} else if (hasResponseSpan(endpoint)) {
responseSpan(it, 1, span(0), span(0), method, endpoint)
}
}
}
@ -402,6 +430,10 @@ abstract class HttpServerTest<SERVER> extends AgentInstrumentationSpecification
throw new UnsupportedOperationException("renderSpan not implemented in " + getClass().name)
}
void responseSpan(TraceAssert trace, int index, Object controllerSpan, Object handlerSpan, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
responseSpan(trace, index, controllerSpan, method, endpoint)
}
void responseSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
throw new UnsupportedOperationException("responseSpan not implemented in " + getClass().name)
}
@ -410,6 +442,46 @@ abstract class HttpServerTest<SERVER> extends AgentInstrumentationSpecification
throw new UnsupportedOperationException("errorPageSpans not implemented in " + getClass().name)
}
void redirectSpan(TraceAssert trace, int index, Object parent) {
trace.span(index) {
name ~/\.sendRedirect$/
kind SpanKind.INTERNAL
childOf((SpanData) parent)
}
}
void sendErrorSpan(TraceAssert trace, int index, Object parent) {
trace.span(index) {
name ~/\.sendError$/
kind SpanKind.INTERNAL
childOf((SpanData) parent)
}
}
void forwardSpan(TraceAssert trace, int index, Object parent, String errorMessage = null, Class exceptionClass = Exception) {
trace.span(index) {
name ~/\.forward$/
kind SpanKind.INTERNAL
errored errorMessage != null
if (errorMessage) {
errorEvent(exceptionClass, errorMessage)
}
childOf((SpanData) parent)
}
}
void includeSpan(TraceAssert trace, int index, Object parent, String errorMessage = null, Class exceptionClass = Exception) {
trace.span(index) {
name ~/\.include$/
kind SpanKind.INTERNAL
errored errorMessage != null
if (errorMessage) {
errorEvent(exceptionClass, errorMessage)
}
childOf((SpanData) parent)
}
}
// parent span must be cast otherwise it breaks debugging classloading (junit loads it early)
void serverSpan(TraceAssert trace, int index, String traceID = null, String parentID = null, String method = "GET", Long responseContentLength = null, ServerEndpoint endpoint = SUCCESS) {
trace.span(index) {