Trying to fix the build
This commit is contained in:
22
README.adoc
22
README.adoc
@@ -401,7 +401,7 @@ the Spring BOM
|
||||
|
||||
==== Sleuth with Zipkin via HTTP
|
||||
|
||||
If you want both Sleuth and Zipkin just add the `spring-cloud-starter-zipkin2` dependency.
|
||||
If you want both Sleuth and Zipkin just add the `spring-cloud-starter-zipkin` dependency.
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||||
.Maven
|
||||
@@ -420,12 +420,12 @@ If you want both Sleuth and Zipkin just add the `spring-cloud-starter-zipkin2` d
|
||||
|
||||
<dependency> <2>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-zipkin2</artifactId>
|
||||
<artifactId>spring-cloud-starter-zipkin</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
<1> In order not to pick versions by yourself it's much better if you add the dependency management via
|
||||
the Spring BOM
|
||||
<2> Add the dependency to `spring-cloud-starter-zipkin2`
|
||||
<2> Add the dependency to `spring-cloud-starter-zipkin`
|
||||
|
||||
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
|
||||
.Gradle
|
||||
@@ -437,12 +437,12 @@ dependencyManagement { <1>
|
||||
}
|
||||
|
||||
dependencies { <2>
|
||||
compile "org.springframework.cloud:spring-cloud-starter-zipkin2"
|
||||
compile "org.springframework.cloud:spring-cloud-starter-zipkin"
|
||||
}
|
||||
----
|
||||
<1> In order not to pick versions by yourself it's much better if you add the dependency management via
|
||||
the Spring BOM
|
||||
<2> Add the dependency to `spring-cloud-starter-zipkin2`
|
||||
<2> Add the dependency to `spring-cloud-starter-zipkin`
|
||||
|
||||
==== Sleuth with Zipkin via RabbitMQ or Kafka
|
||||
|
||||
@@ -451,7 +451,7 @@ dependencies. The default destination name is `zipkin`.
|
||||
|
||||
_Note: `spring-cloud-sleuth-stream` is deprecated and incompatible with these destinations_
|
||||
|
||||
If you want Sleuth over RabbitMQ add the `spring-cloud-starter-zipkin2` and `spring-rabbit`
|
||||
If you want Sleuth over RabbitMQ add the `spring-cloud-starter-zipkin` and `spring-rabbit`
|
||||
dependencies.
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||||
@@ -471,7 +471,7 @@ dependencies.
|
||||
|
||||
<dependency> <2>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-zipkin2</artifactId>
|
||||
<artifactId>spring-cloud-starter-zipkin</artifactId>
|
||||
</dependency>
|
||||
<dependency> <3>
|
||||
<groupId>org.springframework.amqp</groupId>
|
||||
@@ -480,7 +480,7 @@ dependencies.
|
||||
----
|
||||
<1> In order not to pick versions by yourself it's much better if you add the dependency management via
|
||||
the Spring BOM
|
||||
<2> Add the dependency to `spring-cloud-starter-zipkin2` - that way all dependent dependencies will be downloaded
|
||||
<2> Add the dependency to `spring-cloud-starter-zipkin` - that way all dependent dependencies will be downloaded
|
||||
<3> To automatically configure rabbit, simply add the spring-rabbit dependency
|
||||
|
||||
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
|
||||
@@ -493,13 +493,13 @@ dependencyManagement { <1>
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "org.springframework.cloud:spring-cloud-starter-zipkin2" <2>
|
||||
compile "org.springframework.cloud:spring-cloud-starter-zipkin" <2>
|
||||
compile "org.springframework.amqp:spring-rabbit" <3>
|
||||
}
|
||||
----
|
||||
<1> In order not to pick versions by yourself it's much better if you add the dependency management via
|
||||
the Spring BOM
|
||||
<2> Add the dependency to `spring-cloud-starter-zipkin2` - that way all dependent dependencies will be downloaded
|
||||
<2> Add the dependency to `spring-cloud-starter-zipkin` - that way all dependent dependencies will be downloaded
|
||||
<3> To automatically configure rabbit, simply add the spring-rabbit dependency
|
||||
|
||||
== Additional resources
|
||||
@@ -553,7 +553,7 @@ a baggage element then it will be sent downstream either via HTTP or messaging t
|
||||
|
||||
* Provides simple metrics of accepted / dropped spans.
|
||||
|
||||
* If `spring-cloud-sleuth-zipkin2` then the app will generate and collect Zipkin-compatible traces.
|
||||
* If `spring-cloud-sleuth-zipkin` then the app will generate and collect Zipkin-compatible traces.
|
||||
By default it sends them via HTTP to a Zipkin server on localhost (port 9411).
|
||||
Configure the location of the service using `spring.zipkin.baseUrl`.
|
||||
- If you depend on `spring-rabbit` or `spring-kafka` your app will send traces to a broker instead of http.
|
||||
|
||||
@@ -41,11 +41,11 @@ a baggage element then it will be sent downstream either via HTTP or messaging t
|
||||
|
||||
* Provides simple metrics of accepted / dropped spans.
|
||||
|
||||
* If `spring-cloud-sleuth-zipkin2` then the app will generate and collect Zipkin-compatible traces.
|
||||
* If `spring-cloud-sleuth-zipkin` then the app will generate and collect Zipkin-compatible traces.
|
||||
By default it sends them via HTTP to a Zipkin server on localhost (port 9411).
|
||||
Configure the location of the service using `spring.zipkin.baseUrl`.
|
||||
- If you depend on `spring-rabbit` or `spring-kafka` your app will send traces to a broker instead of http.
|
||||
- Note: `spring-cloud-sleuth-stream` is deprecated and should no longer be used.
|
||||
- Note: `spring-cloud-sleuth-stream` is deprecated and should no longer be used.
|
||||
|
||||
IMPORTANT: If using Zipkin, configure the percentage of spans exported using `spring.sleuth.sampler.percentage`
|
||||
(default 0.1, i.e. 10%). *Otherwise you might think that Sleuth is not working cause it's omitting some spans.*
|
||||
|
||||
@@ -280,7 +280,7 @@ the Spring BOM
|
||||
|
||||
==== Sleuth with Zipkin via HTTP
|
||||
|
||||
If you want both Sleuth and Zipkin just add the `spring-cloud-starter-zipkin2` dependency.
|
||||
If you want both Sleuth and Zipkin just add the `spring-cloud-starter-zipkin` dependency.
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||||
.Maven
|
||||
@@ -299,12 +299,12 @@ If you want both Sleuth and Zipkin just add the `spring-cloud-starter-zipkin2` d
|
||||
|
||||
<dependency> <2>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-zipkin2</artifactId>
|
||||
<artifactId>spring-cloud-starter-zipkin</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
<1> In order not to pick versions by yourself it's much better if you add the dependency management via
|
||||
the Spring BOM
|
||||
<2> Add the dependency to `spring-cloud-starter-zipkin2`
|
||||
<2> Add the dependency to `spring-cloud-starter-zipkin`
|
||||
|
||||
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
|
||||
.Gradle
|
||||
@@ -316,12 +316,12 @@ dependencyManagement { <1>
|
||||
}
|
||||
|
||||
dependencies { <2>
|
||||
compile "org.springframework.cloud:spring-cloud-starter-zipkin2"
|
||||
compile "org.springframework.cloud:spring-cloud-starter-zipkin"
|
||||
}
|
||||
----
|
||||
<1> In order not to pick versions by yourself it's much better if you add the dependency management via
|
||||
the Spring BOM
|
||||
<2> Add the dependency to `spring-cloud-starter-zipkin2`
|
||||
<2> Add the dependency to `spring-cloud-starter-zipkin`
|
||||
|
||||
==== Sleuth with Zipkin via RabbitMQ or Kafka
|
||||
|
||||
@@ -330,7 +330,7 @@ dependencies. The default destination name is `zipkin`.
|
||||
|
||||
_Note: `spring-cloud-sleuth-stream` is deprecated and incompatible with these destinations_
|
||||
|
||||
If you want Sleuth over RabbitMQ add the `spring-cloud-starter-zipkin2` and `spring-rabbit`
|
||||
If you want Sleuth over RabbitMQ add the `spring-cloud-starter-zipkin` and `spring-rabbit`
|
||||
dependencies.
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||||
@@ -350,7 +350,7 @@ dependencies.
|
||||
|
||||
<dependency> <2>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-zipkin2</artifactId>
|
||||
<artifactId>spring-cloud-starter-zipkin</artifactId>
|
||||
</dependency>
|
||||
<dependency> <3>
|
||||
<groupId>org.springframework.amqp</groupId>
|
||||
@@ -359,7 +359,7 @@ dependencies.
|
||||
----
|
||||
<1> In order not to pick versions by yourself it's much better if you add the dependency management via
|
||||
the Spring BOM
|
||||
<2> Add the dependency to `spring-cloud-starter-zipkin2` - that way all dependent dependencies will be downloaded
|
||||
<2> Add the dependency to `spring-cloud-starter-zipkin` - that way all dependent dependencies will be downloaded
|
||||
<3> To automatically configure rabbit, simply add the spring-rabbit dependency
|
||||
|
||||
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
|
||||
@@ -372,13 +372,13 @@ dependencyManagement { <1>
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "org.springframework.cloud:spring-cloud-starter-zipkin2" <2>
|
||||
compile "org.springframework.cloud:spring-cloud-starter-zipkin" <2>
|
||||
compile "org.springframework.amqp:spring-rabbit" <3>
|
||||
}
|
||||
----
|
||||
<1> In order not to pick versions by yourself it's much better if you add the dependency management via
|
||||
the Spring BOM
|
||||
<2> Add the dependency to `spring-cloud-starter-zipkin2` - that way all dependent dependencies will be downloaded
|
||||
<2> Add the dependency to `spring-cloud-starter-zipkin` - that way all dependent dependencies will be downloaded
|
||||
<3> To automatically configure rabbit, simply add the spring-rabbit dependency
|
||||
|
||||
== Additional resources
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -29,11 +29,9 @@
|
||||
<module>spring-cloud-sleuth-dependencies</module>
|
||||
<module>spring-cloud-sleuth-core</module>
|
||||
<module>spring-cloud-sleuth-zipkin</module>
|
||||
<module>spring-cloud-sleuth-zipkin2</module>
|
||||
<module>spring-cloud-sleuth-stream</module>
|
||||
<module>spring-cloud-starter-sleuth</module>
|
||||
<module>spring-cloud-starter-zipkin</module>
|
||||
<module>spring-cloud-starter-zipkin2</module>
|
||||
<module>spring-cloud-sleuth-samples</module>
|
||||
<module>docs</module>
|
||||
</modules>
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web.client.feign;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
@@ -41,13 +43,17 @@ class TraceFeignAspect {
|
||||
|
||||
@Around("execution (* feign.Client.*(..)) && !within(is(FinalType))")
|
||||
public Object feignClientWasCalled(final ProceedingJoinPoint pjp) throws Throwable {
|
||||
Object[] args = pjp.getArgs();
|
||||
Request request = (Request) args[0];
|
||||
Request.Options options = (Request.Options) args[1];
|
||||
Object bean = pjp.getTarget();
|
||||
if (!(bean instanceof TraceFeignClient)) {
|
||||
return new TraceFeignClient(this.beanFactory, (Client) bean).execute(request, options);
|
||||
if (!(bean instanceof TraceFeignClient) && !(bean instanceof TraceLoadBalancerFeignClient)) {
|
||||
return executeTraceFeignClient(bean, pjp);
|
||||
}
|
||||
return pjp.proceed();
|
||||
}
|
||||
|
||||
Object executeTraceFeignClient(Object bean, ProceedingJoinPoint pjp) throws IOException {
|
||||
Object[] args = pjp.getArgs();
|
||||
Request request = (Request) args[0];
|
||||
Request.Options options = (Request.Options) args[1];
|
||||
return new TraceFeignClient(this.beanFactory, (Client) bean).execute(request, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web.client.feign;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TraceFeignAspectTests {
|
||||
|
||||
@Mock BeanFactory beanFactory;
|
||||
@Mock Client client;
|
||||
@Mock ProceedingJoinPoint pjp;
|
||||
@Mock TraceLoadBalancerFeignClient traceLoadBalancerFeignClient;
|
||||
TraceFeignAspect traceFeignAspect;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
stubPjp();
|
||||
this.traceFeignAspect = new TraceFeignAspect(this.beanFactory) {
|
||||
@Override Object executeTraceFeignClient(Object bean, ProceedingJoinPoint pjp) throws IOException {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void stubPjp() {
|
||||
Request request = Request.create("foo", "bar", new HashMap<>(), new byte[] {}, Charset
|
||||
.defaultCharset());
|
||||
Request.Options options = new Request.Options();
|
||||
given(this.pjp.getArgs()).willReturn(new Object[] {request, options} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_wrap_feign_client_in_trace_representation() throws Throwable {
|
||||
given(this.pjp.getTarget()).willReturn(this.client);
|
||||
|
||||
this.traceFeignAspect.feignClientWasCalled(this.pjp);
|
||||
|
||||
verify(this.pjp, never()).proceed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_wrap_traced_feign_client_in_trace_representation() throws Throwable {
|
||||
given(this.pjp.getTarget()).willReturn(new TraceFeignClient(this.beanFactory, this.client));
|
||||
|
||||
this.traceFeignAspect.feignClientWasCalled(this.pjp);
|
||||
|
||||
verify(this.pjp).proceed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_wrap_traced_load_balancer_feign_client_in_trace_representation() throws Throwable {
|
||||
given(this.pjp.getTarget()).willReturn(this.traceLoadBalancerFeignClient);
|
||||
|
||||
this.traceFeignAspect.feignClientWasCalled(this.pjp);
|
||||
|
||||
verify(this.pjp).proceed();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,21 +25,11 @@
|
||||
<artifactId>spring-cloud-sleuth-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-reactor</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-zipkin2</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-stream</artifactId>
|
||||
@@ -55,11 +45,6 @@
|
||||
<artifactId>spring-cloud-starter-zipkin</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-zipkin2</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-sleuth</artifactId>
|
||||
|
||||
@@ -6,7 +6,7 @@ There are a few samples with slightly different features. You can run all of the
|
||||
|
||||
* `spring-cloud-sleuth-sample`: vanilla (no zipkin) web app that calls back to itself on various endpoints ("/", "/call", "/async")
|
||||
|
||||
* `spring-cloud-sleuth-sample-zipkin2`: same as vanilla sample but with zipkin (set `sample.zipkin.enabled=true` if you have a collector running)
|
||||
* `spring-cloud-sleuth-sample-zipkin`: same as vanilla sample but with zipkin (set `sample.zipkin.enabled=true` if you have a collector running)
|
||||
|
||||
* `spring-cloud-sleuth-sample-messaging`: a Spring Integration application with two HTTP endpoints ("/" and "/xform")
|
||||
|
||||
@@ -16,7 +16,7 @@ The Ribbon sample makes an interesting demo or playground for learning about zip
|
||||
|
||||
=== Running samples with Zipkin
|
||||
|
||||
1. Optionally run the https://github.com/openzipkin/zipkin[Zipkin] Server, e.g. via docker compose (there's a `docker-compose.yml` in https://github.com/spring-cloud/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin2[Spring Cloud Sleuth], or in https://github.com/openzipkin/docker-zipkin[Docker Zipkin]
|
||||
1. Optionally run the https://github.com/openzipkin/zipkin[Zipkin] Server, e.g. via docker compose (there's a `docker-compose.yml` in https://github.com/spring-cloud/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin[Spring Cloud Sleuth], or in https://github.com/openzipkin/docker-zipkin[Docker Zipkin]
|
||||
7. Run the zipkin sample application (set `sample.zipkin.enabled=false` if you have no Zipkin running). If you are using a VM to run docker you might need to tunnel port 9411 to localhost, or change the `spring.zipkin.baseUrl`.
|
||||
8. Hit `http://localhost:3380`, `http://localhost:3380/call`, `http://localhost:3380/async` for some interesting sample traces (the app callas back to itself).
|
||||
9. Go to `http://localhost:9411` for Zipkin's UI (if you are using boot2docker the host will be different)
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<module>spring-cloud-sleuth-sample-websocket</module>
|
||||
<module>spring-cloud-sleuth-sample-feign</module>
|
||||
<module>spring-cloud-sleuth-sample-ribbon</module>
|
||||
<module>spring-cloud-sleuth-sample-zipkin2</module>
|
||||
<module>spring-cloud-sleuth-sample-zipkin</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
@@ -55,10 +55,15 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.zipkin2</groupId>
|
||||
<groupId>io.zipkin.java</groupId>
|
||||
<artifactId>zipkin</artifactId>
|
||||
<version>2.2.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.java</groupId>
|
||||
<artifactId>zipkin-server</artifactId>
|
||||
<version>2.2.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- ~ Copyright 2013-2017 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
|
||||
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. -->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
@@ -68,7 +68,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-zipkin2</artifactId>
|
||||
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-zipkin2</artifactId>
|
||||
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-zipkin2</artifactId>
|
||||
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-zipkin2</artifactId>
|
||||
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
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>
|
||||
|
||||
<artifactId>spring-cloud-sleuth-sample-zipkin2</artifactId>
|
||||
<artifactId>spring-cloud-sleuth-sample-zipkin</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>spring-cloud-sleuth-sample-zipkin2</name>
|
||||
<description>Spring Cloud Sleuth Sample Zipkin v2</description>
|
||||
<name>spring-cloud-sleuth-sample-zipkin</name>
|
||||
<description>Spring Cloud Sleuth Sample Zipkin</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
@@ -77,7 +77,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-zipkin2</artifactId>
|
||||
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -75,18 +75,32 @@
|
||||
<artifactId>micrometer-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.java</groupId>
|
||||
<artifactId>zipkin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.zipkin2</groupId>
|
||||
<artifactId>zipkin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.reporter</groupId>
|
||||
<groupId>io.zipkin.reporter2</groupId>
|
||||
<artifactId>zipkin-reporter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.reporter2</groupId>
|
||||
<artifactId>zipkin-sender-kafka11</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.kafka</groupId>
|
||||
<artifactId>spring-kafka</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.reporter2</groupId>
|
||||
<artifactId>zipkin-sender-amqp-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.amqp</groupId>
|
||||
<artifactId>spring-rabbit</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-messaging</artifactId>
|
||||
@@ -115,7 +129,7 @@
|
||||
<!-- otherwise spring boot's version of okhttp kicks zipkin-junit's deps out of alignment -->
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<artifactId>mockwebserver</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* 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.zipkin;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Default {@link ZipkinRestTemplateCustomizer} that provides the GZip compression if
|
||||
* {@link ZipkinProperties#compression} is enabled.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class DefaultZipkinRestTemplateCustomizer implements ZipkinRestTemplateCustomizer {
|
||||
private final ZipkinProperties zipkinProperties;
|
||||
|
||||
public DefaultZipkinRestTemplateCustomizer(
|
||||
ZipkinProperties zipkinProperties) {
|
||||
this.zipkinProperties = zipkinProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(RestTemplate restTemplate) {
|
||||
if (this.zipkinProperties.getCompression().isEnabled()) {
|
||||
restTemplate.getInterceptors().add(0, new GZipInterceptor());
|
||||
}
|
||||
}
|
||||
|
||||
private class GZipInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws
|
||||
IOException {
|
||||
request.getHeaders().add("Content-Encoding", "gzip");
|
||||
ByteArrayOutputStream gzipped = new ByteArrayOutputStream();
|
||||
try (GZIPOutputStream compressor = new GZIPOutputStream(gzipped)) {
|
||||
compressor.write(body);
|
||||
}
|
||||
return execution.execute(request, gzipped.toByteArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 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.zipkin;
|
||||
|
||||
import zipkin.Endpoint;
|
||||
|
||||
/**
|
||||
* Strategy for locating a zipkin {@linkplain Endpoint} for the current process.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.0.0
|
||||
* @deprecated Please use spring-cloud-sleuth-zipkin2 to report spans to Zipkin
|
||||
*/
|
||||
@Deprecated
|
||||
public interface EndpointLocator {
|
||||
|
||||
Endpoint local();
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.zipkin;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import zipkin.Endpoint;
|
||||
|
||||
/**
|
||||
* Endpoint locator that will try to call an endpoint via Discovery Client
|
||||
* and will fallback to Server Properties if an exception is thrown
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class FallbackHavingEndpointLocator implements EndpointLocator {
|
||||
|
||||
private static final Log log = LogFactory.getLog(FallbackHavingEndpointLocator.class);
|
||||
|
||||
private final ServiceInstanceEndpointLocator serviceInstanceEndpointLocator;
|
||||
private final ServerPropertiesEndpointLocator serverPropertiesEndpointLocator;
|
||||
|
||||
public FallbackHavingEndpointLocator(ServiceInstanceEndpointLocator serviceInstanceEndpointLocator,
|
||||
ServerPropertiesEndpointLocator serverPropertiesEndpointLocator) {
|
||||
this.serviceInstanceEndpointLocator = serviceInstanceEndpointLocator;
|
||||
this.serverPropertiesEndpointLocator = serverPropertiesEndpointLocator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Endpoint local() {
|
||||
return endpoint();
|
||||
}
|
||||
|
||||
private Endpoint endpoint() {
|
||||
if (this.serviceInstanceEndpointLocator == null) {
|
||||
return this.serverPropertiesEndpointLocator.local();
|
||||
}
|
||||
try {
|
||||
return this.serviceInstanceEndpointLocator.local();
|
||||
} catch (Exception e) {
|
||||
log.warn("Exception occurred while trying to fetch the Zipkin process endpoint. Falling back to server properties endpoint locator.", e);
|
||||
return this.serverPropertiesEndpointLocator.local();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.zipkin;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.Flushable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.cloud.sleuth.metric.SpanMetricReporter;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import zipkin.Span;
|
||||
import zipkin.reporter.AsyncReporter;
|
||||
import zipkin.reporter.Encoding;
|
||||
|
||||
/**
|
||||
* Submits spans using Zipkin's {@code POST /spans} endpoint.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public final class HttpZipkinSpanReporter implements ZipkinSpanReporter, Flushable, Closeable {
|
||||
private final RestTemplateSender sender;
|
||||
private final AsyncReporter<Span> delegate;
|
||||
|
||||
/**
|
||||
* @param restTemplate {@link RestTemplate} used for sending requests to Zipkin
|
||||
* @param baseUrl URL of the zipkin query server instance. Like: http://localhost:9411/
|
||||
* @param flushInterval in seconds. 0 implies spans are {@link #flush() flushed} externally.
|
||||
* @param spanMetricReporter service to count number of accepted / dropped spans
|
||||
*/
|
||||
public HttpZipkinSpanReporter(RestTemplate restTemplate, String baseUrl, int flushInterval,
|
||||
SpanMetricReporter spanMetricReporter) {
|
||||
this(restTemplate, baseUrl, flushInterval, spanMetricReporter, Encoding.JSON);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param restTemplate {@link RestTemplate} used for sending requests to Zipkin
|
||||
* @param baseUrl URL of the zipkin query server instance. Like: http://localhost:9411/
|
||||
* @param flushInterval in seconds. 0 implies spans are {@link #flush() flushed} externally.
|
||||
* @param spanMetricReporter service to count number of accepted / dropped spans
|
||||
* @param encoding span encoding.
|
||||
*/
|
||||
public HttpZipkinSpanReporter(RestTemplate restTemplate, String baseUrl, int flushInterval,
|
||||
SpanMetricReporter spanMetricReporter, Encoding encoding) {
|
||||
this.sender = new RestTemplateSender(restTemplate, baseUrl, encoding);
|
||||
this.delegate = AsyncReporter.builder(this.sender)
|
||||
.queuedMaxSpans(1000) // historical constraint. Note: AsyncReporter supports memory bounds
|
||||
.messageTimeout(flushInterval, TimeUnit.SECONDS)
|
||||
.metrics(new ReporterMetricsAdapter(spanMetricReporter))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues the span for collection, or drops it if the queue is full.
|
||||
*
|
||||
* @param span Span, should not be <code>null</code>.
|
||||
*/
|
||||
@Override
|
||||
public void report(Span span) {
|
||||
this.delegate.report(span);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling this will flush any pending spans to the http transport on the current thread.
|
||||
*/
|
||||
@Override
|
||||
public void flush() {
|
||||
this.delegate.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks until in-flight spans are sent and drops any that are left pending.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
this.delegate.close();
|
||||
this.sender.close();
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.zipkin;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
|
||||
class LoadBalancerClientZipkinLoadBalancer implements ZipkinLoadBalancer {
|
||||
|
||||
private final LoadBalancerClient loadBalancerClient;
|
||||
private final ZipkinProperties zipkinProperties;
|
||||
|
||||
LoadBalancerClientZipkinLoadBalancer(LoadBalancerClient loadBalancerClient,
|
||||
ZipkinProperties zipkinProperties) {
|
||||
this.loadBalancerClient = loadBalancerClient;
|
||||
this.zipkinProperties = zipkinProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI instance() {
|
||||
if (this.loadBalancerClient != null) {
|
||||
URI uri = URI.create(this.zipkinProperties.getBaseUrl());
|
||||
String host = uri.getHost();
|
||||
ServiceInstance instance = this.loadBalancerClient.choose(host);
|
||||
if (instance != null) {
|
||||
return URI.create(String.format("http://%s:%s", instance.getHost(), instance.getPort()));
|
||||
}
|
||||
}
|
||||
return URI.create(this.zipkinProperties.getBaseUrl());
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.zipkin;
|
||||
|
||||
import org.springframework.cloud.sleuth.metric.SpanMetricReporter;
|
||||
|
||||
import zipkin.reporter.ReporterMetrics;
|
||||
|
||||
final class ReporterMetricsAdapter implements ReporterMetrics {
|
||||
private final SpanMetricReporter spanMetricReporter;
|
||||
|
||||
public ReporterMetricsAdapter(SpanMetricReporter spanMetricReporter) {
|
||||
this.spanMetricReporter = spanMetricReporter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementMessages() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementMessagesDropped(Throwable throwable) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementSpans(int i) {
|
||||
this.spanMetricReporter.incrementAcceptedSpans(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementSpanBytes(int i) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementMessageBytes(int i) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementSpansDropped(int i) {
|
||||
this.spanMetricReporter.incrementDroppedSpans(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateQueuedSpans(int i) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateQueuedBytes(int i) {
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.zipkin;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import zipkin.reporter.BytesMessageEncoder;
|
||||
import zipkin.reporter.Callback;
|
||||
import zipkin.reporter.Encoding;
|
||||
import zipkin.reporter.Sender;
|
||||
|
||||
final class RestTemplateSender implements Sender {
|
||||
final RestTemplate restTemplate;
|
||||
final String url;
|
||||
|
||||
final Encoding encoding;
|
||||
final MediaType mediaType;
|
||||
|
||||
RestTemplateSender(RestTemplate restTemplate, String baseUrl, Encoding encoding) {
|
||||
this.restTemplate = restTemplate;
|
||||
this.url = baseUrl + (baseUrl.endsWith("/") ? "" : "/") + "api/v1/spans";
|
||||
this.encoding = encoding;
|
||||
this.mediaType = mediaType(encoding);
|
||||
}
|
||||
|
||||
@Override public Encoding encoding() {
|
||||
return this.encoding;
|
||||
}
|
||||
|
||||
@Override public int messageMaxBytes() {
|
||||
// This will drop a span larger than 5MiB. Note: values like 512KiB benchmark better.
|
||||
return 5 * 1024 * 1024;
|
||||
}
|
||||
|
||||
@Override public int messageSizeInBytes(List<byte[]> spans) {
|
||||
return encoding().listSizeInBytes(spans);
|
||||
}
|
||||
|
||||
/** close is typically called from a different thread */
|
||||
transient boolean closeCalled;
|
||||
|
||||
@Override public void sendSpans(List<byte[]> encodedSpans, Callback callback) {
|
||||
if (this.closeCalled) throw new IllegalStateException("close");
|
||||
try {
|
||||
byte[] message = BytesMessageEncoder.forEncoding(this.encoding).encode(encodedSpans);
|
||||
post(message);
|
||||
callback.onComplete();
|
||||
} catch (Throwable e) {
|
||||
callback.onError(e);
|
||||
if (e instanceof Error) throw (Error) e;
|
||||
}
|
||||
}
|
||||
|
||||
/** Sends an empty json message to the configured endpoint. */
|
||||
@Override public CheckResult check() {
|
||||
try {
|
||||
post(new byte[] {'[', ']'});
|
||||
return CheckResult.OK;
|
||||
} catch (Exception e) {
|
||||
return CheckResult.failed(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void close() {
|
||||
this.closeCalled = true;
|
||||
}
|
||||
|
||||
void post(byte[] json) {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setContentType(this.mediaType);
|
||||
RequestEntity<byte[]> requestEntity =
|
||||
new RequestEntity<>(json, httpHeaders, HttpMethod.POST, URI.create(this.url));
|
||||
this.restTemplate.exchange(requestEntity, String.class);
|
||||
}
|
||||
|
||||
private MediaType mediaType(Encoding encoding) {
|
||||
MediaType mediaType = null;
|
||||
switch (this.encoding) {
|
||||
case JSON:
|
||||
mediaType = MediaType.APPLICATION_JSON;
|
||||
break;
|
||||
case THRIFT:
|
||||
mediaType = new MediaType("application","x-thrift");
|
||||
}
|
||||
return mediaType;
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 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.zipkin;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent;
|
||||
import org.springframework.cloud.commons.util.InetUtils;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.StringUtils;
|
||||
import zipkin.Endpoint;
|
||||
|
||||
/**
|
||||
* {@link EndpointLocator} implementation that:
|
||||
*
|
||||
* <ul>
|
||||
* <li><b>address</b> - from {@link ServerProperties}</li>
|
||||
* <li><b>port</b> - from lazily assigned port or {@link ServerProperties}</li>
|
||||
* </ul>
|
||||
*
|
||||
* You can override the name using {@link ZipkinProperties.Service#setName(String)}
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class ServerPropertiesEndpointLocator implements EndpointLocator {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
private static final String IP_ADDRESS_PROP_NAME = "spring.cloud.client.ipAddress";
|
||||
|
||||
private final ServerProperties serverProperties;
|
||||
private final ZipkinProperties zipkinProperties;
|
||||
private final Environment environment;
|
||||
private Integer port;
|
||||
|
||||
public ServerPropertiesEndpointLocator(ServerProperties serverProperties,
|
||||
Environment environment, ZipkinProperties zipkinProperties) {
|
||||
this.serverProperties = serverProperties;
|
||||
this.environment = environment;
|
||||
this.zipkinProperties = zipkinProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Endpoint local() {
|
||||
String serviceName = serviceName();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Span will contain serviceName [" + serviceName + "]");
|
||||
}
|
||||
return Endpoint.builder()
|
||||
.serviceName(serviceName)
|
||||
.ipv4(getAddress())
|
||||
.port(getPort())
|
||||
.build();
|
||||
}
|
||||
|
||||
private String serviceName() {
|
||||
if (StringUtils.hasText(this.zipkinProperties.getService().getName())) {
|
||||
return this.zipkinProperties.getService().getName();
|
||||
}
|
||||
return this.environment.getProperty("spring.application.name", "unknown");
|
||||
}
|
||||
|
||||
@EventListener(ServletWebServerInitializedEvent.class)
|
||||
public void onApplicationEvent(ServletWebServerInitializedEvent event) {
|
||||
this.port = event.getSource().getPort();
|
||||
}
|
||||
|
||||
private Integer getPort() {
|
||||
if (this.port!=null) {
|
||||
return this.port;
|
||||
}
|
||||
Integer port;
|
||||
if (this.serverProperties!=null && this.serverProperties.getPort() != null && this.serverProperties.getPort() > 0) {
|
||||
port = this.serverProperties.getPort();
|
||||
}
|
||||
else {
|
||||
port = 8080;
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
private int getAddress() {
|
||||
if (this.serverProperties != null && this.serverProperties.getAddress() != null) {
|
||||
return ByteBuffer.wrap(this.serverProperties.getAddress().getAddress())
|
||||
.getInt();
|
||||
}
|
||||
String ipAddress = this.environment
|
||||
.getProperty(IP_ADDRESS_PROP_NAME, String.class);
|
||||
return InetUtils.getIpAddressAsInt(ipAddress);
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 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.zipkin;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.commons.util.InetUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import zipkin.Endpoint;
|
||||
|
||||
/**
|
||||
* An {@link EndpointLocator} that tries to find local service information from a
|
||||
* {@link org.springframework.cloud.client.serviceregistry.Registration}.
|
||||
*
|
||||
* You can override the name using {@link ZipkinProperties.Service#setName(String)}
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class ServiceInstanceEndpointLocator implements EndpointLocator {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
|
||||
private final ServiceInstance localServiceInstance;
|
||||
private final ZipkinProperties zipkinProperties;
|
||||
|
||||
public ServiceInstanceEndpointLocator(ServiceInstance localServiceInstance,
|
||||
ZipkinProperties zipkinProperties) {
|
||||
this.localServiceInstance = localServiceInstance;
|
||||
this.zipkinProperties = zipkinProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Endpoint local() {
|
||||
if (this.localServiceInstance == null) {
|
||||
throw new NoServiceInstanceAvailableException();
|
||||
}
|
||||
String serviceName = StringUtils.hasText(this.zipkinProperties.getService().getName()) ?
|
||||
this.zipkinProperties.getService().getName() : this.localServiceInstance.getServiceId();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Span will contain serviceName [" + serviceName + "]");
|
||||
}
|
||||
return Endpoint.builder()
|
||||
.serviceName(serviceName)
|
||||
.ipv4(getIpAddress(this.localServiceInstance))
|
||||
.port(this.localServiceInstance.getPort()).build();
|
||||
}
|
||||
|
||||
private int getIpAddress(ServiceInstance instance) {
|
||||
try {
|
||||
return InetUtils.getIpAddressAsInt(instance.getHost());
|
||||
}
|
||||
catch (Exception e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static class NoServiceInstanceAvailableException extends RuntimeException { }
|
||||
}
|
||||
@@ -1,269 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 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.zipkin;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.client.serviceregistry.Registration;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.SpanAdjuster;
|
||||
import org.springframework.cloud.sleuth.SpanReporter;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.metric.SpanMetricReporter;
|
||||
import org.springframework.cloud.sleuth.sampler.PercentageBasedSampler;
|
||||
import org.springframework.cloud.sleuth.sampler.SamplerProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.client.RequestCallback;
|
||||
import org.springframework.web.client.ResponseExtractor;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
|
||||
* enables reporting to Zipkin via HTTP. Has a default {@link Sampler} set as
|
||||
* {@link PercentageBasedSampler}.
|
||||
*
|
||||
* The {@link ZipkinRestTemplateCustomizer} allows you to customize the {@link RestTemplate}
|
||||
* that is used to send Spans to Zipkin. Its default implementation - {@link DefaultZipkinRestTemplateCustomizer}
|
||||
* adds the GZip compression.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @see PercentageBasedSampler
|
||||
* @see ZipkinRestTemplateCustomizer
|
||||
* @see DefaultZipkinRestTemplateCustomizer
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({ZipkinProperties.class, SamplerProperties.class})
|
||||
@ConditionalOnProperty(value = "spring.zipkin.enabled", matchIfMissing = true)
|
||||
@AutoConfigureBefore(TraceAutoConfiguration.class)
|
||||
public class ZipkinAutoConfiguration {
|
||||
|
||||
@Autowired(required = false) List<SpanAdjuster> spanAdjusters = new ArrayList<>();
|
||||
@Autowired ZipkinUrlExtractor extractor;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ZipkinSpanReporter reporter(SpanMetricReporter spanMetricReporter, ZipkinProperties zipkin,
|
||||
ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer) {
|
||||
RestTemplate restTemplate = zipkinRestTemplate(zipkin);
|
||||
zipkinRestTemplateCustomizer.customize(restTemplate);
|
||||
return new HttpZipkinSpanReporter(restTemplate, zipkin.getBaseUrl(), zipkin.getFlushInterval(),
|
||||
spanMetricReporter, zipkin.getEncoding());
|
||||
}
|
||||
|
||||
private RestTemplate zipkinRestTemplate(ZipkinProperties zipkinProperties) {
|
||||
return new ZipkinRestTemplateWrapper(zipkinProperties, this.extractor);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(LoadBalancerClient.class)
|
||||
static class DiscoveryClientZipkinUrlExtractorConfiguration {
|
||||
|
||||
@Autowired(required = false) LoadBalancerClient client;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
ZipkinLoadBalancer loadBalancerClientZipkinLoadBalancer(ZipkinProperties zipkinProperties) {
|
||||
return new LoadBalancerClientZipkinLoadBalancer(this.client, zipkinProperties);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingClass("org.springframework.cloud.client.loadbalancer.LoadBalancerClient")
|
||||
static class DefaultZipkinUrlExtractorConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
ZipkinLoadBalancer loadBalancerClientZipkinLoadBalancer(final ZipkinProperties zipkinProperties) {
|
||||
return new ZipkinLoadBalancer() {
|
||||
@Override public URI instance() {
|
||||
return URI.create(zipkinProperties.getBaseUrl());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
ZipkinUrlExtractor zipkinUrlExtractor(final ZipkinLoadBalancer zipkinLoadBalancer) {
|
||||
return new ZipkinUrlExtractor() {
|
||||
@Override
|
||||
public URI zipkinUrl(ZipkinProperties zipkinProperties) {
|
||||
return zipkinLoadBalancer.instance();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer(ZipkinProperties zipkinProperties) {
|
||||
return new DefaultZipkinRestTemplateCustomizer(zipkinProperties);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(RefreshScope.class)
|
||||
protected static class RefreshScopedPercentageBasedSamplerConfiguration {
|
||||
@Bean
|
||||
@RefreshScope
|
||||
@ConditionalOnMissingBean
|
||||
public Sampler defaultTraceSampler(SamplerProperties config) {
|
||||
return new PercentageBasedSampler(config);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingClass("org.springframework.cloud.context.config.annotation.RefreshScope")
|
||||
protected static class NonRefreshScopePercentageBasedSamplerConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Sampler defaultTraceSampler(SamplerProperties config) {
|
||||
return new PercentageBasedSampler(config);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpanReporter zipkinSpanListener(ZipkinSpanReporter reporter, EndpointLocator endpointLocator,
|
||||
Environment environment) {
|
||||
return new ZipkinSpanListener(reporter, endpointLocator, environment, this.spanAdjusters);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(EndpointLocator.class)
|
||||
@ConditionalOnProperty(value = "spring.zipkin.locator.discovery.enabled", havingValue = "false", matchIfMissing = true)
|
||||
protected static class DefaultEndpointLocatorConfiguration {
|
||||
|
||||
@Autowired(required=false)
|
||||
private ServerProperties serverProperties;
|
||||
|
||||
@Autowired
|
||||
private ZipkinProperties zipkinProperties;
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
@Bean
|
||||
public EndpointLocator zipkinEndpointLocator() {
|
||||
return new ServerPropertiesEndpointLocator(this.serverProperties, this.environment,
|
||||
this.zipkinProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(DiscoveryClient.class)
|
||||
@ConditionalOnMissingBean(EndpointLocator.class)
|
||||
@ConditionalOnProperty(value = "spring.zipkin.locator.discovery.enabled", havingValue = "true")
|
||||
protected static class DiscoveryClientEndpointLocatorConfiguration {
|
||||
|
||||
@Autowired(required=false)
|
||||
private ServerProperties serverProperties;
|
||||
|
||||
@Autowired
|
||||
private ZipkinProperties zipkinProperties;
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
@Autowired(required=false)
|
||||
private Registration registration;
|
||||
|
||||
@Bean
|
||||
public EndpointLocator zipkinEndpointLocator() {
|
||||
return new FallbackHavingEndpointLocator(discoveryClientEndpointLocator(),
|
||||
new ServerPropertiesEndpointLocator(this.serverProperties, this.environment,
|
||||
this.zipkinProperties));
|
||||
}
|
||||
|
||||
private ServiceInstanceEndpointLocator discoveryClientEndpointLocator() {
|
||||
if (this.registration != null) {
|
||||
return new ServiceInstanceEndpointLocator(this.registration, this.zipkinProperties);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal interface to provide a way to retrieve Zipkin URI. If there's no discovery client
|
||||
* then this value will be taken from the properties. Otherwise host will be assumed to
|
||||
* be a service id.
|
||||
*/
|
||||
interface ZipkinUrlExtractor {
|
||||
URI zipkinUrl(ZipkinProperties zipkinProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves at runtime where the Zipkin server is. If there's no discovery client then
|
||||
* {@link URI} from the properties is taken. Otherwise service discovery is pinged
|
||||
* for current Zipkin address.
|
||||
*/
|
||||
class ZipkinRestTemplateWrapper extends RestTemplate {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ZipkinRestTemplateWrapper.class);
|
||||
|
||||
private final ZipkinProperties zipkinProperties;
|
||||
private final ZipkinUrlExtractor extractor;
|
||||
|
||||
ZipkinRestTemplateWrapper(ZipkinProperties zipkinProperties,
|
||||
ZipkinUrlExtractor extractor) {
|
||||
this.zipkinProperties = zipkinProperties;
|
||||
this.extractor = extractor;
|
||||
}
|
||||
|
||||
@Override protected <T> T doExecute(URI originalUrl, HttpMethod method,
|
||||
RequestCallback requestCallback,
|
||||
ResponseExtractor<T> responseExtractor) throws RestClientException {
|
||||
URI uri = this.extractor.zipkinUrl(this.zipkinProperties);
|
||||
URI newUri = resolvedZipkinUri(originalUrl, uri);
|
||||
return super.doExecute(newUri, method, requestCallback, responseExtractor);
|
||||
}
|
||||
|
||||
private URI resolvedZipkinUri(URI originalUrl, URI resolvedZipkinUri) {
|
||||
try {
|
||||
return new URI(resolvedZipkinUri.getScheme(), resolvedZipkinUri.getUserInfo(),
|
||||
resolvedZipkinUri.getHost(), resolvedZipkinUri.getPort(), originalUrl.getPath(),
|
||||
originalUrl.getQuery(), originalUrl.getFragment());
|
||||
} catch (URISyntaxException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Failed to create the new URI from original [" + originalUrl + "] and new one [" + resolvedZipkinUri + "]");
|
||||
}
|
||||
return originalUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.zipkin;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* Load balancing strategy for picking a Zipkin instance
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.3.0
|
||||
* @deprecated Please use spring-cloud-sleuth-zipkin2 to report spans to Zipkin
|
||||
*/
|
||||
@Deprecated
|
||||
public interface ZipkinLoadBalancer {
|
||||
|
||||
/**
|
||||
* Returns a concrete {@link URI} of a Zipkin instance.
|
||||
*
|
||||
* @return {@link URI} of the picked instance
|
||||
*/
|
||||
URI instance();
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.zipkin;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import zipkin.reporter.Encoding;
|
||||
|
||||
/**
|
||||
* Zipkin settings
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@ConfigurationProperties("spring.zipkin")
|
||||
public class ZipkinProperties {
|
||||
/** URL of the zipkin query server instance. You can also provide
|
||||
* the service id of the Zipkin server if Zipkin's registered in
|
||||
* service discovery (e.g. http://zipkinserver/)
|
||||
*/
|
||||
private String baseUrl = "http://localhost:9411/";
|
||||
/**
|
||||
* Enables sending spans to Zipkin
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
/**
|
||||
* Interval in seconds in which spans will be sent in batches to Zipkin
|
||||
*/
|
||||
private int flushInterval = 1;
|
||||
/**
|
||||
* Encoding type of spans sent to Zipkin
|
||||
*/
|
||||
private Encoding encoding = Encoding.JSON;
|
||||
/**
|
||||
* Configuration related to compressions of spans sent to Zipkin
|
||||
*/
|
||||
private Compression compression = new Compression();
|
||||
|
||||
private Service service = new Service();
|
||||
|
||||
private Locator locator = new Locator();
|
||||
|
||||
public Locator getLocator() {
|
||||
return this.locator;
|
||||
}
|
||||
|
||||
public String getBaseUrl() {
|
||||
return this.baseUrl;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public int getFlushInterval() {
|
||||
return this.flushInterval;
|
||||
}
|
||||
|
||||
public Compression getCompression() {
|
||||
return this.compression;
|
||||
}
|
||||
|
||||
public Service getService() {
|
||||
return this.service;
|
||||
}
|
||||
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public void setFlushInterval(int flushInterval) {
|
||||
this.flushInterval = flushInterval;
|
||||
}
|
||||
|
||||
public void setCompression(Compression compression) {
|
||||
this.compression = compression;
|
||||
}
|
||||
|
||||
public void setService(Service service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
public void setLocator(Locator locator) {
|
||||
this.locator = locator;
|
||||
}
|
||||
|
||||
public Encoding getEncoding() {
|
||||
return this.encoding;
|
||||
}
|
||||
|
||||
public void setEncoding(Encoding encoding) {
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
/** When enabled, spans are gzipped before sent to the zipkin server */
|
||||
public static class Compression {
|
||||
|
||||
private boolean enabled = false;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
/** When set will override the default {@code spring.application.name} value of the service id */
|
||||
public static class Service {
|
||||
|
||||
/** The name of the service, from which the Span was sent via HTTP, that should appear in Zipkin */
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
/** Configuration related to locating of the host name from service discovery.
|
||||
* This property is NOT related to finding Zipkin via Service Disovery.
|
||||
* To do so use the {@link ZipkinProperties#baseUrl} property with the
|
||||
* service name set inside the URL.
|
||||
*/
|
||||
public static class Locator {
|
||||
|
||||
private Discovery discovery;
|
||||
|
||||
public Discovery getDiscovery() {
|
||||
return this.discovery;
|
||||
}
|
||||
|
||||
public void setDiscovery(Discovery discovery) {
|
||||
this.discovery = discovery;
|
||||
}
|
||||
|
||||
public static class Discovery {
|
||||
|
||||
/** Enabling of locating the host name via service discovery */
|
||||
private boolean enabled;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* 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.zipkin;
|
||||
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Implementations customize the {@link RestTemplate} used to report spans to Zipkin.
|
||||
* For example, they can add an additional header needed by their environment.
|
||||
*
|
||||
* <p>Implementors must gzip according to {@link ZipkinProperties.Compression},
|
||||
* for example by using the {@link DefaultZipkinRestTemplateCustomizer}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.1.0
|
||||
* @deprecated Please use spring-cloud-sleuth-zipkin2 to report spans to Zipkin
|
||||
*/
|
||||
@Deprecated
|
||||
public interface ZipkinRestTemplateCustomizer {
|
||||
|
||||
void customize(RestTemplate restTemplate);
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.zipkin;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.commons.util.IdUtils;
|
||||
import org.springframework.cloud.sleuth.Log;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanAdjuster;
|
||||
import org.springframework.cloud.sleuth.SpanReporter;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.StringUtils;
|
||||
import zipkin.Annotation;
|
||||
import zipkin.BinaryAnnotation;
|
||||
import zipkin.Constants;
|
||||
import zipkin.Endpoint;
|
||||
|
||||
/**
|
||||
* Listener of Sleuth events. Reports to Zipkin via {@link ZipkinSpanReporter}.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class ZipkinSpanListener implements SpanReporter {
|
||||
private static final List<String> ZIPKIN_START_EVENTS = Arrays.asList(
|
||||
Constants.CLIENT_RECV, Constants.SERVER_RECV
|
||||
);
|
||||
private static final List<String> RPC_EVENTS = Arrays.asList(
|
||||
Constants.CLIENT_RECV, Constants.CLIENT_SEND, Constants.SERVER_RECV, Constants.SERVER_SEND
|
||||
);
|
||||
|
||||
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
|
||||
.getLog(ZipkinSpanListener.class);
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
private static final byte[] UNKNOWN_BYTES = "unknown".getBytes(UTF_8);
|
||||
|
||||
private final ZipkinSpanReporter reporter;
|
||||
private final Environment environment;
|
||||
private final List<SpanAdjuster> spanAdjusters;
|
||||
/**
|
||||
* Endpoint is the visible IP address of this service, the port it is listening on and
|
||||
* the service name from discovery.
|
||||
*/
|
||||
// Visible for testing
|
||||
final EndpointLocator endpointLocator;
|
||||
|
||||
public ZipkinSpanListener(ZipkinSpanReporter reporter, EndpointLocator endpointLocator,
|
||||
Environment environment, List<SpanAdjuster> spanAdjusters) {
|
||||
this.reporter = reporter;
|
||||
this.endpointLocator = endpointLocator;
|
||||
this.environment = environment;
|
||||
this.spanAdjusters = spanAdjusters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a given Sleuth span to a Zipkin Span.
|
||||
* <ul>
|
||||
* <li>Set ids, etc
|
||||
* <li>Create timeline annotations based on data from Span object.
|
||||
* <li>Create binary annotations based on data from Span object.
|
||||
* </ul>
|
||||
*
|
||||
* When logging {@link Constants#CLIENT_SEND}, instrumentation should also log the {@link Constants#SERVER_ADDR}
|
||||
* Check <a href="https://github.com/openzipkin/zipkin-java/blob/master/zipkin/src/main/java/zipkin/Constants.java#L28">Zipkin code</a>
|
||||
* for more information
|
||||
*/
|
||||
// Visible for testing
|
||||
zipkin.Span convert(Span span) {
|
||||
//TODO: Consider adding support for the debug flag (related to #496)
|
||||
Span convertedSpan = span;
|
||||
for (SpanAdjuster adjuster : this.spanAdjusters) {
|
||||
convertedSpan = adjuster.adjust(convertedSpan);
|
||||
}
|
||||
zipkin.Span.Builder zipkinSpan = zipkin.Span.builder();
|
||||
Endpoint endpoint = this.endpointLocator.local();
|
||||
processLogs(convertedSpan, zipkinSpan, endpoint);
|
||||
addZipkinAnnotations(zipkinSpan, convertedSpan, endpoint);
|
||||
addZipkinBinaryAnnotations(zipkinSpan, convertedSpan, endpoint);
|
||||
// In the RPC span model, the client owns the timestamp and duration of the span. If we
|
||||
// were propagated an id, we can assume that we shouldn't report timestamp or duration,
|
||||
// rather let the client do that. Worst case we were propagated an unreported ID and
|
||||
// Zipkin backfills timestamp and duration.
|
||||
if (!convertedSpan.isRemote()) {
|
||||
// don't report server-side timestamp on shared spans
|
||||
if (Boolean.TRUE.equals(convertedSpan.isShared())) {
|
||||
zipkinSpan.timestamp(null).duration(null);
|
||||
} else {
|
||||
zipkinSpan.timestamp(convertedSpan.getBegin() * 1000L);
|
||||
if (!convertedSpan.isRunning()) { // duration is authoritative, only write when the span stopped
|
||||
zipkinSpan.duration(calculateDurationInMicros(convertedSpan));
|
||||
}
|
||||
}
|
||||
}
|
||||
zipkinSpan.traceIdHigh(convertedSpan.getTraceIdHigh());
|
||||
zipkinSpan.traceId(convertedSpan.getTraceId());
|
||||
if (convertedSpan.getParents().size() > 0) {
|
||||
if (convertedSpan.getParents().size() > 1) {
|
||||
log.error("Zipkin doesn't support spans with multiple parents. Omitting "
|
||||
+ "other parents for " + convertedSpan);
|
||||
}
|
||||
zipkinSpan.parentId(convertedSpan.getParents().get(0));
|
||||
}
|
||||
zipkinSpan.id(convertedSpan.getSpanId());
|
||||
if (StringUtils.hasText(convertedSpan.getName())) {
|
||||
zipkinSpan.name(convertedSpan.getName());
|
||||
}
|
||||
return zipkinSpan.build();
|
||||
}
|
||||
|
||||
private void ensureLocalComponent(Span span, zipkin.Span.Builder zipkinSpan, Endpoint localEndpoint) {
|
||||
if (span.tags().containsKey(Constants.LOCAL_COMPONENT)) {
|
||||
return;
|
||||
}
|
||||
byte[] processId = span.getProcessId() != null
|
||||
? span.getProcessId().toLowerCase().getBytes(UTF_8)
|
||||
: UNKNOWN_BYTES;
|
||||
BinaryAnnotation component = BinaryAnnotation.builder()
|
||||
.type(BinaryAnnotation.Type.STRING)
|
||||
.key("lc") // LOCAL_COMPONENT
|
||||
.value(processId)
|
||||
.endpoint(localEndpoint).build();
|
||||
zipkinSpan.addBinaryAnnotation(component);
|
||||
}
|
||||
|
||||
private void ensureServerAddr(Span span, zipkin.Span.Builder zipkinSpan) {
|
||||
if (span.tags().containsKey(Span.SPAN_PEER_SERVICE_TAG_NAME)) {
|
||||
zipkinSpan.addBinaryAnnotation(BinaryAnnotation.address(Constants.SERVER_ADDR,
|
||||
Endpoint.builder().serviceName(
|
||||
span.tags().get(Span.SPAN_PEER_SERVICE_TAG_NAME)).build()));
|
||||
}
|
||||
}
|
||||
|
||||
// Instead of going through the list of logs multiple times we're doing it only once
|
||||
private void processLogs(Span span, zipkin.Span.Builder zipkinSpan, Endpoint endpoint) {
|
||||
boolean notClientOrServer = true;
|
||||
boolean hasClientSend = false;
|
||||
boolean instanceIdToTag = false;
|
||||
for (Log log : span.logs()) {
|
||||
if (RPC_EVENTS.contains(log.getEvent())) {
|
||||
instanceIdToTag = true;
|
||||
}
|
||||
if (ZIPKIN_START_EVENTS.contains(log.getEvent())) {
|
||||
notClientOrServer = false;
|
||||
}
|
||||
if (Constants.CLIENT_SEND.equals(log.getEvent())) {
|
||||
hasClientSend = !span.tags().containsKey(Constants.SERVER_ADDR);
|
||||
}
|
||||
}
|
||||
if (notClientOrServer) {
|
||||
// A zipkin span without any annotations cannot be queried, add special "lc" to avoid that.
|
||||
ensureLocalComponent(span, zipkinSpan, endpoint);
|
||||
}
|
||||
if (hasClientSend) {
|
||||
ensureServerAddr(span, zipkinSpan);
|
||||
}
|
||||
if (instanceIdToTag && this.environment != null) {
|
||||
setInstanceIdIfPresent(zipkinSpan, endpoint, Span.INSTANCEID);
|
||||
}
|
||||
}
|
||||
|
||||
private void setInstanceIdIfPresent(zipkin.Span.Builder zipkinSpan,
|
||||
Endpoint endpoint, String key) {
|
||||
String property = IdUtils.getDefaultInstanceId(this.environment);
|
||||
if (StringUtils.hasText(property)) {
|
||||
addZipkinBinaryAnnotation(key, property, endpoint, zipkinSpan);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add annotations from the sleuth Span.
|
||||
*/
|
||||
private void addZipkinAnnotations(zipkin.Span.Builder zipkinSpan,
|
||||
Span span, Endpoint endpoint) {
|
||||
for (Log ta : span.logs()) {
|
||||
Annotation zipkinAnnotation = Annotation.builder()
|
||||
.endpoint(endpoint)
|
||||
.timestamp(ta.getTimestamp() * 1000) // Zipkin is in microseconds
|
||||
.value(ta.getEvent()).build();
|
||||
zipkinSpan.addAnnotation(zipkinAnnotation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds binary annotation from the sleuth Span
|
||||
*/
|
||||
private void addZipkinBinaryAnnotations(zipkin.Span.Builder zipkinSpan,
|
||||
Span span, Endpoint ep) {
|
||||
for (Map.Entry<String, String> e : span.tags().entrySet()) {
|
||||
addZipkinBinaryAnnotation(e.getKey(), e.getValue(), ep, zipkinSpan);
|
||||
}
|
||||
}
|
||||
|
||||
private void addZipkinBinaryAnnotation(String key, String value, Endpoint ep,
|
||||
zipkin.Span.Builder zipkinSpan) {
|
||||
BinaryAnnotation binaryAnn = BinaryAnnotation.builder()
|
||||
.type(BinaryAnnotation.Type.STRING)
|
||||
.key(key)
|
||||
.value(value.getBytes(UTF_8))
|
||||
.endpoint(ep).build();
|
||||
zipkinSpan.addBinaryAnnotation(binaryAnn);
|
||||
}
|
||||
|
||||
/**
|
||||
* There could be instrumentation delay between span creation and the
|
||||
* semantic start of the span (client send). When there's a difference,
|
||||
* spans look confusing. Ex users expect duration to be client
|
||||
* receive - send, but it is a little more than that. Rather than have
|
||||
* to teach each user about the possibility of instrumentation overhead,
|
||||
* we truncate absolute duration (span finish - create) to semantic
|
||||
* duration (client receive - send)
|
||||
*/
|
||||
private long calculateDurationInMicros(Span span) {
|
||||
Log clientSend = hasLog(Span.CLIENT_SEND, span);
|
||||
Log clientReceived = hasLog(Span.CLIENT_RECV, span);
|
||||
if (clientSend != null && clientReceived != null) {
|
||||
return (clientReceived.getTimestamp() - clientSend.getTimestamp()) * 1000;
|
||||
}
|
||||
return span.getAccumulatedMicros();
|
||||
}
|
||||
|
||||
private Log hasLog(String logName, Span span) {
|
||||
for (Log log : span.logs()) {
|
||||
if (logName.equals(log.getEvent())) {
|
||||
return log;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(Span span) {
|
||||
if (span.isExportable()) {
|
||||
this.reporter.report(convert(span));
|
||||
} else {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("The span " + span + " will not be sent to Zipkin due to sampling");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.zipkin;
|
||||
|
||||
/**
|
||||
* Contract for reporting Zipkin spans to Zipkin.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
* @since 1.0.0
|
||||
* @deprecated Please use spring-cloud-sleuth-zipkin2 to report spans to Zipkin
|
||||
*/
|
||||
@Deprecated
|
||||
public interface ZipkinSpanReporter {
|
||||
/**
|
||||
* Receives completed spans from {@link ZipkinSpanListener} and submits them to a Zipkin
|
||||
* collector.
|
||||
*/
|
||||
void report(zipkin.Span span);
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
# Auto Configuration
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.sleuth.zipkin.ZipkinAutoConfiguration
|
||||
org.springframework.cloud.sleuth.zipkin2.ZipkinAutoConfiguration
|
||||
@@ -1,55 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.zipkin;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import zipkin.Endpoint;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class FallbackHavingEndpointLocatorTests {
|
||||
|
||||
@Mock
|
||||
ServiceInstanceEndpointLocator serviceInstanceEndpointLocator;
|
||||
@Mock ServerPropertiesEndpointLocator serverPropertiesEndpointLocator;
|
||||
Endpoint expectedEndpoint = Endpoint.builder()
|
||||
.serviceName("my-tomcat").ipv4(127 << 24 | 1).port(8080).build();
|
||||
|
||||
@Test
|
||||
public void should_use_system_property_locator_if_discovery_client_locator_is_not_present() {
|
||||
given(this.serverPropertiesEndpointLocator.local()).willReturn(this.expectedEndpoint);
|
||||
FallbackHavingEndpointLocator sut = new FallbackHavingEndpointLocator(null,
|
||||
this.serverPropertiesEndpointLocator);
|
||||
|
||||
Endpoint endpoint = sut.local();
|
||||
|
||||
then(endpoint).isSameAs(this.expectedEndpoint);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_use_system_property_locator_if_discovery_client_locator_throws_an_exception() {
|
||||
given(this.serviceInstanceEndpointLocator.local()).willThrow(new RuntimeException());
|
||||
given(this.serverPropertiesEndpointLocator.local()).willReturn(this.expectedEndpoint);
|
||||
FallbackHavingEndpointLocator sut = new FallbackHavingEndpointLocator(this.serviceInstanceEndpointLocator,
|
||||
this.serverPropertiesEndpointLocator);
|
||||
|
||||
Endpoint endpoint = sut.local();
|
||||
|
||||
then(endpoint).isSameAs(this.expectedEndpoint);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_use_discovery_client_locator_by_default() {
|
||||
given(this.serviceInstanceEndpointLocator.local()).willReturn(this.expectedEndpoint);
|
||||
FallbackHavingEndpointLocator sut = new FallbackHavingEndpointLocator(this.serviceInstanceEndpointLocator,
|
||||
this.serverPropertiesEndpointLocator);
|
||||
|
||||
Endpoint endpoint = sut.local();
|
||||
|
||||
then(endpoint).isSameAs(this.expectedEndpoint);
|
||||
}
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.zipkin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import io.micrometer.core.instrument.Counter;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.log.NoOpSpanLogger;
|
||||
import org.springframework.cloud.sleuth.metric.CounterServiceBasedSpanMetricReporter;
|
||||
import org.springframework.cloud.sleuth.metric.SpanMetricReporter;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.trace.DefaultTracer;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import zipkin.Span;
|
||||
import zipkin.junit.HttpFailure;
|
||||
import zipkin.junit.ZipkinRule;
|
||||
import zipkin.reporter.Encoding;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.awaitility.Awaitility.await;
|
||||
|
||||
public class HttpZipkinSpanReporterTest {
|
||||
|
||||
@Rule public final ZipkinRule zipkin = new ZipkinRule();
|
||||
Counter accepted = counter("accepted");
|
||||
Counter dropped = counter("dropped");
|
||||
|
||||
SpanMetricReporter spanMetricReporter = new CounterServiceBasedSpanMetricReporter(this.accepted, this.dropped);
|
||||
RestTemplate restTemplate = defaultRestTemplate();
|
||||
HttpZipkinSpanReporter reporter = new HttpZipkinSpanReporter(restTemplate, this.zipkin.httpUrl(),
|
||||
0, // so that tests can drive flushing explicitly
|
||||
this.spanMetricReporter
|
||||
);
|
||||
|
||||
@Test
|
||||
public void reportDoesntDoIO() throws Exception {
|
||||
this.reporter.report(span(1L, "foo"));
|
||||
|
||||
assertThat(this.zipkin.httpRequestCount()).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reportIncrementsAcceptedMetrics() throws Exception {
|
||||
this.reporter.report(span(1L, "foo"));
|
||||
|
||||
assertThat(this.accepted.count()).isEqualTo(1);
|
||||
assertThat(this.dropped.count()).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dropsWhenQueueIsFull() throws Exception {
|
||||
for (int i = 0; i < 1001; i++)
|
||||
this.reporter.report(span(1L, "foo"));
|
||||
|
||||
assertThat(this.accepted.count()).isEqualTo(1001);
|
||||
assertThat(this.dropped.count()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postsSpans() throws Exception {
|
||||
this.reporter.report(span(1L, "foo"));
|
||||
this.reporter.report(span(2L, "bar"));
|
||||
|
||||
this.reporter.flush(); // manually flush the spans
|
||||
|
||||
// Ensure only one request was sent
|
||||
assertThat(this.zipkin.httpRequestCount()).isEqualTo(1);
|
||||
|
||||
assertThat(this.zipkin.getTraces()).containsExactly(
|
||||
asList(span(1L, "foo")),
|
||||
asList(span(2L, "bar"))
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postsCompressedSpans() throws Exception {
|
||||
this.reporter = new HttpZipkinSpanReporter(restTemplateWithCompression(), this.zipkin.httpUrl(),
|
||||
0, // so that tests can drive flushing explicitly
|
||||
this.spanMetricReporter
|
||||
);
|
||||
|
||||
this.reporter.report(span(1L, "foo"));
|
||||
this.reporter.report(span(2L, "bar"));
|
||||
|
||||
this.reporter.flush(); // manually flush the spans
|
||||
|
||||
// Ensure only one request was sent
|
||||
assertThat(this.zipkin.httpRequestCount()).isEqualTo(1);
|
||||
|
||||
assertThat(this.zipkin.getTraces()).containsExactly(
|
||||
asList(span(1L, "foo")),
|
||||
asList(span(2L, "bar"))
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void incrementsDroppedSpansWhenServerErrors() throws Exception {
|
||||
this.zipkin.enqueueFailure(HttpFailure.sendErrorResponse(500, "Ouch"));
|
||||
|
||||
this.reporter.report(span(1L, "foo"));
|
||||
this.reporter.report(span(2L, "bar"));
|
||||
|
||||
this.reporter.flush(); // manually flush the spans
|
||||
|
||||
assertThat(this.dropped.count()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void incrementsDroppedSpansWhenServerDisconnects() throws Exception {
|
||||
this.zipkin.enqueueFailure(HttpFailure.disconnectDuringBody());
|
||||
|
||||
this.reporter.report(span(1L, "foo"));
|
||||
this.reporter.report(span(2L, "bar"));
|
||||
|
||||
this.reporter.flush(); // manually flush the spans
|
||||
|
||||
assertThat(this.dropped.count()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_change_the_service_name_in_zipkin_to_the_manually_provided_one() {
|
||||
AtomicReference<Span> receivedSpan = new AtomicReference<>();
|
||||
Tracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(), new DefaultSpanNamer(),
|
||||
new NoOpSpanLogger(), new ZipkinSpanListener(receivedSpan::set,
|
||||
new ServerPropertiesEndpointLocator(new ServerProperties(), new MockEnvironment(),
|
||||
new ZipkinProperties()),
|
||||
null, new ArrayList<>()), new TraceKeys());
|
||||
// tag::service_name[]
|
||||
org.springframework.cloud.sleuth.Span newSpan = tracer.createSpan("redis");
|
||||
try {
|
||||
newSpan.tag("redis.op", "get");
|
||||
newSpan.tag("lc", "redis");
|
||||
newSpan.logEvent(org.springframework.cloud.sleuth.Span.CLIENT_SEND);
|
||||
// call redis service e.g
|
||||
// return (SomeObj) redisTemplate.opsForHash().get("MYHASH", someObjKey);
|
||||
} finally {
|
||||
newSpan.tag("peer.service", "redisService");
|
||||
newSpan.tag("peer.ipv4", "1.2.3.4");
|
||||
newSpan.tag("peer.port", "1234");
|
||||
newSpan.logEvent(org.springframework.cloud.sleuth.Span.CLIENT_RECV);
|
||||
tracer.close(newSpan);
|
||||
}
|
||||
// end::service_name[]
|
||||
|
||||
then(tracer.getCurrentSpan()).isNull();
|
||||
then(ExceptionUtils.getLastException()).isNull();
|
||||
then(receivedSpan.get().binaryAnnotations)
|
||||
.flatExtracting(input -> input.key, input -> new String(input.value))
|
||||
.contains("peer.service", "redisService");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSenderThriftEncoding() {
|
||||
ZipkinProperties zipkinProperties = new ZipkinProperties();
|
||||
zipkinProperties.setEncoding(Encoding.THRIFT);
|
||||
zipkinProperties.setBaseUrl(zipkin.httpUrl());
|
||||
|
||||
HttpZipkinSpanReporter httpZipkinSpanReporter = new HttpZipkinSpanReporter(restTemplate(zipkinProperties)
|
||||
, zipkinProperties.getBaseUrl(), 1, spanMetricReporter, zipkinProperties.getEncoding());
|
||||
|
||||
Tracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(), new DefaultSpanNamer(),
|
||||
new NoOpSpanLogger(),new ZipkinSpanListener(httpZipkinSpanReporter,
|
||||
new ServerPropertiesEndpointLocator(new ServerProperties(), new MockEnvironment(),
|
||||
zipkinProperties),
|
||||
null, Collections.emptyList()), new TraceKeys());
|
||||
|
||||
tracer.close(tracer.createSpan("foo"));
|
||||
httpZipkinSpanReporter.flush();
|
||||
|
||||
await().until(() -> zipkin.getTraces().size() == 1);
|
||||
assertThat(zipkin.getTraces().size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
static Span span(long traceId, String spanName) {
|
||||
return Span.builder().traceId(traceId).id(traceId).name(spanName).build();
|
||||
}
|
||||
|
||||
private RestTemplate restTemplate(ZipkinProperties zipkinProperties) {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
new DefaultZipkinRestTemplateCustomizer(zipkinProperties).customize(restTemplate);
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
private RestTemplate defaultRestTemplate() {
|
||||
return restTemplate(new ZipkinProperties());
|
||||
}
|
||||
|
||||
private RestTemplate restTemplateWithCompression() {
|
||||
ZipkinProperties zipkinProperties = new ZipkinProperties();
|
||||
zipkinProperties.getCompression().setEnabled(true);
|
||||
return restTemplate(zipkinProperties);
|
||||
}
|
||||
|
||||
private Counter counter(final String name) {
|
||||
return new Counter() {
|
||||
private double counter;
|
||||
@Override public void increment(double amount) {
|
||||
this.counter = this.counter + amount;
|
||||
}
|
||||
|
||||
@Override public double count() {
|
||||
return this.counter;
|
||||
}
|
||||
|
||||
@Override public Id getId() {
|
||||
return new Id(name, Collections.emptyList(), "unit", "description");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 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.zipkin;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class ServerPropertiesEndpointLocatorTests {
|
||||
|
||||
public static final byte[] ADDRESS1234 = { 1, 2, 3, 4 };
|
||||
|
||||
@Test
|
||||
public void portDefaultsTo8080() throws UnknownHostException {
|
||||
ServerPropertiesEndpointLocator locator = new ServerPropertiesEndpointLocator(
|
||||
new ServerProperties(), new MockEnvironment(), new ZipkinProperties());
|
||||
|
||||
assertThat(locator.local().port).isEqualTo((short) 8080);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void portFromServerProperties() throws UnknownHostException {
|
||||
ServerProperties properties = new ServerProperties();
|
||||
properties.setPort(1234);
|
||||
|
||||
ServerPropertiesEndpointLocator locator = new ServerPropertiesEndpointLocator(
|
||||
properties, new MockEnvironment(), new ZipkinProperties());
|
||||
|
||||
assertThat(locator.local().port).isEqualTo((short) 1234);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void portDefaultsToLocalhost() throws UnknownHostException {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
environment.setProperty("spring.cloud.client.ipAddress", String.valueOf(1 << 24 | 2 << 16 | 3 << 8 | 4));
|
||||
ServerPropertiesEndpointLocator locator = new ServerPropertiesEndpointLocator(
|
||||
new ServerProperties(), environment, new ZipkinProperties());
|
||||
|
||||
assertThat(locator.local().ipv4).isEqualTo(1 << 24 | 2 << 16 | 3 << 8 | 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hostFromServerPropertiesIp() throws UnknownHostException {
|
||||
ServerProperties properties = new ServerProperties();
|
||||
properties.setAddress(InetAddress.getByAddress(ADDRESS1234));
|
||||
|
||||
ServerPropertiesEndpointLocator locator = new ServerPropertiesEndpointLocator(
|
||||
properties, new MockEnvironment(), new ZipkinProperties());
|
||||
|
||||
assertThat(locator.local().ipv4).isEqualTo(1 << 24 | 2 << 16 | 3 << 8 | 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appNameFromProperties() throws UnknownHostException {
|
||||
ServerProperties properties = new ServerProperties();
|
||||
ZipkinProperties zipkinProperties = new ZipkinProperties();
|
||||
zipkinProperties.getService().setName("foo");
|
||||
|
||||
ServerPropertiesEndpointLocator locator = new ServerPropertiesEndpointLocator(
|
||||
properties, new MockEnvironment(), zipkinProperties);
|
||||
|
||||
assertThat(locator.local().serviceName).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void negativePortFromServerProperties() throws UnknownHostException {
|
||||
ServerProperties properties = new ServerProperties();
|
||||
properties.setPort(-1);
|
||||
|
||||
ServerPropertiesEndpointLocator locator = new ServerPropertiesEndpointLocator(
|
||||
properties, new MockEnvironment(), new ZipkinProperties());
|
||||
|
||||
assertThat(locator.local().port).isEqualTo((short) 8080);
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.zipkin;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.cloud.client.serviceregistry.Registration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Marcin Wielgus
|
||||
*/
|
||||
public class ServiceInstanceEndpointLocatorConfigurationTest {
|
||||
|
||||
@Test
|
||||
public void endpointLocatorShouldDefaultToServerPropertiesEndpointLocator() {
|
||||
ConfigurableApplicationContext ctxt = new SpringApplication(
|
||||
EmptyConfiguration.class).run("--spring.jmx.enabled=false",
|
||||
"--spring.cloud.discovery.client.composite-indicator.enabled=false");
|
||||
assertThat(ctxt.getBean(EndpointLocator.class))
|
||||
.isInstanceOf(ServerPropertiesEndpointLocator.class);
|
||||
ctxt.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void endpointLocatorShouldDefaultToServerPropertiesEndpointLocatorEvenWhenDiscoveryClientPresent() {
|
||||
ConfigurableApplicationContext ctxt = new SpringApplication(
|
||||
ConfigurationWithRegistration.class).run("--spring.jmx.enabled=false",
|
||||
"--spring.cloud.discovery.client.composite-indicator.enabled=false");
|
||||
assertThat(ctxt.getBean(EndpointLocator.class))
|
||||
.isInstanceOf(ServerPropertiesEndpointLocator.class);
|
||||
ctxt.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void endpointLocatorShouldRespectExistingEndpointLocator() {
|
||||
ConfigurableApplicationContext ctxt = new SpringApplication(
|
||||
ConfigurationWithCustomLocator.class).run("--spring.jmx.enabled=false",
|
||||
"--spring.cloud.discovery.client.composite-indicator.enabled=false");
|
||||
assertThat(ctxt.getBean(EndpointLocator.class))
|
||||
.isSameAs(ConfigurationWithCustomLocator.locator);
|
||||
ctxt.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void endpointLocatorShouldBeFallbackHavingEndpointLocatorWhenAskedTo() {
|
||||
ConfigurableApplicationContext ctxt = new SpringApplication(
|
||||
ConfigurationWithRegistration.class).run("--spring.jmx.enabled=false",
|
||||
"--spring.zipkin.locator.discovery.enabled=true",
|
||||
"--spring.cloud.discovery.client.composite-indicator.enabled=false");
|
||||
assertThat(ctxt.getBean(EndpointLocator.class))
|
||||
.isInstanceOf(FallbackHavingEndpointLocator.class);
|
||||
ctxt.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void endpointLocatorShouldRespectExistingEndpointLocatorEvenWhenAskedToBeDiscovery() {
|
||||
ConfigurableApplicationContext ctxt = new SpringApplication(
|
||||
ConfigurationWithRegistration.class,
|
||||
ConfigurationWithCustomLocator.class).run("--spring.jmx.enabled=false",
|
||||
"--spring.zipkin.locator.discovery.enabled=true",
|
||||
"--spring.cloud.discovery.client.composite-indicator.enabled=false");
|
||||
assertThat(ctxt.getBean(EndpointLocator.class))
|
||||
.isSameAs(ConfigurationWithCustomLocator.locator);
|
||||
ctxt.close();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
public static class EmptyConfiguration {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
public static class ConfigurationWithRegistration {
|
||||
@Bean public Registration registration() {
|
||||
return Mockito.mock(Registration.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
public static class ConfigurationWithCustomLocator {
|
||||
static EndpointLocator locator = Mockito.mock(EndpointLocator.class);
|
||||
|
||||
@Bean public EndpointLocator getEndpointLocator() {
|
||||
return locator;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 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.zipkin;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.commons.util.InetUtils;
|
||||
import org.springframework.cloud.sleuth.zipkin.ServiceInstanceEndpointLocator.NoServiceInstanceAvailableException;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
import zipkin.Endpoint;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ServiceInstanceEndpointLocatorTest {
|
||||
|
||||
@Test(expected = NoServiceInstanceAvailableException.class)
|
||||
public void should_throw_exception_when_no_instances_are_available() throws Exception {
|
||||
ServiceInstanceEndpointLocator endpointLocator = endpointLocator(null);
|
||||
endpointLocator.local();
|
||||
}
|
||||
|
||||
private ServiceInstanceEndpointLocator endpointLocator(ServiceInstance serviceInstance) {
|
||||
return endpointLocator(serviceInstance, new ZipkinProperties());
|
||||
}
|
||||
|
||||
private ServiceInstanceEndpointLocator endpointLocator(ServiceInstance serviceInstance, ZipkinProperties zipkinProperties) {
|
||||
return new ServiceInstanceEndpointLocator(serviceInstance, zipkinProperties);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_create_endpoint_with_0_ip_when_exception_occurs_on_resolving_host() throws Exception {
|
||||
ServiceInstanceEndpointLocator endpointLocator = endpointLocator(serviceInstanceWithInvalidHost());
|
||||
|
||||
Endpoint local = endpointLocator.local();
|
||||
|
||||
then(local.serviceName).isEqualTo("serviceid");
|
||||
then(local.port).isEqualTo((short)8_000);
|
||||
then(local.ipv4).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_create_valid_endpoint_when_proper_host_is_passed() throws Exception {
|
||||
ServiceInstanceEndpointLocator endpointLocator = endpointLocator(serviceInstanceWithValidHost());
|
||||
|
||||
Endpoint local = endpointLocator.local();
|
||||
|
||||
then(local.serviceName).isEqualTo("serviceid");
|
||||
then(local.port).isEqualTo((short)8_000);
|
||||
then(local.ipv4).isEqualTo(InetUtils.getIpAddressAsInt("localhost"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_create_endpoint_with_overridden_name() throws Exception {
|
||||
ZipkinProperties zipkinProperties = new ZipkinProperties();
|
||||
zipkinProperties.getService().setName("foo");
|
||||
ServiceInstanceEndpointLocator locator = endpointLocator(serviceInstanceWithValidHost(), zipkinProperties);
|
||||
|
||||
Endpoint local = locator.local();
|
||||
|
||||
then(local.serviceName).isEqualTo("foo");
|
||||
then(local.port).isEqualTo((short)8_000);
|
||||
then(local.ipv4).isEqualTo(InetUtils.getIpAddressAsInt("localhost"));
|
||||
}
|
||||
|
||||
private ServiceInstance serviceInstanceWithInvalidHost() {
|
||||
return new ServiceInstance() {
|
||||
@Override public String getServiceId() {
|
||||
return "serviceId";
|
||||
}
|
||||
|
||||
@Override public String getHost() {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
@Override public int getPort() {
|
||||
return 8000;
|
||||
}
|
||||
|
||||
@Override public boolean isSecure() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public URI getUri() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public Map<String, String> getMetadata() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ServiceInstance serviceInstanceWithValidHost() {
|
||||
return new ServiceInstance() {
|
||||
@Override public String getServiceId() {
|
||||
return "serviceId";
|
||||
}
|
||||
|
||||
@Override public String getHost() {
|
||||
return "localhost";
|
||||
}
|
||||
|
||||
@Override public int getPort() {
|
||||
return 8000;
|
||||
}
|
||||
|
||||
@Override public boolean isSecure() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public URI getUri() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public Map<String, String> getMetadata() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.zipkin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanReporter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import zipkin.junit.ZipkinRule;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = ZipkinDiscoveryClientTests.Config.class, properties = {
|
||||
"spring.zipkin.baseUrl=http://zipkin/",
|
||||
"spring.cloud.discovery.client.composite-indicator.enabled=false" })
|
||||
public class ZipkinDiscoveryClientTests {
|
||||
|
||||
@ClassRule public static ZipkinRule ZIPKIN_RULE = new ZipkinRule();
|
||||
|
||||
@Autowired SpanReporter spanReporter;
|
||||
|
||||
@Test
|
||||
public void shouldUseDiscoveryClientToFindZipkinUrlIfPresent() throws Exception {
|
||||
Span span = Span.builder().traceIdHigh(1L).traceId(2L).spanId(3L).name("foo")
|
||||
.build();
|
||||
|
||||
this.spanReporter.report(span);
|
||||
|
||||
Awaitility.await().untilAsserted(() -> then(ZIPKIN_RULE.httpRequestCount()).isGreaterThan(0));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
static class Config {
|
||||
|
||||
@Bean LoadBalancerClient loadBalancerClient() {
|
||||
return new LoadBalancerClient() {
|
||||
@Override public <T> T execute(String serviceId,
|
||||
LoadBalancerRequest<T> request) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public <T> T execute(String serviceId,
|
||||
ServiceInstance serviceInstance, LoadBalancerRequest<T> request)
|
||||
throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public URI reconstructURI(ServiceInstance instance,
|
||||
URI original) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public ServiceInstance choose(String serviceId) {
|
||||
return new ServiceInstance() {
|
||||
@Override
|
||||
public String getServiceId() {
|
||||
return "zipkin";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return "localhost";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return URI.create(ZIPKIN_RULE.httpUrl()).getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
return URI.create(ZIPKIN_RULE.httpUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMetadata() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,372 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 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.zipkin;
|
||||
|
||||
import org.assertj.core.api.Condition;
|
||||
import zipkin.Constants;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanAdjuster;
|
||||
import org.springframework.cloud.sleuth.SpanReporter;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.zipkin.ZipkinSpanListenerTests.TestConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import zipkin.Endpoint;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@SpringBootTest(classes = TestConfiguration.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
public class ZipkinSpanListenerTests {
|
||||
|
||||
@Autowired Tracer tracer;
|
||||
@Autowired TestConfiguration test;
|
||||
@Autowired ZipkinSpanListener spanListener;
|
||||
@Autowired ZipkinSpanReporter spanReporter;
|
||||
@Autowired MockEnvironment mockEnvironment;
|
||||
@Autowired EndpointLocator endpointLocator;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
this.test.zipkinSpans.clear();
|
||||
}
|
||||
|
||||
Span parent = Span.builder().traceId(1L).name("http:parent").remote(true).build();
|
||||
|
||||
/** Sleuth timestamps are millisecond granularity while zipkin is microsecond. */
|
||||
@Test
|
||||
public void convertsTimestampToMicrosecondsAndSetsDurationToAccumulatedMicros() {
|
||||
Span span = Span.builder().traceId(1L).name("http:api").build();
|
||||
long start = System.currentTimeMillis();
|
||||
span.logEvent("hystrix/retry"); // System.currentTimeMillis
|
||||
span.stop();
|
||||
|
||||
zipkin.Span result = this.spanListener.convert(span);
|
||||
|
||||
assertThat(result.timestamp)
|
||||
.isEqualTo(span.getBegin() * 1000);
|
||||
assertThat(result.duration)
|
||||
.isEqualTo(span.getAccumulatedMicros());
|
||||
assertThat(result.annotations.get(0).timestamp)
|
||||
.isGreaterThanOrEqualTo(start * 1000)
|
||||
.isLessThanOrEqualTo(System.currentTimeMillis() * 1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setsTheDurationToTheDifferenceBetweenCRandCS()
|
||||
throws InterruptedException {
|
||||
Span span = Span.builder().traceId(1L).name("http:api").build();
|
||||
span.logEvent(Span.CLIENT_SEND);
|
||||
Thread.sleep(10);
|
||||
span.logEvent(Span.CLIENT_RECV);
|
||||
Thread.sleep(20);
|
||||
span.stop();
|
||||
|
||||
zipkin.Span result = this.spanListener.convert(span);
|
||||
|
||||
assertThat(result.timestamp).isEqualTo(span.getBegin() * 1000);
|
||||
long clientSendTimestamp = span.logs().stream()
|
||||
.filter(log -> Span.CLIENT_SEND.equals(log.getEvent())).findFirst().get()
|
||||
.getTimestamp();
|
||||
long clientRecvTimestamp = span.logs().stream()
|
||||
.filter(log -> Span.CLIENT_RECV.equals(log.getEvent())).findFirst().get()
|
||||
.getTimestamp();
|
||||
assertThat(result.duration).isNotEqualTo(span.getAccumulatedMicros())
|
||||
.isEqualTo((clientRecvTimestamp - clientSendTimestamp) * 1000);
|
||||
}
|
||||
|
||||
/** Zipkin's duration should only be set when the span is finished. */
|
||||
@Test
|
||||
public void doesntSetDurationWhenStillRunning() {
|
||||
Span span = Span.builder().traceId(1L).name("http:api").build();
|
||||
zipkin.Span result = this.spanListener.convert(span);
|
||||
|
||||
assertThat(result.timestamp)
|
||||
.isGreaterThan(0); // sanity check it did start
|
||||
assertThat(result.duration)
|
||||
.isNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* In the RPC span model, the client owns the timestamp and duration of the span. If
|
||||
* we were propagated an id, we can assume that we shouldn't report timestamp or
|
||||
* duration, rather let the client do that. Worst case we were propagated an
|
||||
* unreported ID and Zipkin backfills timestamp and duration.
|
||||
*/
|
||||
@Test
|
||||
public void doesntSetTimestampOrDurationWhenRemote() {
|
||||
this.parent.stop();
|
||||
zipkin.Span result = this.spanListener.convert(this.parent);
|
||||
|
||||
assertThat(result.timestamp)
|
||||
.isNull();
|
||||
assertThat(result.duration)
|
||||
.isNull();
|
||||
}
|
||||
|
||||
/** Sleuth host corresponds to annotation/binaryAnnotation.host in zipkin. */
|
||||
@Test
|
||||
public void annotationsIncludeHost() {
|
||||
this.parent.logEvent("hystrix/retry");
|
||||
this.parent.tag("spring-boot/version", "1.3.1.RELEASE");
|
||||
|
||||
zipkin.Span result = this.spanListener.convert(this.parent);
|
||||
|
||||
assertThat(result.annotations.get(0).endpoint)
|
||||
.isEqualTo(this.spanListener.endpointLocator.local());
|
||||
assertThat(result.binaryAnnotations.get(0).endpoint)
|
||||
.isEqualTo(result.annotations.get(0).endpoint);
|
||||
}
|
||||
|
||||
/** zipkin's Endpoint.serviceName should never be null. */
|
||||
@Test
|
||||
public void localEndpointIncludesServiceName() {
|
||||
assertThat(this.spanListener.endpointLocator.local().serviceName)
|
||||
.isNotEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* In zipkin, the service context is attached to annotations. Sleuth spans that have
|
||||
* no annotations will get an "lc" one, which allows them to be queryable in zipkin by
|
||||
* service name.
|
||||
*/
|
||||
@Test
|
||||
public void spanWithoutAnnotationsLogsComponent() {
|
||||
Span context = this.tracer.createSpan("http:foo");
|
||||
this.tracer.close(context);
|
||||
assertEquals(1, this.test.zipkinSpans.size());
|
||||
assertThat(this.test.zipkinSpans.get(0).binaryAnnotations.get(0).value)
|
||||
.isEqualTo("unknown".getBytes()); // TODO: "unknown" bc process id, documented as not nullable, is null.
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rpcAnnotations() {
|
||||
Span context = this.tracer.createSpan("http:child", this.parent);
|
||||
context.logEvent(Span.CLIENT_SEND);
|
||||
logServerReceived(this.parent);
|
||||
logServerSent(this.spanListener, this.parent);
|
||||
this.tracer.close(context);
|
||||
assertEquals(2, this.test.zipkinSpans.size());
|
||||
}
|
||||
|
||||
void logServerReceived(Span parent) {
|
||||
if (parent != null && parent.isRemote()) {
|
||||
parent.logEvent(Span.SERVER_RECV);
|
||||
}
|
||||
}
|
||||
|
||||
void logServerSent(SpanReporter spanReporter, Span parent) {
|
||||
if (parent != null && parent.isRemote()) {
|
||||
parent.logEvent(Span.SERVER_SEND);
|
||||
spanReporter.report(parent);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appendsLocalComponentTagIfNoZipkinLogIsPresent() {
|
||||
this.parent.logEvent("hystrix/retry");
|
||||
this.parent.stop();
|
||||
|
||||
zipkin.Span result = this.spanListener.convert(this.parent);
|
||||
|
||||
assertThat(result.binaryAnnotations)
|
||||
.extracting(input -> input.key)
|
||||
.contains(Constants.LOCAL_COMPONENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appendServerAddressTagIfClientLogIsPresentWhenPeerServiceIsPresent() {
|
||||
this.parent.logEvent(Constants.CLIENT_SEND);
|
||||
this.parent.tag(Span.SPAN_PEER_SERVICE_TAG_NAME, "fooservice");
|
||||
this.parent.stop();
|
||||
|
||||
zipkin.Span result = this.spanListener.convert(this.parent);
|
||||
|
||||
assertThat(result.binaryAnnotations)
|
||||
.filteredOn("key", Constants.SERVER_ADDR)
|
||||
.extracting(input -> input.endpoint)
|
||||
.hasSize(1)
|
||||
.has(new Condition<List<? extends Endpoint>>() {
|
||||
@Override public boolean matches(List<? extends Endpoint> value) {
|
||||
Endpoint endpoint = value.get(0);
|
||||
return endpoint.serviceName.equals("fooservice") && endpoint.ipv4 == 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotAppendServerAddressTagIfClientLogIsPresent() {
|
||||
this.parent.logEvent(Constants.CLIENT_SEND);
|
||||
this.parent.stop();
|
||||
|
||||
zipkin.Span result = this.spanListener.convert(this.parent);
|
||||
|
||||
assertThat(result.binaryAnnotations)
|
||||
.filteredOn("key", Constants.SERVER_ADDR)
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void converts128BitTraceId() {
|
||||
Span span = Span.builder().traceIdHigh(1L).traceId(2L).spanId(3L).name("foo").build();
|
||||
|
||||
zipkin.Span result = this.spanListener.convert(span);
|
||||
|
||||
assertThat(result.traceIdHigh).isEqualTo(span.getTraceIdHigh());
|
||||
assertThat(result.traceId).isEqualTo(span.getTraceId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReuseServerAddressTag() {
|
||||
this.parent.logEvent(Constants.CLIENT_SEND);
|
||||
this.parent.tag(Span.SPAN_PEER_SERVICE_TAG_NAME, "fooservice");
|
||||
this.parent.stop();
|
||||
|
||||
zipkin.Span result = this.spanListener.convert(this.parent);
|
||||
|
||||
assertThat(result.binaryAnnotations)
|
||||
.filteredOn("key", Constants.SERVER_ADDR)
|
||||
.extracting(input -> input.endpoint.serviceName)
|
||||
.containsOnly("fooservice");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotReportToZipkinWhenSpanIsNotExportable() {
|
||||
Span span = Span.builder().exportable(false).build();
|
||||
|
||||
this.spanListener.report(span);
|
||||
|
||||
assertThat(this.test.zipkinSpans).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAddClientServiceIdTagWhenSpanContainsRpcEvent() {
|
||||
this.parent.logEvent(Span.CLIENT_SEND);
|
||||
this.mockEnvironment.setProperty("vcap.application.instance_id", "foo");
|
||||
|
||||
zipkin.Span result = this.spanListener.convert(this.parent);
|
||||
|
||||
assertThat(result.binaryAnnotations)
|
||||
.filteredOn("key", Span.INSTANCEID)
|
||||
.extracting(input -> input.value)
|
||||
.containsOnly("foo".getBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotAddAnyServiceIdTagWhenSpanContainsRpcEventAndThereIsNoEnvironment() {
|
||||
this.parent.logEvent(Span.CLIENT_RECV);
|
||||
ZipkinSpanListener spanListener = new ZipkinSpanListener(this.spanReporter,
|
||||
this.endpointLocator, null, new ArrayList<>());
|
||||
|
||||
zipkin.Span result = spanListener.convert(this.parent);
|
||||
|
||||
assertThat(result.binaryAnnotations)
|
||||
.filteredOn("key", Span.INSTANCEID)
|
||||
.extracting(input -> input.value)
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_adjust_span_before_reporting_it() {
|
||||
this.parent.logEvent(Span.CLIENT_RECV);
|
||||
ZipkinSpanListener spanListener = new ZipkinSpanListener(this.spanReporter,
|
||||
this.endpointLocator, null, Arrays.asList(
|
||||
(SpanAdjuster) span -> Span.builder().from(span).name("foo").build(),
|
||||
(SpanAdjuster) span -> Span.builder().from(span).name(span.getName() + "bar").build()
|
||||
));
|
||||
|
||||
zipkin.Span result = spanListener.convert(this.parent);
|
||||
|
||||
assertThat(result.name).isEqualTo("foobar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRemoveTimestampAndDurationForNonRemoteSharedSpan() {
|
||||
Span span = Span.builder()
|
||||
.name("foo")
|
||||
.exportable(false)
|
||||
.remote(false)
|
||||
.shared(true)
|
||||
.build();
|
||||
|
||||
zipkin.Span result = this.spanListener.convert(span);
|
||||
|
||||
assertThat(result.duration).isNull();
|
||||
assertThat(result.timestamp).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotRemoveTimestampAndDurationForNonRemoteNonSharedSpan() {
|
||||
Span span = Span.builder()
|
||||
.name("foo")
|
||||
.exportable(false)
|
||||
.remote(false)
|
||||
.shared(false)
|
||||
.build();
|
||||
span.stop();
|
||||
|
||||
zipkin.Span result = this.spanListener.convert(span);
|
||||
|
||||
assertThat(result.duration).isNotNull();
|
||||
assertThat(result.timestamp).isNotNull();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
protected static class TestConfiguration {
|
||||
|
||||
private List<zipkin.Span> zipkinSpans = new ArrayList<>();
|
||||
|
||||
@Bean
|
||||
public Sampler sampler() {
|
||||
return new AlwaysSampler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ZipkinSpanReporter reporter() {
|
||||
return this.zipkinSpans::add;
|
||||
}
|
||||
|
||||
@Bean @Primary MockEnvironment mockEnvironment() {
|
||||
return new MockEnvironment();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 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.zipkin;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = ZipkinWithDisabledSleuthTests.Config.class)
|
||||
@TestPropertySource(properties = "spring.sleuth.enabled=false")
|
||||
public class ZipkinWithDisabledSleuthTests {
|
||||
|
||||
@Test public void shouldStartContext() {
|
||||
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
static class Config {
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2013-2017 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.
|
||||
-->
|
||||
|
||||
<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>
|
||||
|
||||
<artifactId>spring-cloud-sleuth-zipkin2</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Sleuth Zipkin v2</name>
|
||||
<description>Spring Cloud Sleuth Zipkin v2</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth</artifactId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
</dependency>
|
||||
<!--<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-context</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-actuator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.zipkin2</groupId>
|
||||
<artifactId>zipkin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.reporter2</groupId>
|
||||
<artifactId>zipkin-reporter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.reporter2</groupId>
|
||||
<artifactId>zipkin-sender-kafka11</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.kafka</groupId>
|
||||
<artifactId>spring-kafka</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.reporter2</groupId>
|
||||
<artifactId>zipkin-sender-amqp-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.amqp</groupId>
|
||||
<artifactId>spring-rabbit</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-messaging</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>mockwebserver</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjrt</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,3 +0,0 @@
|
||||
# Auto Configuration
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.sleuth.zipkin2.ZipkinAutoConfiguration
|
||||
@@ -9,8 +9,8 @@
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-zipkin</artifactId>
|
||||
<name>spring-cloud-starter-zipkin</name>
|
||||
<description>Spring Cloud Starter</description>
|
||||
<name>Spring Cloud Starter Zipkin</name>
|
||||
<description>Spring Cloud Starter Zipkin</description>
|
||||
<properties>
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
</properties>
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth</artifactId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-zipkin2</artifactId>
|
||||
<name>spring-cloud-starter-zipkin2</name>
|
||||
<description>Spring Cloud Starter Zipkin v2</description>
|
||||
<properties>
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-sleuth</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-zipkin2</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1 +0,0 @@
|
||||
provides: spring-platform-netflix-core, eureka-client
|
||||
Reference in New Issue
Block a user