Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds Spring Boot 1.5 (webmvc4) example #25

Merged
merged 2 commits into from
Aug 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ Next, you can view traces that went through the backend via http://localhost:941
* This is a locally run zipkin service which keeps traces in memory

## Starting the Services

### Servlet Container Option
In a separate tab or window, start each of [brave.webmvc.Frontend](/webmvc4/src/main/java/brave/webmvc/Frontend.java)
and [brave.webmvc.Backend](/webmvc4/src/main/java/brave/webmvc/Backend.java):
```bash
Expand All @@ -39,6 +41,15 @@ $ mvn jetty:run -Pfrontend
$ mvn jetty:run -Pbackend
```

### Spring Boot Option
In a separate tab or window, start each of [brave.webmvc.Frontend](/webmvc4-boot/src/main/java/brave/webmvc/Frontend.java)
and [brave.webmvc.Backend](/webmvc4-boot/src/main/java/brave/webmvc/Backend.java):
```bash
$ cd webmvc4-boot
$ mvn compile exec:java -Dexec.mainClass=brave.webmvc.Backend
$ mvn compile exec:java -Dexec.mainClass=brave.webmvc.Frontend
```

Next, run [Zipkin](http://zipkin.io/), which stores and queries traces
reported by the above services.

Expand Down
18 changes: 18 additions & 0 deletions webmvc4-boot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
## WebMVC 4 Boot Example

Instead of servlet, this uses Spring Boot 1.5 to create a self-contained
application that runs Spring WebMVC 4 controllers.

* brave.webmvc.Frontend and Backend : Rest controllers with no tracing configuration
* brave.webmvc.TracingConfiguration : This adds tracing by configuring the tracer, server and client tracing interceptors.

`TracingConfiguration` is automatically loaded due to `META-INF/spring.factories`
This allows the `Frontend` and `Backend` controllers to have no tracing
code. Inside the tracing configuration, you'll notice the rest template
is setup via a `RestTemplateCustomizer`, which ensures application-level
interceptors are not affected. Also, you'll notice layered tracing for
server requests. First, `TracingFilter` creates a span, then later,
`SpanCustomizingAsyncHandlerInterceptor` adds MVC tags to it.

*Note* This only lightly configures tracing. When doing anything serious,
consider [Spring Cloud Sleuth](https://github.com/spring-cloud/spring-cloud-sleuth) instead.
69 changes: 69 additions & 0 deletions webmvc4-boot/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>io.zipkin.brave</groupId>
<artifactId>brave-webmvc4-boot-example</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>brave-webmvc4-boot-example</name>
<description>Example using Brave to trace RPCs from Spring Web MVC</description>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>

<spring-boot.version>1.5.14.RELEASE</spring-boot.version>
<brave.version>5.1.3</brave.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-bom</artifactId>
<version>${brave.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-sender-okhttp3</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-context-slf4j</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-spring-web</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-spring-webmvc</artifactId>
</dependency>
</dependencies>
</project>
28 changes: 28 additions & 0 deletions webmvc4-boot/src/main/java/brave/webmvc/Backend.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package brave.webmvc;

import java.util.Date;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@EnableAutoConfiguration
@RestController
public class Backend {

@RequestMapping("/api")
public String printDate(@RequestHeader(name = "user-name", required = false) String username) {
if (username != null) {
return new Date().toString() + " " + username;
}
return new Date().toString();
}

public static void main(String[] args) {
SpringApplication.run(Backend.class,
"--spring.application.name=backend",
"--server.port=9000"
);
}
}
33 changes: 33 additions & 0 deletions webmvc4-boot/src/main/java/brave/webmvc/Frontend.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package brave.webmvc;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@EnableAutoConfiguration
@RestController
@CrossOrigin // So that javascript can be hosted elsewhere
public class Frontend {

@Autowired RestTemplate restTemplate;

@RequestMapping("/") public String callBackend() {
return restTemplate.getForObject("http://localhost:9000/api", String.class);
}

@Bean RestTemplate restTemplate() {
return new RestTemplate();
}

public static void main(String[] args) {
SpringApplication.run(Frontend.class,
"--spring.application.name=frontend",
"--server.port=8081"
);
}
}
86 changes: 86 additions & 0 deletions webmvc4-boot/src/main/java/brave/webmvc/TracingConfiguration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package brave.webmvc;

import brave.Tracing;
import brave.context.slf4j.MDCCurrentTraceContext;
import brave.http.HttpTracing;
import brave.propagation.B3Propagation;
import brave.propagation.ExtraFieldPropagation;
import brave.servlet.TracingFilter;
import brave.spring.web.TracingClientHttpRequestInterceptor;
import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import zipkin2.Span;
import zipkin2.reporter.AsyncReporter;
import zipkin2.reporter.Sender;
import zipkin2.reporter.okhttp3.OkHttpSender;

/**
* This adds tracing configuration to any web mvc controllers or rest template clients.
*/
@Configuration
// Importing a class is effectively the same as declaring bean methods
@Import(SpanCustomizingAsyncHandlerInterceptor.class)
public class TracingConfiguration extends WebMvcConfigurerAdapter {

/** Configuration for how to send spans to Zipkin */
@Bean Sender sender() {
return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans");
}

/** Configuration for how to buffer spans into messages for Zipkin */
@Bean AsyncReporter<Span> spanReporter() {
return AsyncReporter.create(sender());
}

/** Controls aspects of tracing such as the name that shows up in the UI */
@Bean Tracing tracing(@Value("${spring.application.name}") String serviceName) {
return Tracing.newBuilder()
.localServiceName(serviceName)
.propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "user-name"))
.currentTraceContext(MDCCurrentTraceContext.create()) // puts trace IDs into logs
.spanReporter(spanReporter()).build();
}

/** decides how to name and tag spans. By default they are named the same as the http method. */
@Bean HttpTracing httpTracing(Tracing tracing) {
return HttpTracing.create(tracing);
}

/** Creates client spans for http requests */
@Bean @Order(Ordered.HIGHEST_PRECEDENCE)
RestTemplateCustomizer tracingRestTemplateCustomizer(final HttpTracing httpTracing) {
return new RestTemplateCustomizer() {
@Override public void customize(RestTemplate restTemplate) {
List<ClientHttpRequestInterceptor> interceptors =
new ArrayList<>(restTemplate.getInterceptors());
interceptors.add(0, TracingClientHttpRequestInterceptor.create(httpTracing));
}
};
}

/** Creates server spans for http requests */
@Bean Filter tracingFilter(HttpTracing httpTracing) {
return TracingFilter.create(httpTracing);
}

@Autowired SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;

/** Decorates server spans with application-defined web tags */
@Override public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(webMvcTracingCustomizer);
}
}
2 changes: 2 additions & 0 deletions webmvc4-boot/src/main/resources/META-INF/spring.factories
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
brave.webmvc.TracingConfiguration
5 changes: 5 additions & 0 deletions webmvc4-boot/src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# spring.application.name and server.port are set in the main methods,
# so not done here
logging.level.org.springframework.web=DEBUG
# Adds trace and span IDs to logs (when a trace is in progress)
logging.pattern.level=%d{ABSOLUTE} [%X{traceId}/%X{spanId}] %-5p [%t] %C{2} - %m%n