From 9b90ab215e380cffd4f8bcbfdbe000adad3ebbb1 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 2 Mar 2016 16:55:52 +0100 Subject: [PATCH] [#79] Docs for span naming --- .../main/asciidoc/spring-cloud-sleuth.adoc | 60 ++++++++-- .../SpringCloudSleuthDocTests.java | 108 ++++++++++++++++++ .../instrument/async/TraceRunnableTests.java | 4 +- .../zipkin/stream/documentation/Consumer.java | 29 ++--- 4 files changed, 173 insertions(+), 28 deletions(-) create mode 100644 spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java rename spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SamplingTest.java => spring-cloud-sleuth-zipkin-stream/src/test/java/org/springframework/cloud/sleuth/zipkin/stream/documentation/Consumer.java (56%) diff --git a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc index 5af72dc7cf..9706931492 100644 --- a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc +++ b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc @@ -34,7 +34,7 @@ A sampler can be installed just by creating a bean definition, e.g: [source,java] ---- -include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/issues_%2379_updating_documentation/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SamplingTest.java[tags=always_sampler,indent=0] +include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=always_sampler,indent=0] ---- == Instrumentation @@ -57,6 +57,54 @@ NOTE: Remember that tags are only collected and exported if there is a danger of accidentally collecting too much data without configuring something). +=== Span Name + +Picking a span name is not a trivial task. Span name should depict an operation +but shouldn't be too descriptive (you don't want the Zipkin UI to render for minutes, +do you?). + +Since there is a lot of instrumentation going on some of the span names will be +artificial like: + +- `http:path` when received an http request on a given path +- `async` for asynchronous operations done via wrapped `Callable` and `Runnable`. +- `@Scheduled` annotated methods will return the simple name of the class. + +Fortunately, for the asynchronous processing you can provide explicit naming. + +==== @SpanName annotation + +You can do name the span explicitly via the `@SpanName` annotation. + +[source,java] +---- +include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=span_name_annotation,indent=0] +---- + +In this case, when processed in the following manner: + +[source,java] +---- +include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=span_name_annotated_runnable_execution,indent=0] +---- + +The span will be named `calculateTax`. + +==== toString() method + +It's pretty rare to create separate classes for `Runnable` or `Callable`. Typically one creates an anonymous +instance of those classes. You can't annotate such classes thus to override that, if there is no `@SpanName` annotation present, +we're checking if the class has a custom implementation of the `toString()` method. + +So executing such code: + +[source,java] +---- +include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=span_name_to_string_runnable_execution,indent=0] +---- + +will lead in creating a span named `calculateTax`. + == Span Data as Messages You can accumulate and send span data over @@ -75,13 +123,7 @@ for the Span data and pushing it into a Zipkin `SpanStore`. This application [source,java] ---- -@SpringBootApplication -@EnableZipkinStreamServer -public class Consumer { - public static void main(String[] args) { - SpringApplication.run(Consumer.class, args); - } -} +include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/spring-cloud-sleuth-zipkin-stream/src/test/java/org/springframework/cloud/sleuth/zipkin/stream/documentation/Consumer.java[tags=zipkin_consumer,indent=0] ---- will listen for the Span data on whatever transport you provide via a @@ -93,7 +135,7 @@ can point a standard Zipkin UI at it (e.g. run the consumer app on port 9411 if you want the query server on the same host and the default configuration). -The deafult `SpanStore` is in-memory (good for demos and getting +The default `SpanStore` is in-memory (good for demos and getting started quickly). For a more robust solution you can add MySQL and `spring-boot-starter-jdbc` to your classpath and enable the JDBC `SpanStore` via configuration, e.g.: diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java new file mode 100644 index 0000000000..d61c44b6c3 --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java @@ -0,0 +1,108 @@ +/* + * Copyright 2013-2016 the original author or 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 org.springframework.cloud.sleuth.documentation; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import org.junit.Test; +import org.mockito.BDDMockito; +import org.mockito.Mockito; +import org.springframework.cloud.sleuth.DefaultSpanNamer; +import org.springframework.cloud.sleuth.Sampler; +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanName; +import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.TraceRunnable; +import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.sampler.AlwaysSampler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Test class to be embedded in the + * {@code docs/src/main/asciidoc/spring-cloud-sleuth.adoc} file + * + * @author Marcin Grzejszczak + */ +public class SpringCloudSleuthDocTests { + + + @Configuration + public class SamplingConfiguration { + // tag::always_sampler[] + @Bean + public Sampler defaultSampler() { + return new AlwaysSampler(); + } + // end::always_sampler[] + } + + // tag::span_name_annotation[] + @SpanName("calculateTax") + class TaxCountingRunnable implements Runnable { + + @Override public void run() { + // perform logic + } + } + // end::span_name_annotation[] + + @Test + public void should_set_runnable_name_to_annotated_value() + throws ExecutionException, InterruptedException { + ExecutorService executorService = Executors.newSingleThreadExecutor(); + SpanNamer spanNamer = new DefaultSpanNamer(); + Tracer tracer = Mockito.mock(Tracer.class); + + // tag::span_name_annotated_runnable_execution[] + Runnable runnable = new TraceRunnable(tracer, spanNamer, new TaxCountingRunnable()); + Future future = executorService.submit(runnable); + // ... some additional logic ... + future.get(); + // end::span_name_annotated_runnable_execution[] + + BDDMockito.then(tracer.joinTrace(BDDMockito.eq("calculateTax"), BDDMockito.any(Span.class))); + } + + @Test + public void should_set_runnable_name_to_to_string_value() + throws ExecutionException, InterruptedException { + ExecutorService executorService = Executors.newSingleThreadExecutor(); + SpanNamer spanNamer = new DefaultSpanNamer(); + Tracer tracer = Mockito.mock(Tracer.class); + + // tag::span_name_to_string_runnable_execution[] + Runnable runnable = new TraceRunnable(tracer, spanNamer, new Runnable() { + @Override public void run() { + // perform logic + } + + @Override public String toString() { + return "calculateTax"; + } + }); + Future future = executorService.submit(runnable); + // ... some additional logic ... + future.get(); + // end::span_name_to_string_runnable_execution[] + + BDDMockito.then(tracer.joinTrace(BDDMockito.eq("calculateTax"), BDDMockito.any(Span.class))); + } +} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnableTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnableTests.java index f434f39405..583b662451 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnableTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnableTests.java @@ -106,9 +106,9 @@ private void whenRunnableGetsSubmitted(Runnable runnable) throws Exception { this.executor.submit(new TraceRunnable(this.tracer, new DefaultSpanNamer(), runnable)).get(); } - private void whenNonTraceableRunnableGetsSubmitted(Runnable callable) + private void whenNonTraceableRunnableGetsSubmitted(Runnable runnable) throws Exception { - this.executor.submit(callable).get(); + this.executor.submit(runnable).get(); } private Runnable runnableWithCustomToString(final AtomicReference span) { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SamplingTest.java b/spring-cloud-sleuth-zipkin-stream/src/test/java/org/springframework/cloud/sleuth/zipkin/stream/documentation/Consumer.java similarity index 56% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SamplingTest.java rename to spring-cloud-sleuth-zipkin-stream/src/test/java/org/springframework/cloud/sleuth/zipkin/stream/documentation/Consumer.java index d30a54f19a..76f10a9486 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SamplingTest.java +++ b/spring-cloud-sleuth-zipkin-stream/src/test/java/org/springframework/cloud/sleuth/zipkin/stream/documentation/Consumer.java @@ -14,29 +14,24 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.documentation; +package org.springframework.cloud.sleuth.zipkin.stream.documentation; -import org.springframework.cloud.sleuth.Sampler; -import org.springframework.cloud.sleuth.sampler.AlwaysSampler; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.sleuth.zipkin.stream.EnableZipkinStreamServer; /** - * Test class to be embedded in the Sampling part of + * Test class to be embedded in the Zipkin Consumer part of * {@code docs/src/main/asciidoc/spring-cloud-sleuth.adoc} * * @author Marcin Grzejszczak */ -public class SamplingTest { - - - @Configuration - public class SamplingConfiguration { - // tag::always_sampler[] - @Bean - public Sampler defaultSampler() { - return new AlwaysSampler(); - } - // end::always_sampler[] +// tag::zipkin_consumer[] +@SpringBootApplication +@EnableZipkinStreamServer +public class Consumer { + public static void main(String[] args) { + SpringApplication.run(Consumer.class, args); } } +// end::zipkin_consumer[] \ No newline at end of file