diff --git a/zipkin-server/README.md b/zipkin-server/README.md index 98ab9271d86..b4cd8dbc594 100644 --- a/zipkin-server/README.md +++ b/zipkin-server/README.md @@ -1,5 +1,8 @@ # zipkin-server -The receives spans via HTTP POST and respond to queries from zipkin-web. +The hosts the Zipkin [Api](http://zipkin.io/zipkin-api/#/) and [UI](https://github.com/openzipkin/zipkin/tree/master/zipkin-ui). + +Span storage and transports are configurable. By default storage is +in-memory and the http span transport (POST /spans endpoint) is enabled. Note that the server requires minimum JRE 8. diff --git a/zipkin-server/pom.xml b/zipkin-server/pom.xml index b2b28416d00..26417b97ef0 100644 --- a/zipkin-server/pom.xml +++ b/zipkin-server/pom.xml @@ -29,6 +29,7 @@ ${project.basedir}/.. 3.5.0 + 1.38.0 zipkin.server.ZipkinServer 2.0.0 @@ -75,6 +76,14 @@ zipkin + + + io.zipkin + zipkin-ui + ${zipkin-ui.version} + true + + ${project.groupId} diff --git a/zipkin-server/src/main/java/zipkin/server/EnableZipkinServer.java b/zipkin-server/src/main/java/zipkin/server/EnableZipkinServer.java index f40815fb741..5e7b98453d0 100644 --- a/zipkin-server/src/main/java/zipkin/server/EnableZipkinServer.java +++ b/zipkin-server/src/main/java/zipkin/server/EnableZipkinServer.java @@ -24,7 +24,7 @@ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented -@Import({ZipkinServerConfiguration.class, BraveConfiguration.class, ZipkinQueryApiV1.class, ZipkinSpanWriter.class}) +@Import({ZipkinServerConfiguration.class, BraveConfiguration.class, ZipkinQueryApiV1.class, ZipkinUiConfiguration.class, ZipkinSpanWriter.class}) public @interface EnableZipkinServer { } diff --git a/zipkin-server/src/main/java/zipkin/server/ZipkinServerProperties.java b/zipkin-server/src/main/java/zipkin/server/ZipkinServerProperties.java index 5caeb9c393c..c2cf8d91908 100644 --- a/zipkin-server/src/main/java/zipkin/server/ZipkinServerProperties.java +++ b/zipkin-server/src/main/java/zipkin/server/ZipkinServerProperties.java @@ -23,6 +23,12 @@ public Store getStore() { return store; } + private Ui ui = new Ui(); + + public Ui getUi() { + return ui; + } + static class Store { enum Type { cassandra, mysql, mem @@ -38,4 +44,25 @@ public void setType(Type type) { this.type = type; } } + + static class Ui { + private String environment; + private int queryLimit = 10; + + public String getEnvironment() { + return environment; + } + + public void setEnvironment(String environment) { + this.environment = environment; + } + + public int getQueryLimit() { + return queryLimit; + } + + public void setQueryLimit(int queryLimit) { + this.queryLimit = queryLimit; + } + } } diff --git a/zipkin-server/src/main/java/zipkin/server/ZipkinUiConfiguration.java b/zipkin-server/src/main/java/zipkin/server/ZipkinUiConfiguration.java new file mode 100644 index 00000000000..9623243e4c7 --- /dev/null +++ b/zipkin-server/src/main/java/zipkin/server/ZipkinUiConfiguration.java @@ -0,0 +1,74 @@ +/** + * Copyright 2015-2016 The OpenZipkin Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package zipkin.server; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnResource; +import org.springframework.context.annotation.Configuration; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +/** + * Zipkin-UI is a single-page application that reads configuration from /config.json. + * + *

When looking at a trace, the browser is sent to the path "/traces/{id}". For the single-page + * app to serve that route, the server needs to forward the request to "/index.html". The same + * forwarding applies to "/dependencies" and any other routes the UI controls. + * + *

Under the scenes the JavaScript code looks at {@code window.location} to figure out what the + * UI should do. This is handled by a route api defined in the crossroads library. + */ +@Configuration +@ConditionalOnResource(resources = "classpath:zipkin-ui") // from io.zipkin:zipkin-ui +public class ZipkinUiConfiguration extends WebMvcConfigurerAdapter { + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/**").addResourceLocations("classpath:/zipkin-ui/"); + } + + @RestController + public static class ZipkinUi { + + @Autowired + ZipkinServerProperties server; + + @RequestMapping(value = "/config.json", method = RequestMethod.GET, produces = APPLICATION_JSON_VALUE) + public ZipkinServerProperties.Ui getUiConfig() { + return server.getUi(); + } + + /** + * This cherry-picks well-known routes the single-page app serves, and forwards to that as + * opposed to returning a 404. + */ + // TODO This approach requires maintenance when new UI routes are added. Change to the following: + // If the path is a a file w/an extension, treat normally. + // Otherwise instead of returning 404, forward to the index. + // See https://github.com/twitter/finatra/blob/458c6b639c3afb4e29873d123125eeeb2b02e2cd/http/src/main/scala/com/twitter/finatra/http/response/ResponseBuilder.scala#L321 + @RequestMapping(value = {"/", "/traces/{id}", "/dependency"}, method = RequestMethod.GET) + public ModelAndView forwardUiEndpoints(ModelMap model) { + // Note: RequestMapping "/" requires us to use ModelAndView result vs just a string. + // When "/" is mapped, the server literally returns "forward:/index.html" vs forwarding. + return new ModelAndView("forward:/index.html", model); + } + } +} diff --git a/zipkin-server/src/main/resources/zipkin-server.yml b/zipkin-server/src/main/resources/zipkin-server.yml index 0499cad460f..49b6db480ea 100644 --- a/zipkin-server/src/main/resources/zipkin-server.yml +++ b/zipkin-server/src/main/resources/zipkin-server.yml @@ -41,6 +41,12 @@ zipkin: lookback: ${QUERY_LOOKBACK:86400000} store: type: ${STORAGE_TYPE:mem} + # Values here are read by Zipkin UI's javascript at /config.json + ui: + # Default limit for Find Traces + query-limit: 10 + # The value here becomes a label in the top-right corner + environment: server: port: ${QUERY_PORT:9411} compression: