package brave.spring.webmvc; import brave.Tracer; import brave.http.HttpTracing; import brave.http.ITServletContainer; import java.io.IOException; import java.util.concurrent.Callable; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.AsyncHandlerInterceptor; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; public class ITTracingHandlerInterceptor extends ITServletContainer { @Controller static class TestController { final Tracer tracer; @Autowired TestController(HttpTracing httpTracing) { this.tracer = httpTracing.tracing().tracer(); } @RequestMapping(value = "/foo") public ResponseEntity<Void> foo() throws IOException { return new ResponseEntity<>(HttpStatus.OK); } @RequestMapping(value = "/badrequest") public ResponseEntity<Void> badrequest() throws IOException { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } @RequestMapping(value = "/child") public ResponseEntity<Void> child() { tracer.nextSpan().name("child").start().finish(); return new ResponseEntity<>(HttpStatus.OK); } @RequestMapping(value = "/async") public Callable<ResponseEntity<Void>> async() throws IOException { return () -> new ResponseEntity<>(HttpStatus.OK); } @RequestMapping(value = "/exception") public ResponseEntity<Void> disconnect() throws IOException { throw new IOException(); } @RequestMapping(value = "/exceptionAsync") public Callable<ResponseEntity<Void>> disconnectAsync() throws IOException { return () -> { throw new IOException(); }; } } @Configuration @EnableWebMvc static class TracingConfig extends WebMvcConfigurerAdapter { @Bean AsyncHandlerInterceptor tracingInterceptor(HttpTracing httpTracing) { return TracingHandlerInterceptor.create(httpTracing); } @Autowired private AsyncHandlerInterceptor tracingInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(tracingInterceptor); } } @Override public void init(ServletContextHandler handler) { AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext() { // overriding this allows us to register dependencies of TracingHandlerInterceptor // without passing static state to a configuration class. @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) { beanFactory.registerSingleton("httpTracing", httpTracing); super.loadBeanDefinitions(beanFactory); } }; appContext.register(TestController.class); // the test resource appContext.register(TracingConfig.class); // generic tracing setup handler.addServlet(new ServletHolder(new DispatcherServlet(appContext)), "/*"); } }