Skip to content

Commit

Permalink
[#79] Docs for span naming
Browse files Browse the repository at this point in the history
  • Loading branch information
marcingrzejszczak committed Mar 2, 2016
1 parent 3184a40 commit 9b90ab2
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 28 deletions.
60 changes: 51 additions & 9 deletions docs/src/main/asciidoc/spring-cloud-sleuth.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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.:
Expand Down
Original file line number Diff line number Diff line change
@@ -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)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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> span) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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[]

0 comments on commit 9b90ab2

Please sign in to comment.