Migrate spring-web to base httpserver test
Unfortunately 404 test was not consistent and non-trivial to adapt. I also couldn’t get the validation tests to work well either. Revisit if we have time to dig deeper into spring.
This commit is contained in:
parent
e895d7c3a7
commit
407c973029
|
@ -43,7 +43,7 @@ public class TestServlets {
|
|||
}
|
||||
}
|
||||
|
||||
@WebServlet("/error")
|
||||
@WebServlet("/error-status")
|
||||
public static class Error extends HttpServlet {
|
||||
@Override
|
||||
protected void service(final HttpServletRequest req, final HttpServletResponse resp) {
|
||||
|
|
|
@ -55,7 +55,7 @@ class GrizzlyAsyncTest extends GrizzlyTest {
|
|||
}
|
||||
|
||||
@GET
|
||||
@Path("error")
|
||||
@Path("error-status")
|
||||
void error(@Suspended AsyncResponse ar) {
|
||||
executor.execute {
|
||||
controller(ERROR) {
|
||||
|
|
|
@ -74,7 +74,7 @@ class GrizzlyTest extends HttpServerTest<HttpServer, GrizzlyDecorator> {
|
|||
}
|
||||
|
||||
@GET
|
||||
@Path("error")
|
||||
@Path("error-status")
|
||||
Response error() {
|
||||
controller(ERROR) {
|
||||
Response.status(ERROR.status).entity(ERROR.body).build()
|
||||
|
|
|
@ -29,5 +29,4 @@ dependencies {
|
|||
|
||||
testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '1.5.17.RELEASE'
|
||||
testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '1.5.17.RELEASE'
|
||||
testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: '1.5.17.RELEASE'
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package test
|
||||
|
||||
import org.apache.catalina.connector.Connector
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory
|
||||
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer
|
||||
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException
|
||||
import org.springframework.web.accept.ContentNegotiationStrategy
|
||||
import org.springframework.web.context.request.NativeWebRequest
|
||||
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
|
||||
|
||||
@SpringBootApplication
|
||||
class AppConfig extends WebMvcConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
|
||||
configurer.favorPathExtension(false)
|
||||
.favorParameter(true)
|
||||
.ignoreAcceptHeader(true)
|
||||
.useJaf(false)
|
||||
.defaultContentTypeStrategy(new ContentNegotiationStrategy() {
|
||||
@Override
|
||||
List<MediaType> resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException {
|
||||
return [MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Bean
|
||||
EmbeddedServletContainerFactory servletContainerFactory() {
|
||||
def factory = new TomcatEmbeddedServletContainerFactory()
|
||||
|
||||
factory.addConnectorCustomizers(
|
||||
new TomcatConnectorCustomizer() {
|
||||
@Override
|
||||
void customize(final Connector connector) {
|
||||
connector.setEnableLookups(true)
|
||||
}
|
||||
})
|
||||
|
||||
return factory
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package test;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import datadog.trace.agent.test.base.HttpServerTestAdvice;
|
||||
import datadog.trace.agent.tooling.Instrumenter;
|
||||
import net.bytebuddy.agent.builder.AgentBuilder;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public class ServletTestInstrumentation implements Instrumenter {
|
||||
|
||||
@Override
|
||||
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
|
||||
return agentBuilder
|
||||
// Tomcat
|
||||
.type(named("org.apache.catalina.connector.CoyoteAdapter"))
|
||||
.transform(
|
||||
new AgentBuilder.Transformer.ForAdvice()
|
||||
.advice(named("service"), HttpServerTestAdvice.ServerEntryAdvice.class.getName()));
|
||||
}
|
||||
}
|
|
@ -1,394 +1,146 @@
|
|||
package test
|
||||
|
||||
import datadog.trace.agent.test.AgentTestRunner
|
||||
import datadog.opentracing.DDSpan
|
||||
import datadog.trace.agent.test.asserts.ListWriterAssert
|
||||
import datadog.trace.agent.test.asserts.SpanAssert
|
||||
import datadog.trace.agent.test.asserts.TraceAssert
|
||||
import datadog.trace.agent.test.base.HttpServerTest
|
||||
import datadog.trace.api.DDSpanTypes
|
||||
import datadog.trace.api.DDTags
|
||||
import datadog.trace.instrumentation.servlet3.Servlet3Decorator
|
||||
import datadog.trace.instrumentation.springweb.SpringWebHttpServerDecorator
|
||||
import groovy.transform.stc.ClosureParams
|
||||
import groovy.transform.stc.SimpleType
|
||||
import io.opentracing.tag.Tags
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.context.embedded.LocalServerPort
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException
|
||||
import org.apache.catalina.core.ApplicationFilterChain
|
||||
import org.springframework.boot.SpringApplication
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import org.springframework.web.servlet.view.RedirectView
|
||||
|
||||
import static test.Application.PASS
|
||||
import static test.Application.USER
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.SUCCESS
|
||||
import static java.util.Collections.singletonMap
|
||||
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
class SpringBootBasedTest extends AgentTestRunner {
|
||||
class SpringBootBasedTest extends HttpServerTest<ConfigurableApplicationContext, Servlet3Decorator> {
|
||||
|
||||
@LocalServerPort
|
||||
private int port
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate
|
||||
|
||||
def "valid response"() {
|
||||
expect:
|
||||
port != 0
|
||||
restTemplate.withBasicAuth(USER, PASS)
|
||||
.getForObject("http://localhost:$port/", String) == "Hello World"
|
||||
|
||||
and:
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
operationName "servlet.request"
|
||||
resourceName "GET /"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
"http.url" "http://localhost:$port/"
|
||||
"http.method" "GET"
|
||||
"peer.hostname" "127.0.0.1"
|
||||
"peer.ipv4" "127.0.0.1"
|
||||
"peer.port" Integer
|
||||
"span.kind" "server"
|
||||
"span.origin.type" "org.apache.catalina.core.ApplicationFilterChain"
|
||||
"component" "java-web-servlet"
|
||||
"http.status_code" 200
|
||||
"$DDTags.USER_NAME" USER
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
controllerSpan(it, 1, "TestController.greeting")
|
||||
}
|
||||
}
|
||||
@Override
|
||||
ConfigurableApplicationContext startServer(int port) {
|
||||
def app = new SpringApplication(AppConfig)
|
||||
app.setDefaultProperties(singletonMap("server.port", port))
|
||||
def context = app.run()
|
||||
return context
|
||||
}
|
||||
|
||||
def "generates spans"() {
|
||||
setup:
|
||||
def entity = restTemplate.withBasicAuth(USER, PASS)
|
||||
.getForEntity("http://localhost:$port/param/$param/", String)
|
||||
|
||||
expect:
|
||||
entity.statusCode == status
|
||||
if (entity.hasBody()) {
|
||||
entity.body == "Hello asdf1234"
|
||||
}
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
operationName "servlet.request"
|
||||
resourceName(status.value == 404 ? "404" : "GET /param/{parameter}/")
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
"http.url" "http://localhost:$port/param/$param/"
|
||||
"http.method" "GET"
|
||||
"peer.hostname" "127.0.0.1"
|
||||
"peer.ipv4" "127.0.0.1"
|
||||
"peer.port" Integer
|
||||
"span.kind" "server"
|
||||
"span.origin.type" "org.apache.catalina.core.ApplicationFilterChain"
|
||||
"component" "java-web-servlet"
|
||||
"http.status_code" status.value
|
||||
"$DDTags.USER_NAME" USER
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
controllerSpan(it, 1, "TestController.withParam")
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
param | status
|
||||
"asdf1234" | HttpStatus.OK
|
||||
"missing" | HttpStatus.NOT_FOUND
|
||||
@Override
|
||||
void stopServer(ConfigurableApplicationContext ctx) {
|
||||
ctx.close()
|
||||
}
|
||||
|
||||
def "missing auth"() {
|
||||
setup:
|
||||
def resp = restTemplate.getForObject("http://localhost:$port/param/asdf1234/", Map)
|
||||
|
||||
expect:
|
||||
resp["status"] == 401
|
||||
resp["error"] == "Unauthorized"
|
||||
|
||||
assertTraces(2) {
|
||||
trace(0, 1) {
|
||||
span(0) {
|
||||
operationName "servlet.request"
|
||||
resourceName "GET /param/?/"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
"http.url" "http://localhost:$port/param/asdf1234/"
|
||||
"http.method" "GET"
|
||||
"peer.hostname" "127.0.0.1"
|
||||
"peer.ipv4" "127.0.0.1"
|
||||
"peer.port" Integer
|
||||
"span.kind" "server"
|
||||
"span.origin.type" "org.apache.catalina.core.ApplicationFilterChain"
|
||||
"component" "java-web-servlet"
|
||||
"http.status_code" 401
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
trace(1, 2) {
|
||||
span(0) {
|
||||
operationName "servlet.request"
|
||||
resourceName "GET /error"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
"http.url" "http://localhost:$port/error"
|
||||
"http.method" "GET"
|
||||
"peer.hostname" "127.0.0.1"
|
||||
"peer.ipv4" "127.0.0.1"
|
||||
"peer.port" Integer
|
||||
"span.kind" "server"
|
||||
"span.origin.type" "org.apache.catalina.core.ApplicationFilterChain"
|
||||
"component" "java-web-servlet"
|
||||
"http.status_code" 401
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
controllerSpan(it, 1, "BasicErrorController.error")
|
||||
}
|
||||
}
|
||||
@Override
|
||||
Servlet3Decorator decorator() {
|
||||
return Servlet3Decorator.DECORATE
|
||||
}
|
||||
|
||||
def "generates 404 spans"() {
|
||||
setup:
|
||||
def response = restTemplate.withBasicAuth(USER, PASS)
|
||||
.getForObject("http://localhost:$port/invalid", Map)
|
||||
|
||||
expect:
|
||||
response.get("status") == 404
|
||||
response.get("error") == "Not Found"
|
||||
|
||||
assertTraces(2) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
operationName "servlet.request"
|
||||
resourceName "404"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
"http.url" "http://localhost:$port/invalid"
|
||||
"http.method" "GET"
|
||||
"peer.hostname" "127.0.0.1"
|
||||
"peer.ipv4" "127.0.0.1"
|
||||
"peer.port" Integer
|
||||
"span.kind" "server"
|
||||
"span.origin.type" "org.apache.catalina.core.ApplicationFilterChain"
|
||||
"component" "java-web-servlet"
|
||||
"http.status_code" 404
|
||||
"$DDTags.USER_NAME" USER
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
controllerSpan(it, 1, "ResourceHttpRequestHandler.handleRequest")
|
||||
}
|
||||
trace(1, 2) {
|
||||
span(0) {
|
||||
operationName "servlet.request"
|
||||
resourceName "404"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
"http.url" "http://localhost:$port/error"
|
||||
"http.method" "GET"
|
||||
"peer.hostname" "127.0.0.1"
|
||||
"peer.ipv4" "127.0.0.1"
|
||||
"peer.port" Integer
|
||||
"span.kind" "server"
|
||||
"span.origin.type" "org.apache.catalina.core.ApplicationFilterChain"
|
||||
"component" "java-web-servlet"
|
||||
"http.status_code" 404
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
controllerSpan(it, 1, "BasicErrorController.error")
|
||||
}
|
||||
}
|
||||
@Override
|
||||
String expectedOperationName() {
|
||||
return "servlet.request"
|
||||
}
|
||||
|
||||
def "generates error spans"() {
|
||||
setup:
|
||||
def response = restTemplate.withBasicAuth(USER, PASS)
|
||||
.getForObject("http://localhost:$port/error/qwerty/", Map)
|
||||
|
||||
expect:
|
||||
response.get("status") == 500
|
||||
response.get("error") == "Internal Server Error"
|
||||
response.get("exception") == "java.lang.RuntimeException"
|
||||
response.get("message") == "qwerty"
|
||||
|
||||
assertTraces(2) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
operationName "servlet.request"
|
||||
resourceName "GET /error/{parameter}/"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored true
|
||||
tags {
|
||||
"http.url" "http://localhost:$port/error/qwerty/"
|
||||
"http.method" "GET"
|
||||
"peer.hostname" "127.0.0.1"
|
||||
"peer.ipv4" "127.0.0.1"
|
||||
"peer.port" Integer
|
||||
"span.kind" "server"
|
||||
"span.origin.type" "org.apache.catalina.core.ApplicationFilterChain"
|
||||
"component" "java-web-servlet"
|
||||
"http.status_code" 500
|
||||
"$DDTags.USER_NAME" USER
|
||||
errorTags RuntimeException, "qwerty"
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
controllerSpan(it, 1, "TestController.withError", RuntimeException)
|
||||
}
|
||||
trace(1, 2) {
|
||||
span(0) {
|
||||
operationName "servlet.request"
|
||||
resourceName "GET /error"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored true
|
||||
tags {
|
||||
"http.url" "http://localhost:$port/error"
|
||||
"http.method" "GET"
|
||||
"peer.hostname" "127.0.0.1"
|
||||
"peer.ipv4" "127.0.0.1"
|
||||
"peer.port" Integer
|
||||
"span.kind" "server"
|
||||
"span.origin.type" "org.apache.catalina.core.ApplicationFilterChain"
|
||||
"component" "java-web-servlet"
|
||||
"http.status_code" 500
|
||||
"error" true
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
controllerSpan(it, 1, "BasicErrorController.error")
|
||||
}
|
||||
}
|
||||
@Override
|
||||
boolean hasHandlerSpan() {
|
||||
true
|
||||
}
|
||||
|
||||
def "validated form"() {
|
||||
expect:
|
||||
restTemplate.withBasicAuth(USER, PASS)
|
||||
.postForObject("http://localhost:$port/validated", new TestForm("bob", 20), String) == "Hello bob Person(Name: bob, Age: 20)"
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
operationName "servlet.request"
|
||||
resourceName "POST /validated"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
"http.url" "http://localhost:$port/validated"
|
||||
"http.method" "POST"
|
||||
"peer.hostname" "127.0.0.1"
|
||||
"peer.ipv4" "127.0.0.1"
|
||||
"peer.port" Integer
|
||||
"span.kind" "server"
|
||||
"span.origin.type" "org.apache.catalina.core.ApplicationFilterChain"
|
||||
"component" "java-web-servlet"
|
||||
"http.status_code" 200
|
||||
"$DDTags.USER_NAME" USER
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
controllerSpan(it, 1, "TestController.withValidation")
|
||||
}
|
||||
}
|
||||
@Override
|
||||
boolean testNotFound() {
|
||||
// FIXME: the instrumentation adds an extra controller span which is not consistent.
|
||||
// Fix tests or remove extra span.
|
||||
false
|
||||
}
|
||||
|
||||
def "invalid form"() {
|
||||
setup:
|
||||
def response = restTemplate.withBasicAuth(USER, PASS)
|
||||
.postForObject("http://localhost:$port/validated", new TestForm("bill", 5), Map, Map)
|
||||
void cleanAndAssertTraces(
|
||||
final int size,
|
||||
@ClosureParams(value = SimpleType, options = "datadog.trace.agent.test.asserts.ListWriterAssert")
|
||||
@DelegatesTo(value = ListWriterAssert, strategy = Closure.DELEGATE_FIRST)
|
||||
final Closure spec) {
|
||||
|
||||
expect:
|
||||
response.get("status") == 400
|
||||
response.get("error") == "Bad Request"
|
||||
response.get("exception") == "org.springframework.web.bind.MethodArgumentNotValidException"
|
||||
response.get("message") == "Validation failed for object='testForm'. Error count: 1"
|
||||
// If this is failing, make sure HttpServerTestAdvice is applied correctly.
|
||||
TEST_WRITER.waitForTraces(size * 2)
|
||||
|
||||
assertTraces(2) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
operationName "servlet.request"
|
||||
resourceName "POST /validated"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
errored false
|
||||
tags {
|
||||
"http.url" "http://localhost:$port/validated"
|
||||
"http.method" "POST"
|
||||
"peer.hostname" "127.0.0.1"
|
||||
"peer.ipv4" "127.0.0.1"
|
||||
"peer.port" Integer
|
||||
"span.kind" "server"
|
||||
"span.origin.type" "org.apache.catalina.core.ApplicationFilterChain"
|
||||
"component" "java-web-servlet"
|
||||
"http.status_code" 400
|
||||
"$DDTags.USER_NAME" USER
|
||||
"error" false
|
||||
"error.msg" String
|
||||
"error.type" MethodArgumentNotValidException.name
|
||||
"error.stack" String
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
controllerSpan(it, 1, "TestController.withValidation", MethodArgumentNotValidException)
|
||||
TEST_WRITER.each {
|
||||
def renderSpan = it.find {
|
||||
it.operationName == "response.render"
|
||||
}
|
||||
trace(1, 2) {
|
||||
span(0) {
|
||||
operationName "servlet.request"
|
||||
resourceName "POST /error"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
parent()
|
||||
if (renderSpan) {
|
||||
SpanAssert.assertSpan(renderSpan) {
|
||||
operationName "response.render"
|
||||
resourceName "response.render"
|
||||
spanType "web"
|
||||
errored false
|
||||
tags {
|
||||
"http.url" "http://localhost:$port/error"
|
||||
"http.method" "POST"
|
||||
"peer.hostname" "127.0.0.1"
|
||||
"peer.ipv4" "127.0.0.1"
|
||||
"peer.port" Integer
|
||||
"component" "spring-webmvc"
|
||||
"view.type" RedirectView.name
|
||||
"span.kind" "server"
|
||||
"span.origin.type" "org.apache.catalina.core.ApplicationFilterChain"
|
||||
"component" "java-web-servlet"
|
||||
"http.status_code" 400
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
controllerSpan(it, 1, "BasicErrorController.error")
|
||||
it.remove(renderSpan)
|
||||
}
|
||||
}
|
||||
|
||||
super.cleanAndAssertTraces(size, spec)
|
||||
}
|
||||
|
||||
def controllerSpan(TraceAssert trace, int index, String name, Class<Throwable> errorType = null) {
|
||||
@Override
|
||||
void handlerSpan(TraceAssert trace, int index, Object parent, ServerEndpoint endpoint = SUCCESS) {
|
||||
trace.span(index) {
|
||||
serviceName "unnamed-java-app"
|
||||
operationName name
|
||||
resourceName name
|
||||
serviceName expectedServiceName()
|
||||
operationName "TestController.${endpoint.name().toLowerCase()}"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
childOf(trace.span(0))
|
||||
errored errorType != null
|
||||
errored endpoint == EXCEPTION
|
||||
childOf(parent as DDSpan)
|
||||
tags {
|
||||
"$Tags.COMPONENT.key" "spring-web-controller"
|
||||
"$Tags.COMPONENT.key" SpringWebHttpServerDecorator.DECORATE.component()
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
if (errorType) {
|
||||
"error.msg" String
|
||||
errorTags(errorType)
|
||||
}
|
||||
defaultTags()
|
||||
if (endpoint == EXCEPTION) {
|
||||
errorTags(Exception, EXCEPTION.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void serverSpan(TraceAssert trace, int index, String traceID = null, String parentID = null, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
|
||||
trace.span(index) {
|
||||
serviceName expectedServiceName()
|
||||
operationName expectedOperationName()
|
||||
resourceName endpoint.status == 404 ? "404" : "$method ${endpoint.resolve(address).path}"
|
||||
spanType DDSpanTypes.HTTP_SERVER
|
||||
errored endpoint.errored
|
||||
if (parentID != null) {
|
||||
traceId traceID
|
||||
parentId parentID
|
||||
} else {
|
||||
parent()
|
||||
}
|
||||
tags {
|
||||
"span.origin.type" ApplicationFilterChain.name
|
||||
|
||||
defaultTags(true)
|
||||
"$Tags.COMPONENT.key" serverDecorator.component()
|
||||
if (endpoint.errored) {
|
||||
"$Tags.ERROR.key" endpoint.errored
|
||||
"error.msg" { it == null || it == EXCEPTION.body }
|
||||
"error.type" { it == null || it == Exception.name }
|
||||
"error.stack" { it == null || it instanceof String }
|
||||
}
|
||||
"$Tags.HTTP_STATUS.key" endpoint.status
|
||||
"$Tags.HTTP_URL.key" "${endpoint.resolve(address)}"
|
||||
"$Tags.PEER_HOSTNAME.key" { it == "localhost" || it == "127.0.0.1" }
|
||||
"$Tags.PEER_PORT.key" Integer
|
||||
"$Tags.PEER_HOST_IPV4.key" { it == null || it == "127.0.0.1" } // Optional
|
||||
"$Tags.HTTP_METHOD.key" method
|
||||
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_SERVER
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package test
|
||||
|
||||
import datadog.trace.agent.test.base.HttpServerTest
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.stereotype.Controller
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.ResponseBody
|
||||
import org.springframework.web.servlet.view.RedirectView
|
||||
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.ERROR
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.REDIRECT
|
||||
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.SUCCESS
|
||||
|
||||
@Controller
|
||||
class TestController {
|
||||
|
||||
@RequestMapping("/success")
|
||||
@ResponseBody
|
||||
String success() {
|
||||
HttpServerTest.controller(SUCCESS) {
|
||||
SUCCESS.body
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/redirect")
|
||||
@ResponseBody
|
||||
RedirectView redirect() {
|
||||
HttpServerTest.controller(REDIRECT) {
|
||||
new RedirectView(REDIRECT.body)
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/error-status")
|
||||
ResponseEntity error() {
|
||||
HttpServerTest.controller(ERROR) {
|
||||
new ResponseEntity(ERROR.body, HttpStatus.valueOf(ERROR.status))
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/exception")
|
||||
ResponseEntity exception() {
|
||||
HttpServerTest.controller(EXCEPTION) {
|
||||
throw new Exception(EXCEPTION.body)
|
||||
}
|
||||
}
|
||||
|
||||
@ExceptionHandler
|
||||
ResponseEntity handleException(Throwable throwable) {
|
||||
new ResponseEntity(throwable.message, HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package test;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Application {
|
||||
public static final String USER = "username";
|
||||
public static final String PASS = "password";
|
||||
private static final String ROLE = "USER";
|
||||
|
||||
public static void main(final String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(final HttpSecurity http) throws Exception {
|
||||
http.csrf().disable().authorizeRequests().anyRequest().authenticated().and().httpBasic();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public UserDetailsService userDetailsService() {
|
||||
return new InMemoryUserDetailsManager(
|
||||
User.withUsername(USER).password(PASS).roles(ROLE).build());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package test;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
public class TestController {
|
||||
|
||||
@GetMapping("/")
|
||||
public @ResponseBody String greeting() {
|
||||
return "Hello World";
|
||||
}
|
||||
|
||||
@GetMapping("/param/{parameter}/")
|
||||
public ResponseEntity<String> withParam(@PathVariable("parameter") final String param) {
|
||||
if (param.equals("missing")) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
return ResponseEntity.ok("Hello " + param);
|
||||
}
|
||||
|
||||
@PostMapping("/validated")
|
||||
public @ResponseBody String withValidation(@Valid @RequestBody final TestForm form) {
|
||||
return "Hello " + form.getName() + " " + form;
|
||||
}
|
||||
|
||||
@GetMapping("/error/{parameter}/")
|
||||
public @ResponseBody String withError(@PathVariable("parameter") final String param) {
|
||||
throw new RuntimeException(param);
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package test;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
public class TestForm {
|
||||
|
||||
@NotNull
|
||||
@Size(min = 2, max = 30)
|
||||
private String name;
|
||||
|
||||
@NotNull
|
||||
@Min(18)
|
||||
private Integer age;
|
||||
|
||||
public TestForm() {}
|
||||
|
||||
public TestForm(final String name, final Integer age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(final Integer age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Person(Name: " + this.name + ", Age: " + this.age + ")";
|
||||
}
|
||||
}
|
|
@ -106,9 +106,12 @@ abstract class HttpServerTest<SERVER, DECORATOR extends HttpServerDecorator> ext
|
|||
enum ServerEndpoint {
|
||||
SUCCESS("success", 200, "success"),
|
||||
REDIRECT("redirect", 302, "/redirected"),
|
||||
ERROR("error", 500, "controller error"),
|
||||
ERROR("error-status", 500, "controller error"), // "error" is a special path for some frameworks
|
||||
EXCEPTION("exception", 500, "controller exception"),
|
||||
NOT_FOUND("notFound", 404, "not found"),
|
||||
|
||||
// TODO: add tests for the following cases:
|
||||
PATH_PARAM("path/123/param", 200, "123"),
|
||||
AUTH_REQUIRED("authRequired", 200, null),
|
||||
|
||||
private final String path
|
||||
|
@ -244,7 +247,7 @@ abstract class HttpServerTest<SERVER, DECORATOR extends HttpServerDecorator> ext
|
|||
if (hasHandlerSpan()) {
|
||||
trace(0, 3) {
|
||||
serverSpan(it, 0, null, null, method, REDIRECT)
|
||||
handlerSpan(it, 1, span(0))
|
||||
handlerSpan(it, 1, span(0), REDIRECT)
|
||||
controllerSpan(it, 2, span(1))
|
||||
}
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue