Sleuth now uses Brave (#829)
with this pull request we have rewritten the whole Sleuth internals to use Brave. That way we can leverage all the functionalities & instrumentations that Brave already has (https://github.com/openzipkin/brave/tree/master/instrumentation). Migration guide is available here: https://github.com/spring-cloud/spring-cloud-sleuth/wiki/Spring-Cloud-Sleuth-2.0-Migration-Guide fixes #711 - Brave instrumentation fixes #92 - we move to Brave's Sampler fixes #143 - Brave is capable of passing context fixes #255 - we've moved away from Zipkin Stream server fixes #305 - Brave has GRPC instrumentation (https://github.com/openzipkin/brave/tree/master/instrumentation/grpc) fixes #459 - Brave (openzipkin/brave#510) & Zipkin (openzipkin/zipkin#1754) will deal with the AWS XRay instrumentation fixes #577 - Messaging instrumentation has been rewritten
This commit is contained in:
committed by
GitHub
parent
9f716d7d92
commit
7eb374b5a5
63
README.adoc
63
README.adoc
@@ -8,7 +8,7 @@
|
||||
:github-code: https://github.com/{github-repo}/tree/{github-tag}
|
||||
|
||||
image::https://circleci.com/gh/spring-cloud/spring-cloud-sleuth.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-sleuth"]
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-sleuth/branch/master/graph/badge.svg["codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-sleuth"]
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-sleuth/branch/{github-tag}/graph/badge.svg["codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-sleuth"]
|
||||
image::https://badges.gitter.im/spring-cloud/spring-cloud-sleuth.svg[Gitter, link="https://gitter.im/spring-cloud/spring-cloud-sleuth?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"]
|
||||
== Spring Cloud Sleuth
|
||||
|
||||
@@ -66,8 +66,12 @@ of that span is equal to trace id.
|
||||
*Trace:* A set of spans forming a tree-like structure. For example, if you are running a distributed
|
||||
big-data store, a trace might be formed by a put request.
|
||||
|
||||
*Annotation:* is used to record existence of an event in time. Some of the core annotations used to define
|
||||
the start and stop of a request are:
|
||||
*Annotation:* is used to record existence of an event in time. With
|
||||
https://github.com/openzipkin/brave[Brave] instrumentation we no longer need to set special events
|
||||
for https://zipkin.io/[Zipkin] to understand who the client and server are and where
|
||||
the request started and where it has ended. For learning purposes
|
||||
however we will mark these events to highlight what kind
|
||||
of an action took place.
|
||||
|
||||
- *cs* - Client Sent - The client has made a request. This annotation depicts the start of the span.
|
||||
- *sr* - Server Received - The server side got the request and will start processing it.
|
||||
@@ -90,8 +94,8 @@ Trace Id = X
|
||||
Span Id = D
|
||||
Client Sent
|
||||
|
||||
That means that the current span has *Trace-Id* set to *X*, *Span-Id* set to *D*. It also has emitted
|
||||
*Client Sent* event.
|
||||
That means that the current span has *Trace-Id* set to *X*, *Span-Id* set to *D*. Also, the
|
||||
*Client Sent* event took place.
|
||||
|
||||
This is how the visualization of the parent / child relationship of spans would look like:
|
||||
|
||||
@@ -118,14 +122,14 @@ annotations then they will presented as a single span.
|
||||
Why is there a difference between the 7 and 4 spans in this case?
|
||||
|
||||
- 2 spans come from `http:/start` span. It has the Server Received (SR) and Server Sent (SS) annotations.
|
||||
- 2 spans come from the RPC call from `service1` to `service2` to the `http:/foo` endpoint. It has the Client Sent (CS)
|
||||
and Client Received (CR) annotations on `service1` side. It also has Server Received (SR) and Server Sent (SS) annotations
|
||||
- 2 spans come from the RPC call from `service1` to `service2` to the `http:/foo` endpoint. The Client Sent (CS)
|
||||
and Client Received (CR) events took place on `service1` side. Server Received (SR) and Server Sent (SS) events took place
|
||||
on the `service2` side. Physically there are 2 spans but they form 1 logical span related to an RPC call.
|
||||
- 2 spans come from the RPC call from `service2` to `service3` to the `http:/bar` endpoint. It has the Client Sent (CS)
|
||||
and Client Received (CR) annotations on `service2` side. It also has Server Received (SR) and Server Sent (SS) annotations
|
||||
- 2 spans come from the RPC call from `service2` to `service3` to the `http:/bar` endpoint. The Client Sent (CS)
|
||||
and Client Received (CR) events took place on `service2` side. Server Received (SR) and Server Sent (SS) events took place
|
||||
on the `service3` side. Physically there are 2 spans but they form 1 logical span related to an RPC call.
|
||||
- 2 spans come from the RPC call from `service2` to `service4` to the `http:/baz` endpoint. It has the Client Sent (CS)
|
||||
and Client Received (CR) annotations on `service2` side. It also has Server Received (SR) and Server Sent (SS) annotations
|
||||
- 2 spans come from the RPC call from `service2` to `service4` to the `http:/baz` endpoint. The Client Sent (CS)
|
||||
and Client Received (CR) events took place on `service2` side. Server Received (SR) and Server Sent (SS) events took place
|
||||
on the `service4` side. Physically there are 2 spans but they form 1 logical span related to an RPC call.
|
||||
|
||||
So if we count the physical spans we have *1* from `http:/start`, *2* from `service1` calling `service2`, *2* form `service2`
|
||||
@@ -150,11 +154,25 @@ image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/{branc
|
||||
|
||||
As you can see you can easily see the reason for an error and the whole stacktrace related to it.
|
||||
|
||||
==== Distributed tracing with Brave
|
||||
|
||||
Starting with version `2.0.0`, Spring Cloud Sleuth uses
|
||||
https://github.com/openzipkin/brave[Brave] as the tracing library. That means
|
||||
that Sleuth no longer takes care of storing the context but it delegates
|
||||
that work to Brave.
|
||||
|
||||
Due to the fact that Sleuth had different naming / tagging
|
||||
conventions than Brave, we've decided to follow the Brave's
|
||||
conventions from now on. However, if you want to use the legacy
|
||||
Sleuth approaches, it's enough to set the `spring.sleuth.http.legacy.enabled` property
|
||||
to `true`.
|
||||
|
||||
==== Live examples
|
||||
|
||||
.Click Pivotal Web Services icon to see it live!
|
||||
[caption="Click Pivotal Web Services icon to see it live!"]
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/{branch}/docs/src/main/asciidoc/images/pws.png["Zipkin deployed on Pivotal Web Services", link="http://docssleuth-zipkin-server.cfapps.io/", width=150, height=74]
|
||||
http://docssleuth-zipkin-server.cfapps.io/[Click here to see it live!]
|
||||
|
||||
The dependency graph in Zipkin would look like this:
|
||||
|
||||
@@ -163,7 +181,7 @@ image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/{branc
|
||||
.Click Pivotal Web Services icon to see it live!
|
||||
[caption="Click Pivotal Web Services icon to see it live!"]
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/{branch}/docs/src/main/asciidoc/images/pws.png["Zipkin deployed on Pivotal Web Services", link="http://docssleuth-zipkin-server.cfapps.io/dependency", width=150, height=74]
|
||||
|
||||
http://docssleuth-zipkin-server.cfapps.io/dependency[Click here to see it live!]
|
||||
|
||||
==== Log correlation
|
||||
|
||||
@@ -322,9 +340,8 @@ Example of setting baggage on a span:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
Span initialSpan = this.tracer.createSpan("span");
|
||||
initialSpan.setBaggageItem("foo", "bar");
|
||||
initialSpan.setBaggageItem("UPPER_CASE", "someValue");
|
||||
Unresolved directive in intro.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/brave/instrument/web/multiple/MultipleHopsIntegrationTests.java[tags=baggage,indent=0]
|
||||
}
|
||||
----
|
||||
|
||||
===== Baggage vs. Span Tags
|
||||
@@ -337,15 +354,11 @@ can search by tag to find the trace, where there exists a span having the search
|
||||
|
||||
If you want to be able to lookup a span based on baggage, you should add corresponding entry as a tag in the root span.
|
||||
|
||||
IMPORTANT: Remember that the span needs to be in scope!
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Autowired Tracer tracer;
|
||||
|
||||
Span span = tracer.getCurrentSpan();
|
||||
String baggageKey = "key";
|
||||
String baggageValue = "foo";
|
||||
span.setBaggageItem(baggageKey, baggageValue);
|
||||
tracer.addTag(baggageKey, baggageValue);
|
||||
Unresolved directive in intro.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/brave/instrument/web/multiple/MultipleHopsIntegrationTests.java[tags=baggage_tag,indent=0]
|
||||
----
|
||||
|
||||
=== Adding to the project
|
||||
@@ -361,7 +374,7 @@ the `spring-cloud-starter-sleuth` module to your project.
|
||||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||||
.Maven
|
||||
----
|
||||
<dependencyManagement> <1>
|
||||
<dependencyManagement> <1>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
@@ -406,7 +419,7 @@ If you want both Sleuth and Zipkin just add the `spring-cloud-starter-zipkin` de
|
||||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||||
.Maven
|
||||
----
|
||||
<dependencyManagement> <1>
|
||||
<dependencyManagement> <1>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
@@ -457,7 +470,7 @@ dependencies.
|
||||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||||
.Maven
|
||||
----
|
||||
<dependencyManagement> <1>
|
||||
<dependencyManagement> <1>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -21,7 +21,6 @@ import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -30,9 +29,9 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent;
|
||||
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
@@ -40,9 +39,7 @@ import org.springframework.cloud.sleuth.annotation.ContinueSpan;
|
||||
import org.springframework.cloud.sleuth.annotation.NewSpan;
|
||||
import org.springframework.cloud.sleuth.annotation.SpanTag;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanAccumulator;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
@@ -102,9 +99,9 @@ public class SleuthBenchmarkingSpringApp implements
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EmbeddedServletContainerFactory servletContainer(@Value("${server.port:0}") int serverPort) {
|
||||
public ServletWebServerFactory servletContainer(@Value("${server.port:0}") int serverPort) {
|
||||
log.info("Starting container at port [" + serverPort + "]");
|
||||
return new TomcatEmbeddedServletContainerFactory(serverPort == 0 ? SocketUtils.findAvailableTcpPort() : serverPort);
|
||||
return new TomcatServletWebServerFactory(serverPort == 0 ? SocketUtils.findAvailableTcpPort() : serverPort);
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright 2013-2017 the original author or authors.
|
||||
# Copyright 2013-2018 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.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
:github-code: https://github.com/{github-repo}/tree/{github-tag}
|
||||
|
||||
image::https://circleci.com/gh/spring-cloud/spring-cloud-sleuth.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-sleuth"]
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-sleuth/branch/master/graph/badge.svg["codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-sleuth"]
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-sleuth/branch/{github-tag}/graph/badge.svg["codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-sleuth"]
|
||||
image::https://badges.gitter.im/spring-cloud/spring-cloud-sleuth.svg[Gitter, link="https://gitter.im/spring-cloud/spring-cloud-sleuth?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"]
|
||||
== Spring Cloud Sleuth
|
||||
|
||||
|
||||
@@ -22,8 +22,12 @@ of that span is equal to trace id.
|
||||
*Trace:* A set of spans forming a tree-like structure. For example, if you are running a distributed
|
||||
big-data store, a trace might be formed by a put request.
|
||||
|
||||
*Annotation:* is used to record existence of an event in time. Some of the core annotations used to define
|
||||
the start and stop of a request are:
|
||||
*Annotation:* is used to record existence of an event in time. With
|
||||
https://github.com/openzipkin/brave[Brave] instrumentation we no longer need to set special events
|
||||
for https://zipkin.io/[Zipkin] to understand who the client and server are and where
|
||||
the request started and where it has ended. For learning purposes
|
||||
however we will mark these events to highlight what kind
|
||||
of an action took place.
|
||||
|
||||
- *cs* - Client Sent - The client has made a request. This annotation depicts the start of the span.
|
||||
- *sr* - Server Received - The server side got the request and will start processing it.
|
||||
@@ -46,8 +50,8 @@ Trace Id = X
|
||||
Span Id = D
|
||||
Client Sent
|
||||
|
||||
That means that the current span has *Trace-Id* set to *X*, *Span-Id* set to *D*. It also has emitted
|
||||
*Client Sent* event.
|
||||
That means that the current span has *Trace-Id* set to *X*, *Span-Id* set to *D*. Also, the
|
||||
*Client Sent* event took place.
|
||||
|
||||
This is how the visualization of the parent / child relationship of spans would look like:
|
||||
|
||||
@@ -74,14 +78,14 @@ annotations then they will presented as a single span.
|
||||
Why is there a difference between the 7 and 4 spans in this case?
|
||||
|
||||
- 2 spans come from `http:/start` span. It has the Server Received (SR) and Server Sent (SS) annotations.
|
||||
- 2 spans come from the RPC call from `service1` to `service2` to the `http:/foo` endpoint. It has the Client Sent (CS)
|
||||
and Client Received (CR) annotations on `service1` side. It also has Server Received (SR) and Server Sent (SS) annotations
|
||||
- 2 spans come from the RPC call from `service1` to `service2` to the `http:/foo` endpoint. The Client Sent (CS)
|
||||
and Client Received (CR) events took place on `service1` side. Server Received (SR) and Server Sent (SS) events took place
|
||||
on the `service2` side. Physically there are 2 spans but they form 1 logical span related to an RPC call.
|
||||
- 2 spans come from the RPC call from `service2` to `service3` to the `http:/bar` endpoint. It has the Client Sent (CS)
|
||||
and Client Received (CR) annotations on `service2` side. It also has Server Received (SR) and Server Sent (SS) annotations
|
||||
- 2 spans come from the RPC call from `service2` to `service3` to the `http:/bar` endpoint. The Client Sent (CS)
|
||||
and Client Received (CR) events took place on `service2` side. Server Received (SR) and Server Sent (SS) events took place
|
||||
on the `service3` side. Physically there are 2 spans but they form 1 logical span related to an RPC call.
|
||||
- 2 spans come from the RPC call from `service2` to `service4` to the `http:/baz` endpoint. It has the Client Sent (CS)
|
||||
and Client Received (CR) annotations on `service2` side. It also has Server Received (SR) and Server Sent (SS) annotations
|
||||
- 2 spans come from the RPC call from `service2` to `service4` to the `http:/baz` endpoint. The Client Sent (CS)
|
||||
and Client Received (CR) events took place on `service2` side. Server Received (SR) and Server Sent (SS) events took place
|
||||
on the `service4` side. Physically there are 2 spans but they form 1 logical span related to an RPC call.
|
||||
|
||||
So if we count the physical spans we have *1* from `http:/start`, *2* from `service1` calling `service2`, *2* form `service2`
|
||||
@@ -106,11 +110,25 @@ image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/{branc
|
||||
|
||||
As you can see you can easily see the reason for an error and the whole stacktrace related to it.
|
||||
|
||||
==== Distributed tracing with Brave
|
||||
|
||||
Starting with version `2.0.0`, Spring Cloud Sleuth uses
|
||||
https://github.com/openzipkin/brave[Brave] as the tracing library. That means
|
||||
that Sleuth no longer takes care of storing the context but it delegates
|
||||
that work to Brave.
|
||||
|
||||
Due to the fact that Sleuth had different naming / tagging
|
||||
conventions than Brave, we've decided to follow the Brave's
|
||||
conventions from now on. However, if you want to use the legacy
|
||||
Sleuth approaches, it's enough to set the `spring.sleuth.http.legacy.enabled` property
|
||||
to `true`.
|
||||
|
||||
==== Live examples
|
||||
|
||||
.Click Pivotal Web Services icon to see it live!
|
||||
[caption="Click Pivotal Web Services icon to see it live!"]
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/{branch}/docs/src/main/asciidoc/images/pws.png["Zipkin deployed on Pivotal Web Services", link="http://docssleuth-zipkin-server.cfapps.io/", width=150, height=74]
|
||||
http://docssleuth-zipkin-server.cfapps.io/[Click here to see it live!]
|
||||
|
||||
The dependency graph in Zipkin would look like this:
|
||||
|
||||
@@ -119,7 +137,7 @@ image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/{branc
|
||||
.Click Pivotal Web Services icon to see it live!
|
||||
[caption="Click Pivotal Web Services icon to see it live!"]
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/{branch}/docs/src/main/asciidoc/images/pws.png["Zipkin deployed on Pivotal Web Services", link="http://docssleuth-zipkin-server.cfapps.io/dependency", width=150, height=74]
|
||||
|
||||
http://docssleuth-zipkin-server.cfapps.io/dependency[Click here to see it live!]
|
||||
|
||||
==== Log correlation
|
||||
|
||||
@@ -203,7 +221,8 @@ Example of setting baggage on a span:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{github-raw}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/multiple/MultipleHopsIntegrationTests.java[tags=baggage,indent=0]
|
||||
include::{github-raw}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/brave/instrument/web/multiple/MultipleHopsIntegrationTests.java[tags=baggage,indent=0]
|
||||
}
|
||||
----
|
||||
|
||||
===== Baggage vs. Span Tags
|
||||
@@ -216,15 +235,11 @@ can search by tag to find the trace, where there exists a span having the search
|
||||
|
||||
If you want to be able to lookup a span based on baggage, you should add corresponding entry as a tag in the root span.
|
||||
|
||||
IMPORTANT: Remember that the span needs to be in scope!
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Autowired Tracer tracer;
|
||||
|
||||
Span span = tracer.getCurrentSpan();
|
||||
String baggageKey = "key";
|
||||
String baggageValue = "foo";
|
||||
span.setBaggageItem(baggageKey, baggageValue);
|
||||
tracer.addTag(baggageKey, baggageValue);
|
||||
include::{github-raw}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/brave/instrument/web/multiple/MultipleHopsIntegrationTests.java[tags=baggage_tag,indent=0]
|
||||
----
|
||||
|
||||
=== Adding to the project
|
||||
@@ -240,7 +255,7 @@ the `spring-cloud-starter-sleuth` module to your project.
|
||||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||||
.Maven
|
||||
----
|
||||
<dependencyManagement> <1>
|
||||
<dependencyManagement> <1>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
@@ -285,7 +300,7 @@ If you want both Sleuth and Zipkin just add the `spring-cloud-starter-zipkin` de
|
||||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||||
.Maven
|
||||
----
|
||||
<dependencyManagement> <1>
|
||||
<dependencyManagement> <1>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
@@ -336,7 +351,7 @@ dependencies.
|
||||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||||
.Maven
|
||||
----
|
||||
<dependencyManagement> <1>
|
||||
<dependencyManagement> <1>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
|
||||
@@ -19,29 +19,287 @@ include::intro.adoc[]
|
||||
|
||||
include::features.adoc[]
|
||||
|
||||
=== Introduction to Brave
|
||||
|
||||
IMPORTANT: Starting with version `2.0.0` Spring Cloud Sleuth uses
|
||||
https://github.com/openzipkin/brave[Brave] as the tracing library.
|
||||
For your convenience we're embedding part of the Brave's docs here.
|
||||
|
||||
Brave is a library used to capture and report latency information about
|
||||
distributed operations to Zipkin. Most users won't use Brave directly,
|
||||
rather libraries or frameworks than employ Brave on their behalf.
|
||||
|
||||
This module includes tracer creates and joins spans that model the
|
||||
latency of potentially distributed work. It also includes libraries to
|
||||
propagate the trace context over network boundaries, for example, via
|
||||
http headers.
|
||||
|
||||
==== Tracing
|
||||
|
||||
Most importantly, you need a `brave.Tracer`, configured to [report to Zipkin]
|
||||
(https://github.com/openzipkin/zipkin-reporter-java).
|
||||
|
||||
Here's an example setup that sends trace data (spans) to Zipkin over
|
||||
http (as opposed to Kafka).
|
||||
|
||||
```java
|
||||
|
||||
class MyClass {
|
||||
|
||||
private final Tracer tracer;
|
||||
|
||||
// Tracer will be autowired
|
||||
MyClass(Tracer tracer) {
|
||||
this.tracer = tracer;
|
||||
}
|
||||
|
||||
void doSth() {
|
||||
Span span = tracer.newTrace().name("encode").start();
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
IMPORTANT: If your span contains a name greater than 50 chars, then that name will
|
||||
be truncated to 50 chars. Your names have to be explicit and concrete. Big names lead to
|
||||
latency issues and sometimes even thrown exceptions.
|
||||
|
||||
==== Tracing
|
||||
|
||||
The tracer creates and joins spans that model the latency of potentially
|
||||
distributed work. It can employ sampling to reduce overhead in process
|
||||
or to reduce the amount of data sent to Zipkin.
|
||||
|
||||
Spans returned by a tracer report data to Zipkin when finished, or do
|
||||
nothing if unsampled. After starting a span, you can annotate events of
|
||||
interest or add tags containing details or lookup keys.
|
||||
|
||||
Spans have a context which includes trace identifiers that place it at
|
||||
the correct spot in the tree representing the distributed operation.
|
||||
|
||||
==== Local Tracing
|
||||
|
||||
When tracing local code, just run it inside a span.
|
||||
|
||||
```java
|
||||
Span span = tracer.newTrace().name("encode").start();
|
||||
try {
|
||||
doSomethingExpensive();
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
```
|
||||
|
||||
In the above example, the span is the root of the trace. In many cases,
|
||||
you will be a part of an existing trace. When this is the case, call
|
||||
`newChild` instead of `newTrace`
|
||||
|
||||
```java
|
||||
Span span = tracer.newChild(root.context()).name("encode").start();
|
||||
try {
|
||||
doSomethingExpensive();
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
```
|
||||
|
||||
==== Customizing spans
|
||||
|
||||
Once you have a span, you can add tags to it, which can be used as lookup
|
||||
keys or details. For example, you might add a tag with your runtime
|
||||
version.
|
||||
|
||||
```java
|
||||
span.tag("clnt/finagle.version", "6.36.0");
|
||||
```
|
||||
|
||||
When exposing the ability to customize spans to third parties, prefer
|
||||
`brave.SpanCustomizer` as opposed to `brave.Span`. The former is simpler to
|
||||
understand and test, and doesn't tempt users with span lifecycle hooks.
|
||||
|
||||
```java
|
||||
interface MyTraceCallback {
|
||||
void request(Request request, SpanCustomizer customizer);
|
||||
}
|
||||
```
|
||||
|
||||
Since `brave.Span` implements `brave.SpanCustomizer`, it is just as easy for you
|
||||
to pass to users.
|
||||
|
||||
Ex.
|
||||
```java
|
||||
for (MyTraceCallback callback : userCallbacks) {
|
||||
callback.request(request, span);
|
||||
}
|
||||
```
|
||||
|
||||
==== Implicitly looking up the current span
|
||||
|
||||
Sometimes you won't know if a trace is in progress or not, and you don't
|
||||
want users to do null checks. `brave.CurrentSpanCustomizer` adds to any
|
||||
span that's in progress or drops data accordingly.
|
||||
|
||||
Ex.
|
||||
```java
|
||||
// user code can then inject this without a chance of it being null.
|
||||
@Autowire SpanCustomizer span;
|
||||
|
||||
void userCode() {
|
||||
span.annotate("tx.started");
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
==== RPC tracing
|
||||
|
||||
Check for https://github.com/openzipkin/sleuth/tree/master/instrumentation[instrumentation written here]
|
||||
and http://zipkin.io/pages/existing_instrumentations.html[Zipkin's list]
|
||||
before rolling your own RPC instrumentation!
|
||||
|
||||
RPC tracing is often done automatically by interceptors. Under the scenes,
|
||||
they add tags and events that relate to their role in an RPC operation.
|
||||
|
||||
Here's an example of a client span:
|
||||
|
||||
```java
|
||||
// before you send a request, add metadata that describes the operation
|
||||
span = tracer.newTrace().name("get").type(CLIENT);
|
||||
span.tag("clnt/finagle.version", "6.36.0");
|
||||
span.tag(TraceKeys.HTTP_PATH, "/api");
|
||||
span.remoteEndpoint(Endpoint.builder()
|
||||
.serviceName("backend")
|
||||
.ipv4(127 << 24 | 1)
|
||||
.port(8080).build());
|
||||
|
||||
// when the request is scheduled, start the span
|
||||
span.start();
|
||||
|
||||
// if you have callbacks for when data is on the wire, note those events
|
||||
span.annotate(Constants.WIRE_SEND);
|
||||
span.annotate(Constants.WIRE_RECV);
|
||||
|
||||
// when the response is complete, finish the span
|
||||
span.finish();
|
||||
```
|
||||
|
||||
===== One-Way tracing
|
||||
|
||||
Sometimes you need to model an asynchronous operation, where there is a
|
||||
request, but no response. In normal RPC tracing, you use `span.finish()`
|
||||
which indicates the response was received. In one-way tracing, you use
|
||||
`span.flush()` instead, as you don't expect a response.
|
||||
|
||||
Here's how a client might model a one-way operation
|
||||
```java
|
||||
// start a new span representing a client request
|
||||
oneWaySend = tracer.newSpan(parent).kind(Span.Kind.CLIENT);
|
||||
|
||||
// Add the trace context to the request, so it can be propagated in-band
|
||||
tracing.propagation().injector(Request::addHeader)
|
||||
.inject(oneWaySend.context(), request);
|
||||
|
||||
// fire off the request asynchronously, totally dropping any response
|
||||
request.execute();
|
||||
|
||||
// start the client side and flush instead of finish
|
||||
oneWaySend.start().flush();
|
||||
```
|
||||
|
||||
And here's how a server might handle this..
|
||||
```java
|
||||
// pull the context out of the incoming request
|
||||
extractor = tracing.propagation().extractor(Request::getHeader);
|
||||
|
||||
// convert that context to a span which you can name and add tags to
|
||||
oneWayReceive = nextSpan(tracer, extractor.extract(request))
|
||||
.name("process-request")
|
||||
.kind(SERVER)
|
||||
... add tags etc.
|
||||
|
||||
// start the server side and flush instead of finish
|
||||
oneWayReceive.start().flush();
|
||||
|
||||
// you should not modify this span anymore as it is complete. However,
|
||||
// you can create children to represent follow-up work.
|
||||
next = tracer.newSpan(oneWayReceive.context()).name("step2").start();
|
||||
```
|
||||
|
||||
**Note** The above propagation logic is a simplified version of our [http handlers](https://github.com/openzipkin/sleuth/tree/master/instrumentation/http#http-server).
|
||||
|
||||
There's a working example of a one-way span [here](src/test/java/sleuth/features/async/OneWaySpanTest.java).
|
||||
|
||||
== Sampling
|
||||
|
||||
In distributed tracing the data volumes can be very high so sampling
|
||||
can be important (you usually don't need to export all spans to get a
|
||||
good picture of what is happening). Spring Cloud Sleuth has a
|
||||
`Sampler` strategy that you can implement to take control of the
|
||||
sampling algorithm. Samplers do not stop span (correlation) ids from
|
||||
being generated, but they do prevent the tags and events being
|
||||
attached and exported. By default you get a strategy that continues to
|
||||
trace if a span is already active, but new ones are always marked as
|
||||
non-exportable. If all your apps run with this sampler you will see
|
||||
traces in logs, but not in any remote store. For testing the default
|
||||
is often enough, and it probably is all you need if you are only using
|
||||
the logs (e.g. with an ELK aggregator). If you are exporting span data
|
||||
to Zipkin or Spring Cloud Stream, there is also an `AlwaysSampler`
|
||||
that exports everything and a `PercentageBasedSampler` that samples a
|
||||
Sampling may be employed to reduce the data collected and reported out
|
||||
of process. When a span isn't sampled, it adds no overhead (noop).
|
||||
|
||||
Sampling is an up-front decision, meaning that the decision to report
|
||||
data is made at the first operation in a trace, and that decision is
|
||||
propagated downstream.
|
||||
|
||||
By default, there's a global sampler that applies a single rate to all
|
||||
traced operations. `Tracer.Builder.sampler` is how you indicate this,
|
||||
and it defaults to trace every request.
|
||||
|
||||
=== Declarative sampling
|
||||
|
||||
Some need to sample based on the type or annotations of a java method.
|
||||
|
||||
Most users will use a framework interceptor which automates this sort of
|
||||
policy. Here's how they might work internally.
|
||||
|
||||
```java
|
||||
// derives a sample rate from an annotation on a java method
|
||||
DeclarativeSampler<Traced> sampler = DeclarativeSampler.create(Traced::sampleRate);
|
||||
|
||||
@Around("@annotation(traced)")
|
||||
public Object traceThing(ProceedingJoinPoint pjp, Traced traced) throws Throwable {
|
||||
Span span = tracing.tracer().newTrace(sampler.sample(traced))...
|
||||
try {
|
||||
return pjp.proceed();
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== Custom sampling
|
||||
|
||||
You may want to apply different policies depending on what the operation
|
||||
is. For example, you might not want to trace requests to static resources
|
||||
such as images, or you might want to trace all requests to a new api.
|
||||
|
||||
Most users will use a framework interceptor which automates this sort of
|
||||
policy. Here's how they might work internally.
|
||||
|
||||
```java
|
||||
Span newTrace(Request input) {
|
||||
SamplingFlags flags = SamplingFlags.NONE;
|
||||
if (input.url().startsWith("/experimental")) {
|
||||
flags = SamplingFlags.SAMPLED;
|
||||
} else if (input.url().startsWith("/static")) {
|
||||
flags = SamplingFlags.NOT_SAMPLED;
|
||||
}
|
||||
return tracer.newTrace(flags);
|
||||
}
|
||||
```
|
||||
|
||||
Note: the above is the basis for the built-in https://github.com/openzipkin/sleuth/tree/master/instrumentation/http[http sampler]
|
||||
|
||||
=== Sampling in Spring Cloud Sleuth
|
||||
|
||||
Spring Cloud Sleuth by default sets all spans to non-exportable.
|
||||
That means that you will see traces in logs, but not in any remote store.
|
||||
For testing the default is often enough, and it probably is all you need
|
||||
if you are only using the logs (e.g. with an ELK aggregator). If you are
|
||||
exporting span data to Zipkin, there is also an `Sampler.ALWAYS_SAMPLE`
|
||||
that exports everything and a `ProbabilityBasedSampler` that samples a
|
||||
fixed fraction of spans.
|
||||
|
||||
NOTE: the `PercentageBasedSampler` is the default if you are using
|
||||
`spring-cloud-sleuth-zipkin` or `spring-cloud-sleuth-stream`. You can
|
||||
configure the exports using `spring.sleuth.sampler.percentage`. The passed
|
||||
value needs to be a double from `0.0` to `1.0` so it's not a percentage.
|
||||
For backwards compatibility reasons we're not changing the property name.
|
||||
NOTE: The `ProbabilityBasedSampler` is the default if you are using
|
||||
`spring-cloud-sleuth-zipkin`. You can
|
||||
configure the exports using `spring.sleuth.sampler.probability`. The passed
|
||||
value needs to be a double from `0.0` to `1.0`.
|
||||
|
||||
A sampler can be installed just by creating a bean definition, e.g:
|
||||
|
||||
@@ -54,6 +312,247 @@ TIP: You can set the HTTP header `X-B3-Flags` to `1` or when doing messaging you
|
||||
set `spanFlags` header to `1`. Then the current span will be forced to be exportable
|
||||
regardless of the sampling decision.
|
||||
|
||||
== Propagation
|
||||
|
||||
Propagation is needed to ensure activity originating from the same root
|
||||
are collected together in the same trace. The most common propagation
|
||||
approach is to copy a trace context from a client sending an RPC request
|
||||
to a server receiving it.
|
||||
|
||||
For example, when an downstream Http call is made, its trace context is
|
||||
sent along with it, encoded as request headers:
|
||||
|
||||
```
|
||||
Client Span Server Span
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ │ │ │
|
||||
│ TraceContext │ Http Request Headers │ TraceContext │
|
||||
│ ┌──────────────┐ │ ┌───────────────────┐ │ ┌──────────────┐ │
|
||||
│ │ TraceId │ │ │ X─B3─TraceId │ │ │ TraceId │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ │ ParentSpanId │ │ Extract │ X─B3─ParentSpanId │ Inject │ │ ParentSpanId │ │
|
||||
│ │ ├─┼─────────>│ ├────────┼>│ │ │
|
||||
│ │ SpanId │ │ │ X─B3─SpanId │ │ │ SpanId │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ │ Sampled │ │ │ X─B3─Sampled │ │ │ Sampled │ │
|
||||
│ └──────────────┘ │ └───────────────────┘ │ └──────────────┘ │
|
||||
│ │ │ │
|
||||
└──────────────────┘ └──────────────────┘
|
||||
```
|
||||
|
||||
The names above are from https://github.com/openzipkin/b3-propagation[B3 Propagation],
|
||||
which is built-in to Brave and has implementations in many languages and
|
||||
frameworks.
|
||||
|
||||
Most users will use a framework interceptor which automates propagation.
|
||||
Here's how they might work internally.
|
||||
|
||||
Here's what client-side propagation might look like
|
||||
|
||||
```java
|
||||
// configure a function that injects a trace context into a request
|
||||
injector = tracing.propagation().injector(Request.Builder::addHeader);
|
||||
|
||||
// before a request is sent, add the current span's context to it
|
||||
injector.inject(span.context(), request);
|
||||
```
|
||||
|
||||
Here's what server-side propagation might look like
|
||||
|
||||
```java
|
||||
// configure a function that extracts the trace context from a request
|
||||
extracted = tracing.propagation().extractor(Request::getHeader);
|
||||
|
||||
// when a server receives a request, it joins or starts a new trace
|
||||
span = tracer.nextSpan(extracted, request);
|
||||
```
|
||||
|
||||
=== Propagating extra fields
|
||||
|
||||
Sometimes you need to propagate extra fields, such as a request ID or an alternate trace context.
|
||||
For example, if you are in a Cloud Foundry environment, you might want to pass the request ID:
|
||||
|
||||
```java
|
||||
// when you initialize the builder, define the extra field you want to propagate
|
||||
tracingBuilder.propagationFactory(
|
||||
ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "x-vcap-request-id")
|
||||
);
|
||||
|
||||
// later, you can tag that request ID or use it in log correlation
|
||||
requestId = ExtraFieldPropagation.get("x-vcap-request-id");
|
||||
```
|
||||
|
||||
You may also need to propagate a trace context you aren't using. For example, you may be in an
|
||||
Amazon Web Services environment, but not reporting data to X-Ray. To ensure X-Ray can co-exist
|
||||
correctly, pass-through its tracing header like so.
|
||||
|
||||
```java
|
||||
tracingBuilder.propagationFactory(
|
||||
ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "x-amzn-trace-id")
|
||||
);
|
||||
```
|
||||
|
||||
==== Prefixed fields
|
||||
|
||||
You can also prefix fields, if they follow a common pattern. For example, the following will
|
||||
propagate the field "x-vcap-request-id" as-is, but send the fields "country-code" and "user-id"
|
||||
on the wire as "x-baggage-country-code" and "x-baggage-user-id" respectively.
|
||||
|
||||
Setup your tracing instance with allowed fields:
|
||||
|
||||
```java
|
||||
tracingBuilder.propagationFactory(
|
||||
ExtraFieldPropagation.newFactoryBuilder(B3Propagation.FACTORY)
|
||||
.addField("x-vcap-request-id")
|
||||
.addPrefixedFields("baggage-", Arrays.asList("country-code", "user-id"))
|
||||
.build()
|
||||
);
|
||||
```
|
||||
|
||||
Later, you can call below to affect the country code of the current trace context
|
||||
|
||||
```java
|
||||
ExtraFieldPropagation.set("country-code", "FO");
|
||||
String countryCode = ExtraFieldPropagation.get("country-code");
|
||||
```
|
||||
|
||||
Or, if you have a reference to a trace context, use it explicitly
|
||||
|
||||
```java
|
||||
ExtraFieldPropagation.set(span.context(), "country-code", "FO");
|
||||
String countryCode = ExtraFieldPropagation.get(span.context(), "country-code");
|
||||
```
|
||||
|
||||
IMPORTANT: In comparison to previous versions of Sleuth, with
|
||||
Brave it's required to pass the list of baggage keys.
|
||||
There are two properties to achieve this. Via the `spring.sleuth.baggage-keys` you set keys
|
||||
that will get prefixed with `baggage-` for http calls and `baggage_` for messaging. You can also pass
|
||||
a list of prefixed keys that will be whitelisted without any prefix via
|
||||
`spring.sleuth.prefixed-keys` property.
|
||||
|
||||
==== Extracting a propagated context
|
||||
|
||||
The `TraceContext.Extractor<C>` reads trace identifiers and sampling status
|
||||
from an incoming request or message. The carrier is usually a request object
|
||||
or headers.
|
||||
|
||||
This utility is used in standard instrumentation like [HttpServerHandler](../instrumentation/http/src/main/java/sleuth/http/HttpServerHandler.java),
|
||||
but can also be used for custom RPC or messaging code.
|
||||
|
||||
`TraceContextOrSamplingFlags` is usually only used with `Tracer.nextSpan(extracted)`, unless you are
|
||||
sharing span IDs between a client and a server.
|
||||
|
||||
==== Sharing span IDs between client and server
|
||||
|
||||
A normal instrumentation pattern is creating a span representing the server
|
||||
side of an RPC. `Extractor.extract` might return a complete trace context when
|
||||
applied to an incoming client request. `Tracer.joinSpan` attempts to continue
|
||||
the this trace, using the same span ID if supported, or creating a child span
|
||||
if not. When span ID is shared, data reported includes a flag saying so.
|
||||
|
||||
Here's an example of B3 propagation:
|
||||
|
||||
```
|
||||
┌───────────────────┐ ┌───────────────────┐
|
||||
Incoming Headers │ TraceContext │ │ TraceContext │
|
||||
┌───────────────────┐(extract)│ ┌───────────────┐ │(join)│ ┌───────────────┐ │
|
||||
│ X─B3-TraceId │─────────┼─┼> TraceId │ │──────┼─┼> TraceId │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ X─B3-ParentSpanId │─────────┼─┼> ParentSpanId │ │──────┼─┼> ParentSpanId │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ X─B3-SpanId │─────────┼─┼> SpanId │ │──────┼─┼> SpanId │ │
|
||||
└───────────────────┘ │ │ │ │ │ │ │ │
|
||||
│ │ │ │ │ │ Shared: true │ │
|
||||
│ └───────────────┘ │ │ └───────────────┘ │
|
||||
└───────────────────┘ └───────────────────┘
|
||||
```
|
||||
|
||||
Some propagation systems only forward the parent span ID, detected when
|
||||
`Propagation.Factory.supportsJoin() == false`. In this case, a new span ID is
|
||||
always provisioned and the incoming context determines the parent ID.
|
||||
|
||||
Here's an example of AWS propagation:
|
||||
```
|
||||
┌───────────────────┐ ┌───────────────────┐
|
||||
x-amzn-trace-id │ TraceContext │ │ TraceContext │
|
||||
┌───────────────────┐(extract)│ ┌───────────────┐ │(join)│ ┌───────────────┐ │
|
||||
│ Root │─────────┼─┼> TraceId │ │──────┼─┼> TraceId │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ Parent │─────────┼─┼> SpanId │ │──────┼─┼> ParentSpanId │ │
|
||||
└───────────────────┘ │ └───────────────┘ │ │ │ │ │
|
||||
└───────────────────┘ │ │ SpanId: New │ │
|
||||
│ └───────────────┘ │
|
||||
└───────────────────┘
|
||||
```
|
||||
|
||||
Note: Some span reporters do not support sharing span IDs. For example, if you
|
||||
set `Tracing.Builder.spanReporter(amazonXrayOrGoogleStackdrive)`, disable join
|
||||
via `Tracing.Builder.supportsJoin(false)`. This will force a new child span on
|
||||
`Tracer.joinSpan()`.
|
||||
|
||||
==== Implementing Propagation
|
||||
|
||||
`TraceContext.Extractor<C>` is implemented by a `Propagation.Factory` plugin. Internally, this code
|
||||
will create the union type `TraceContextOrSamplingFlags` with one of the following:
|
||||
* `TraceContext` if trace and span IDs were present.
|
||||
* `TraceIdContext` if a trace ID was present, but not span IDs.
|
||||
* `SamplingFlags` if no identifiers were present
|
||||
|
||||
Some `Propagation` implementations carry extra data from point of extraction (ex reading incoming
|
||||
headers) to injection (ex writing outgoing headers). For example, it might carry a request ID. When
|
||||
implementations have extra data, here's how they handle it.
|
||||
* If a `TraceContext` was extracted, add the extra data as `TraceContext.extra()`
|
||||
* Otherwise, add it as `TraceContextOrSamplingFlags.extra()`, which `Tracer.nextSpan` handles.
|
||||
|
||||
== Current Tracing Component
|
||||
|
||||
Brave supports a "current tracing component" concept which should only
|
||||
be used when you have no other means to get a reference. This was made
|
||||
for JDBC connections, as they often initialize prior to the tracing
|
||||
component.
|
||||
|
||||
The most recent tracing component instantiated is available via
|
||||
`Tracing.current()`. You there's also a shortcut to get only the tracer
|
||||
via `Tracing.currentTracer()`. If you use either of these methods, do
|
||||
noot cache the result. Instead, look them up each time you need them.
|
||||
|
||||
== Current Span
|
||||
|
||||
Brave supports a "current span" concept which represents the in-flight
|
||||
operation. `Tracer.currentSpan()` can be used to add custom tags to a
|
||||
span and `Tracer.nextSpan()` can be used to create a child of whatever
|
||||
is in-flight.
|
||||
|
||||
=== Setting a span in scope manually
|
||||
|
||||
When writing new instrumentation, it is important to place a span you
|
||||
created in scope as the current span. Not only does this allow users to
|
||||
access it with `Tracer.currentSpan()`, but it also allows customizations
|
||||
like SLF4J MDC to see the current trace IDs.
|
||||
|
||||
`Tracer.withSpanInScope(Span)` facilitates this and is most conveniently
|
||||
employed via the try-with-resources idiom. Whenever external code might
|
||||
be invoked (such as proceeding an interceptor or otherwise), place the
|
||||
span in scope like this.
|
||||
|
||||
```java
|
||||
try (SpanInScope ws = tracer.withSpanInScope(span)) {
|
||||
return inboundRequest.invoke();
|
||||
} finally { // note the scope is independent of the span
|
||||
span.finish();
|
||||
}
|
||||
```
|
||||
|
||||
In edge cases, you may need to clear the current span temporarily. For
|
||||
example, launching a task that should not be associated with the current
|
||||
request. To do this, simply pass null to `withSpanInScope`.
|
||||
|
||||
```java
|
||||
try (SpanInScope cleared = tracer.withSpanInScope(null)) {
|
||||
startBackgroundThread();
|
||||
}
|
||||
```
|
||||
|
||||
== Instrumentation
|
||||
|
||||
Spring Cloud Sleuth instruments all your Spring application
|
||||
@@ -74,29 +573,24 @@ NOTE: Remember that tags are only collected and exported if there is a
|
||||
danger of accidentally collecting too much data without configuring
|
||||
something).
|
||||
|
||||
NOTE: Currently the instrumentation in Spring Cloud Sleuth is eager - it means that
|
||||
we're actively trying to pass the tracing context between threads. Also timing events
|
||||
are captured even when sleuth isn't exporting data to a tracing system.
|
||||
This approach may change in the future towards being lazy on this matter.
|
||||
|
||||
== Span lifecycle
|
||||
|
||||
You can do the following operations on the Span by means of *org.springframework.cloud.sleuth.Tracer* interface:
|
||||
You can do the following operations on the Span by means of *brave.Tracer*:
|
||||
|
||||
- <<creating-and-closing-spans, start>> - when you start a span its name is assigned and start timestamp is recorded.
|
||||
- <<creating-and-closing-spans, close>> - the span gets finished (the end time of the span is recorded) and if
|
||||
the span is *exportable* then it will be eligible for collection to Zipkin.
|
||||
The span is also removed from the current thread.
|
||||
- <<creating-and-finishing-spans, start>> - when you start a span its name is assigned and start timestamp is recorded.
|
||||
- <<creating-and-finishing-spans, close>> - the span gets finished (the end time of the span is recorded) and if
|
||||
the span is *sampled* then it will be eligible for collection to e.g. Zipkin.
|
||||
- <<continuing-spans, continue>> - a new instance of span will be created whereas it will be a copy of the
|
||||
one that it continues.
|
||||
- <<continuing-spans, detach>> - the span doesn't get stopped or closed. It only gets removed from the current thread.
|
||||
- <<creating-spans-with-explicit-parent, create with explicit parent>> - you can create a new span and set an explicit parent to it
|
||||
|
||||
TIP: Spring creates the instance of `Tracer` for you. In order to use it all you need is to just autowire it.
|
||||
TIP: Spring Cloud Sleuth creates the instance of `Tracer` for you. In order to use it,
|
||||
all you need is to just autowire it.
|
||||
|
||||
=== Creating and closing spans [[creating-and-closing-spans]]
|
||||
=== Creating and finishing spans [[creating-and-finishing-spans]]
|
||||
|
||||
You can manually create spans by using the *Tracer* interface.
|
||||
You can manually create spans by using the *Tracer*.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -106,7 +600,7 @@ include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/
|
||||
In this example we could see how to create a new instance of span. Assuming that there already
|
||||
was a span present in this thread then it would become the parent of that span.
|
||||
|
||||
IMPORTANT: Always clean after you create a span! Don't forget to close a span if you want to send it to Zipkin.
|
||||
IMPORTANT: Always clean after you create a span! Don't forget to finish a span if you want to send it to Zipkin.
|
||||
|
||||
IMPORTANT: If your span contains a name greater than 50 chars, then that name will
|
||||
be truncated to 50 chars. Your names have to be explicit and concrete. Big names lead to
|
||||
@@ -121,39 +615,28 @@ situation might be (of course it all depends on the use-case):
|
||||
- *Hystrix* - executing a Hystrix command is most likely a logical part of the current processing. It's in fact
|
||||
only a technical implementation detail that you wouldn't necessarily want to reflect in tracing as a separate being.
|
||||
|
||||
The continued instance of span is equal to the one that it continues:
|
||||
[source,java]
|
||||
----
|
||||
Span continuedSpan = this.tracer.continueSpan(spanToContinue);
|
||||
assertThat(continuedSpan).isEqualTo(spanToContinue);
|
||||
----
|
||||
|
||||
To continue a span you can use the *Tracer* interface.
|
||||
To continue a span you can use *brave.Tracer*.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=manual_span_continuation,indent=0]
|
||||
----
|
||||
|
||||
IMPORTANT: Always clean after you create a span! Don't forget to detach a span if some work was done started in one
|
||||
thread (e.g. thread X) and it's waiting for other threads (e.g. Y, Z) to finish.
|
||||
Then the spans in the threads Y, Z should be detached at the end of their work. When the results are collected
|
||||
the span in thread X should be closed.
|
||||
|
||||
=== Creating spans with an explicit parent [[creating-spans-with-explicit-parent]]
|
||||
|
||||
There is a possibility that you want to start a new span and provide an explicit parent of that span.
|
||||
Let's assume that the parent of a span is in one thread and you want to start a new span in another thread. The
|
||||
`startSpan` method of the `Tracer` interface is the method you are looking for.
|
||||
Let's assume that the parent of a span is in one thread and you want to start a new span in another thread.
|
||||
In Brave, whenever you call `nextSpan()`, it's creating one in reference
|
||||
to the span being currently in scope. It's enough to just put
|
||||
the span in scope and then call `nextSpan()`, as presented in the example below:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=manual_span_joining,indent=0]
|
||||
----
|
||||
|
||||
IMPORTANT: After having created such a span remember to close it. Otherwise you will see a lot of warnings in your logs
|
||||
related to the fact that you have a span present in the current thread other than the one you're trying to close.
|
||||
What's worse your spans won't get closed properly thus will not get collected to Zipkin.
|
||||
IMPORTANT: After having created such a span remember to finish it, otherwise it will not get
|
||||
reported to e.g. Zipkin
|
||||
|
||||
== Naming spans
|
||||
|
||||
@@ -344,82 +827,14 @@ if executed with a value of `15` will lead to setting of a tag with a String val
|
||||
|
||||
== Customizations
|
||||
|
||||
Thanks to the `SpanInjector` and `SpanExtractor` you can customize the way spans
|
||||
are created and propagated.
|
||||
|
||||
There are currently two built-in ways to pass tracing information between processes:
|
||||
|
||||
* via Spring Integration
|
||||
* via HTTP
|
||||
|
||||
Span ids are extracted from Zipkin-compatible (B3) headers (either `Message`
|
||||
or HTTP headers), to start or join an existing trace. Trace information is
|
||||
injected into any outbound requests so the next hop can extract them.
|
||||
|
||||
The key change in comparison to the previous versions of Sleuth is that Sleuth is implementing
|
||||
the Open Tracing's `TextMap` notion. In Sleuth it's called `SpanTextMap`. Basically the idea
|
||||
is that any means of communication (e.g. message, http request, etc.) can be abstracted via
|
||||
a `SpanTextMap`. This abstraction defines how one can insert data into the carrier and
|
||||
how to retrieve it from there. Thanks to this if you want to instrument a new HTTP library
|
||||
that uses a `FooRequest` as a mean of sending HTTP requests then you have to create an
|
||||
implementation of a `SpanTextMap` that delegates calls to `FooRequest` in terms of retrieval
|
||||
and insertion of HTTP headers.
|
||||
// TODO: Update this
|
||||
|
||||
=== Spring Integration
|
||||
|
||||
For Spring Integration there are 2 interfaces responsible for creation of a Span from a `Message`.
|
||||
These are:
|
||||
|
||||
- `MessagingSpanTextMapExtractor`
|
||||
- `MessagingSpanTextMapInjector`
|
||||
|
||||
You can override them by providing your own implementation.
|
||||
|
||||
=== HTTP
|
||||
|
||||
For HTTP there are 2 interfaces responsible for creation of a Span from a `Message`.
|
||||
These are:
|
||||
|
||||
- `HttpSpanExtractor`
|
||||
- `HttpSpanInjector`
|
||||
|
||||
You can override them by providing your own implementation.
|
||||
|
||||
=== Example
|
||||
|
||||
Let's assume that instead of the standard Zipkin compatible tracing HTTP header names
|
||||
you have
|
||||
|
||||
* for trace id - `correlationId`
|
||||
* for span id - `mySpanId`
|
||||
|
||||
This is a an example of a `SpanExtractor`
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterCustomExtractorTests.java[tags=extractor,indent=0]
|
||||
----
|
||||
|
||||
And you could register it like this:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterCustomExtractorTests.java[tags=configuration,indent=0]
|
||||
----
|
||||
|
||||
Spring Cloud Sleuth does not add trace/span related headers to the Http Response for security reasons. If you need the headers then a custom `SpanInjector`
|
||||
that injects the headers into the Http Response and a Servlet filter which makes use of this can be added the following way:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.java[tags=injector,indent=0]
|
||||
----
|
||||
|
||||
And you could register them like this:
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.java[tags=configuration,indent=0]
|
||||
----
|
||||
// TODO: Update this
|
||||
|
||||
=== TraceFilter
|
||||
|
||||
@@ -436,19 +851,6 @@ add to the Span a tag with key `custom` and a value `tag`.
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java[tags=response_headers,indent=0]
|
||||
----
|
||||
|
||||
=== Custom SA tag in Zipkin
|
||||
|
||||
Sometimes you want to create a manual Span that will wrap a call to an external service which is not instrumented.
|
||||
What you can do is to create a span with the `peer.service` tag that will contain a value of the service that you want to call.
|
||||
Below you can see an example of a call to Redis that is wrapped in such a span.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinSpanReporterTests.java[tags=service_name,indent=0]
|
||||
----
|
||||
|
||||
IMPORTANT: Remember not to add both `peer.service` tag and the `SA` tag! You have to add only `peer.service`.
|
||||
|
||||
=== Custom service name
|
||||
|
||||
By default Sleuth assumes that when you send a span to Zipkin, you want the span's service name
|
||||
@@ -467,26 +869,23 @@ spring.zipkin.service.name: foo
|
||||
Before reporting spans to e.g. Zipkin you can be interested in modifying that span in some way.
|
||||
You can achieve that by using the `SpanAdjuster` interface.
|
||||
|
||||
Example of usage:
|
||||
|
||||
In Sleuth we're generating spans with a fixed name. Some users want to modify the name depending on values
|
||||
of tags. Implementation of the `SpanAdjuster` interface can be used to alter that name. Example:
|
||||
|
||||
[source,yaml]
|
||||
Example. If you register two beans of `SpanAdjuster` type:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SpanAdjuster customSpanAdjuster() {
|
||||
return span -> span.toBuilder().name(scrub(span.getName())).build();
|
||||
}
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/SpanAdjusterAspectTests.java[tags=adjuster,indent=0]
|
||||
----
|
||||
|
||||
This will lead in changing the name of the reported span just before it gets sent to Zipkin.
|
||||
|
||||
IMPORTANT: Your `SpanReporter` should inject the `SpanAdjuster` and
|
||||
allow span manipulation before the actual reporting is done.
|
||||
This will lead in changing the name of the reported span to `foo bar`, just before it gets reported (e.g. to Zipkin).
|
||||
|
||||
=== Host locator
|
||||
|
||||
IMPORTANT: This section is about defining *host* from service discovery. It's *NOT*
|
||||
about finding Zipkin in service discovery.
|
||||
|
||||
In order to define the host that is corresponding to a particular span we need to resolve the host name
|
||||
and port. The default approach is to take it from server properties. If those for some reason are not set
|
||||
then we're trying to retrieve the host name from the network interfaces.
|
||||
@@ -520,68 +919,15 @@ Zipkin's service id inside the URL (example for `zipkinserver` service id)
|
||||
spring.zipkin.baseUrl: http://zipkinserver/
|
||||
----
|
||||
|
||||
== Span Data as Messages
|
||||
== Zipkin Stream Span Consumer
|
||||
|
||||
IMPORTANT: The suggested approach is to use the Zipkin's
|
||||
native support for message based span sending. Starting from
|
||||
Edgware Zipkin Stream server is deprecated and in Finchley
|
||||
it got removed.
|
||||
|
||||
You can accumulate and send span data over
|
||||
http://cloud.spring.io/spring-cloud-stream[Spring Cloud Stream] by
|
||||
including the `spring-cloud-sleuth-stream` jar as a dependency, and
|
||||
adding a Channel Binder implementation
|
||||
(e.g. `spring-cloud-starter-stream-rabbit` for RabbitMQ or
|
||||
`spring-cloud-starter-stream-kafka` for Kafka). This will
|
||||
automatically turn your app into a producer of messages with payload
|
||||
type `Spans`. The channel name to which the spans will be sent
|
||||
is called `sleuth`.
|
||||
|
||||
=== Zipkin Consumer
|
||||
|
||||
Please refer to the http://cloud.spring.io/spring-cloud-static/Dalston.SR4/multi/multi__span_data_as_messages.html#_zipkin_consumer[Dalston Documentaion]
|
||||
on how to create a Stream Zipkin server. That approach has been
|
||||
deprecated in Edgware and removed in Finchley release.
|
||||
|
||||
=== Custom Consumer
|
||||
|
||||
A custom consumer can also easily be implemented using
|
||||
`spring-cloud-sleuth-stream` and binding to the `SleuthSink`. Example:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@EnableBinding(SleuthSink.class)
|
||||
@SpringBootApplication(exclude = SleuthStreamAutoConfiguration.class)
|
||||
@MessageEndpoint
|
||||
public class Consumer {
|
||||
|
||||
@ServiceActivator(inputChannel = SleuthSink.INPUT)
|
||||
public void sink(Spans input) throws Exception {
|
||||
// ... process spans
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: the sample consumer application above explicitly excludes
|
||||
`SleuthStreamAutoConfiguration` so it doesn't send messages to itself,
|
||||
but this is optional (you might actually want to trace requests into
|
||||
the consumer app).
|
||||
|
||||
In order to customize the polling mechanism you can create a bean of `PollerMetadata` type
|
||||
with name equal to `StreamSpanReporter.POLLER`. Here you can find an example of such a configuration.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../../spring-cloud-sleuth-stream/src/test/java/org/springframework/cloud/sleuth/stream/SleuthStreamAutoConfigurationTest.java[tags=custom_poller,indent=0]
|
||||
----
|
||||
|
||||
== Metrics
|
||||
|
||||
Currently Spring Cloud Sleuth registers very simple metrics related to spans.
|
||||
It's using the http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-metrics.html#production-ready-recording-metrics[Spring Boot's metrics support]
|
||||
to calculate the number of accepted and dropped spans. Each time a span gets
|
||||
sent to Zipkin the number of accepted spans will increase. If there's an error then
|
||||
the number of dropped spans will get increased.
|
||||
on how to create a Stream Zipkin server.
|
||||
|
||||
== Integrations
|
||||
|
||||
@@ -672,7 +1018,6 @@ Via the `TraceWebFilter` all sampled incoming requests result in creation of a S
|
||||
like to skip via the `spring.sleuth.web.skipPattern` property. If you have `ManagementServerProperties` on classpath then
|
||||
its value of `contextPath` gets appended to the provided skip pattern.
|
||||
|
||||
|
||||
=== HTTP client integration
|
||||
|
||||
==== Synchronous Rest Template
|
||||
@@ -686,19 +1031,9 @@ If you create a `RestTemplate` instance with a `new` keyword then the instrument
|
||||
|
||||
==== Asynchronous Rest Template
|
||||
|
||||
IMPORTANT: A traced version of an `AsyncRestTemplate` bean is registered for you out of the box. If you
|
||||
have your own bean you have to wrap it in a `TraceAsyncRestTemplate` representation. The best solution
|
||||
is to only customize the `ClientHttpRequestFactory` and / or `AsyncClientHttpRequestFactory`.
|
||||
*If you have your own `AsyncRestTemplate` and you don't wrap it your calls WILL NOT GET TRACED*.
|
||||
|
||||
Custom instrumentation is set to create and close Spans upon sending and receiving requests. You can customize the `ClientHttpRequestFactory`
|
||||
and the `AsyncClientHttpRequestFactory` by registering your beans. Remember to use tracing compatible implementations (e.g. don't forget to
|
||||
wrap `ThreadPoolTaskScheduler` in a `TraceAsyncListenableTaskExecutor`). Example of custom request factories:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebAsyncClientAutoConfigurationTests.java[tags=async_template_factories,indent=0]
|
||||
----
|
||||
IMPORTANT: Starting with Sleuth `2.0.0` we no longer register
|
||||
a bean of `AsyncRestTemplate` type. It's up to you to create such
|
||||
a bean. Then we will instrument it.
|
||||
|
||||
To block the `AsyncRestTemplate` features set `spring.sleuth.web.async.client.enabled` to `false`.
|
||||
To disable creation of the default `TraceAsyncClientHttpRequestFactoryWrapper` set `spring.sleuth.web.async.client.factory.enabled`
|
||||
@@ -711,7 +1046,7 @@ can see an example of how to set up such a custom `AsyncRestTemplate`.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/MultipleAsyncRestTemplateTests.java[tags=custom_async_rest_template,indent=0]
|
||||
include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/MultipleAsyncRestTemplateTests.java[tags=custom_async_rest_template,indent=0]
|
||||
----
|
||||
|
||||
==== WebClient
|
||||
@@ -800,7 +1135,7 @@ can see an example of how to set up such a custom `Executor`.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/MultipleAsyncRestTemplateTests.java[tags=custom_executor,indent=0]
|
||||
include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/MultipleAsyncRestTemplateTests.java[tags=custom_executor,indent=0]
|
||||
----
|
||||
|
||||
=== Messaging
|
||||
|
||||
13
pom.xml
13
pom.xml
@@ -29,7 +29,6 @@
|
||||
<module>spring-cloud-sleuth-dependencies</module>
|
||||
<module>spring-cloud-sleuth-core</module>
|
||||
<module>spring-cloud-sleuth-zipkin</module>
|
||||
<module>spring-cloud-sleuth-stream</module>
|
||||
<module>spring-cloud-starter-sleuth</module>
|
||||
<module>spring-cloud-starter-zipkin</module>
|
||||
<module>spring-cloud-sleuth-samples</module>
|
||||
@@ -253,6 +252,18 @@
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
</repository>
|
||||
<!-- TODO: REMOVE ME - ONLY FOR BRAVE SNAPSHOTS -->
|
||||
<repository>
|
||||
<id>jfrog-snapshots</id>
|
||||
<name>JFrog Snapshots</name>
|
||||
<url>https://oss.jfrog.org/oss-snapshot-local/</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
|
||||
@@ -135,6 +135,24 @@
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- BRAVE -->
|
||||
<dependency>
|
||||
<groupId>io.zipkin.brave</groupId>
|
||||
<artifactId>brave</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.brave</groupId>
|
||||
<artifactId>brave-context-log4j2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.brave</groupId>
|
||||
<artifactId>brave-instrumentation-spring-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.brave</groupId>
|
||||
<artifactId>brave-instrumentation-spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
<!-- BRAVE -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -34,7 +34,7 @@ import org.springframework.core.annotation.AnnotationUtils;
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @see org.springframework.cloud.sleuth.SpanName
|
||||
* @see SpanName
|
||||
*/
|
||||
public class DefaultSpanNamer implements SpanNamer {
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.springframework.cloud.sleuth;
|
||||
|
||||
import brave.SpanCustomizer;
|
||||
|
||||
/**
|
||||
* Contract for hooking into process of adding error response tags.
|
||||
* This interface is only called when an exception is thrown upon receiving a response.
|
||||
@@ -12,11 +14,9 @@ public interface ErrorParser {
|
||||
|
||||
/**
|
||||
* Allows setting of tags when an exception was thrown when the response was received.
|
||||
* The implementation should not manipulate the {@link Span} in other way than just
|
||||
* by adding the tags.
|
||||
*
|
||||
* @param span - current span in context
|
||||
* @param error - error that was thrown upon receiving a response
|
||||
*/
|
||||
void parseErrorTags(Span span, Throwable error);
|
||||
void parseErrorTags(SpanCustomizer span, Throwable error);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package org.springframework.cloud.sleuth;
|
||||
|
||||
import brave.SpanCustomizer;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
/**
|
||||
* {@link ErrorParser} that sets the error tag for an exportable span.
|
||||
@@ -13,16 +12,20 @@ import java.lang.invoke.MethodHandles;
|
||||
*/
|
||||
public class ExceptionMessageErrorParser implements ErrorParser {
|
||||
|
||||
private static final org.apache.commons.logging.Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
private static final Log log = LogFactory.getLog(ExceptionMessageErrorParser.class);
|
||||
|
||||
@Override
|
||||
public void parseErrorTags(Span span, Throwable error) {
|
||||
if (span != null && span.isExportable()) {
|
||||
String errorMsg = ExceptionUtils.getExceptionMessage(error);
|
||||
public void parseErrorTags(SpanCustomizer span, Throwable error) {
|
||||
if (span != null && error != null) {
|
||||
String errorMsg = getExceptionMessage(error);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Adding an error tag [" + errorMsg + "] to span " + span);
|
||||
}
|
||||
span.tag(Span.SPAN_ERROR_TAG_NAME, errorMsg);
|
||||
span.tag("error", errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
private String getExceptionMessage(Throwable e) {
|
||||
return e.getMessage() != null ? e.getMessage() : e.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package org.springframework.cloud.sleuth;
|
||||
|
||||
/**
|
||||
* Internal API that can be changed any time so please do not use it!
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class InternalApi {
|
||||
|
||||
public static void renameSpan(Span span, String newName) {
|
||||
span.name = newName;
|
||||
}
|
||||
}
|
||||
@@ -1,93 +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;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* Represents an event in time associated with a span. Every span has zero or more Logs,
|
||||
* each of which being a timestamped event name.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class Log {
|
||||
/**
|
||||
* The epoch timestamp of the log record; often set via {@link System#currentTimeMillis()}.
|
||||
*/
|
||||
private final long timestamp;
|
||||
|
||||
/**
|
||||
* Event should be the stable name of some notable moment in the lifetime of a span.
|
||||
* For instance, a span representing a browser page load might add an Event for each of the
|
||||
* Performance.timing moments here: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceTiming
|
||||
*
|
||||
* <p>While it is not a formal requirement, Event strings will be most useful if they are *not*
|
||||
* unique; rather, tracing systems should be able to use them to understand how two similar spans
|
||||
* relate from an internal timing perspective.
|
||||
*/
|
||||
private final String event;
|
||||
|
||||
@JsonCreator
|
||||
public Log(
|
||||
@JsonProperty(value = "timestamp", required = true) long timestamp,
|
||||
@JsonProperty(value = "event", required = true) String event
|
||||
) {
|
||||
if (event == null) throw new NullPointerException("event");
|
||||
this.timestamp = timestamp;
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return this.timestamp;
|
||||
}
|
||||
|
||||
public String getEvent() {
|
||||
return this.event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (o instanceof Log) {
|
||||
Log that = (Log) o;
|
||||
return (this.timestamp == that.timestamp)
|
||||
&& (this.event.equals(that.event));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = 1;
|
||||
h *= 1000003;
|
||||
h ^= (this.timestamp >>> 32) ^ this.timestamp;
|
||||
h *= 1000003;
|
||||
h ^= this.event.hashCode();
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "Log{" +
|
||||
"timestamp=" + this.timestamp +
|
||||
", event='" + this.event + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,29 +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;
|
||||
|
||||
/**
|
||||
* Span adjuster that does nothing.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.1.4
|
||||
*/
|
||||
public class NoOpSpanAdjuster implements SpanAdjuster {
|
||||
@Override public Span adjust(Span span) {
|
||||
return span;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +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;
|
||||
|
||||
/**
|
||||
* Span reporter that does nothing
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class NoOpSpanReporter implements SpanReporter {
|
||||
@Override
|
||||
public void report(Span span) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,29 +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;
|
||||
|
||||
/**
|
||||
* Extremely simple callback to determine the frequency that an action should be traced.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface Sampler {
|
||||
/**
|
||||
* @return true if the span is not null and should be exported to the tracing system
|
||||
*/
|
||||
boolean isSampled(Span span);
|
||||
}
|
||||
@@ -1,803 +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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
/**
|
||||
* Class for gathering and reporting statistics about a block of execution.
|
||||
* <p>
|
||||
* Spans should form a directed acyclic graph structure. It should be possible to keep
|
||||
* following the parents of a span until you arrive at a span with no parents.
|
||||
* <p>
|
||||
* Spans can be either annotated with tags or logs.
|
||||
* <p>
|
||||
* An <b>Annotation</b> is used to record existence of an event in time. Below you can
|
||||
* find some of the core annotations used to define the start and stop of a request:
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li><b>cs</b> - Client Sent</li>
|
||||
* <li><b>sr</b> - Server Received</li>
|
||||
* <li><b>ss</b> - Server Sent</li>
|
||||
* <li><b>cr</b> - Client Received</li>
|
||||
* </ul>
|
||||
*
|
||||
* Spring Cloud Sleuth uses Zipkin compatible header names
|
||||
*
|
||||
* <ul>
|
||||
* <li>X-B3-TraceId: 64 encoded bits</li>
|
||||
* <li>X-B3-SpanId: 64 encoded bits</li>
|
||||
* <li>X-B3-ParentSpanId: 64 encoded bits</li>
|
||||
* <li>X-B3-Sampled: Boolean (either “1” or “0”)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
*/
|
||||
/*
|
||||
* OpenTracing spans can affect the trace tree by creating children. In this way, they are
|
||||
* like scoped tracers. Sleuth spans are DTOs, whose sole responsibility is the current
|
||||
* span in the trace tree.
|
||||
*/
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
|
||||
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
|
||||
public class Span implements SpanContext {
|
||||
|
||||
public static final String SAMPLED_NAME = "X-B3-Sampled";
|
||||
public static final String PROCESS_ID_NAME = "X-Process-Id";
|
||||
public static final String PARENT_ID_NAME = "X-B3-ParentSpanId";
|
||||
public static final String TRACE_ID_NAME = "X-B3-TraceId";
|
||||
public static final String SPAN_NAME_NAME = "X-Span-Name";
|
||||
public static final String SPAN_ID_NAME = "X-B3-SpanId";
|
||||
public static final String SPAN_EXPORT_NAME = "X-Span-Export";
|
||||
public static final String SPAN_FLAGS = "X-B3-Flags";
|
||||
public static final String SPAN_BAGGAGE_HEADER_PREFIX = "baggage";
|
||||
public static final Set<String> SPAN_HEADERS = new HashSet<>(
|
||||
Arrays.asList(SAMPLED_NAME, PROCESS_ID_NAME, PARENT_ID_NAME, TRACE_ID_NAME,
|
||||
SPAN_ID_NAME, SPAN_NAME_NAME, SPAN_EXPORT_NAME));
|
||||
|
||||
public static final String SPAN_SAMPLED = "1";
|
||||
public static final String SPAN_NOT_SAMPLED = "0";
|
||||
|
||||
public static final String SPAN_LOCAL_COMPONENT_TAG_NAME = "lc";
|
||||
public static final String SPAN_ERROR_TAG_NAME = "error";
|
||||
|
||||
/**
|
||||
* <b>cr</b> - Client Receive. Signifies the end of the span. The client has
|
||||
* successfully received the response from the server side. If one subtracts the cs
|
||||
* timestamp from this timestamp one will receive the whole time needed by the client
|
||||
* to receive the response from the server.
|
||||
*/
|
||||
public static final String CLIENT_RECV = "cr";
|
||||
|
||||
/**
|
||||
* <b>cs</b> - Client Sent. The client has made a request (a client can be e.g.
|
||||
* {@link org.springframework.web.client.RestTemplate}. This annotation depicts the
|
||||
* start of the span.
|
||||
*/
|
||||
// For an outbound RPC call, it should log a "cs" annotation.
|
||||
// If possible, it should log a binary annotation of "sa", indicating the
|
||||
// destination address.
|
||||
public static final String CLIENT_SEND = "cs";
|
||||
|
||||
/**
|
||||
* <b>sr</b> - Server Receive. The server side got the request and will start
|
||||
* processing it. If one subtracts the cs timestamp from this timestamp one will
|
||||
* receive the network latency.
|
||||
*/
|
||||
// If an inbound RPC call, it should log a "sr" annotation.
|
||||
// If possible, it should log a binary annotation of "ca", indicating the
|
||||
// caller's address (ex X-Forwarded-For header)
|
||||
public static final String SERVER_RECV = "sr";
|
||||
|
||||
/**
|
||||
* <b>ss</b> - Server Send. Annotated upon completion of request processing (when the
|
||||
* response got sent back to the client). If one subtracts the sr timestamp from this
|
||||
* timestamp one will receive the time needed by the server side to process the
|
||||
* request.
|
||||
*/
|
||||
public static final String SERVER_SEND = "ss";
|
||||
|
||||
/**
|
||||
* <a href="https://github.com/opentracing/opentracing-go/blob/master/ext/tags.go">As
|
||||
* in Open Tracing</a>
|
||||
*/
|
||||
public static final String SPAN_PEER_SERVICE_TAG_NAME = "peer.service";
|
||||
|
||||
/**
|
||||
* ID of the instance from which the span was originated.
|
||||
*/
|
||||
public static final String INSTANCEID = "spring.instance_id";
|
||||
|
||||
private final long begin;
|
||||
private long end = 0;
|
||||
volatile String name;
|
||||
private final long traceIdHigh;
|
||||
private final long traceId;
|
||||
private List<Long> parents = new ArrayList<>();
|
||||
private final long spanId;
|
||||
private boolean remote = false;
|
||||
private boolean exportable = true;
|
||||
private final Map<String, String> tags;
|
||||
private final String processId;
|
||||
private final Collection<Log> logs;
|
||||
private final Span savedSpan;
|
||||
@JsonIgnore
|
||||
private final Map<String,String> baggage;
|
||||
|
||||
// Null means we don't know the start tick, so fallback to time
|
||||
@JsonIgnore
|
||||
private final Long startNanos;
|
||||
private Long durationMicros; // serialized in json so micros precision isn't lost
|
||||
/*
|
||||
Using B3 propagation, it is most typical to share the same span ID across client and
|
||||
the server. This has backend implications like who owns the timestamp (hint the
|
||||
client does). When a SpanReporter receives a completed span, it should know if it
|
||||
is shared or not.
|
||||
*/
|
||||
private final boolean shared;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Span() {
|
||||
this(-1, -1, "dummy", 0, Collections.<Long>emptyList(), 0, false, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new span that still tracks tags and logs of the current span. This is
|
||||
* crucial when continuing spans since the changes in those collections done in the
|
||||
* continued span need to be reflected until the span gets closed.
|
||||
*
|
||||
* @deprecated - use {@link SpanBuilder}
|
||||
*/
|
||||
@Deprecated
|
||||
public Span(Span current, Span savedSpan) {
|
||||
this.begin = current.getBegin();
|
||||
this.end = current.getEnd();
|
||||
this.name = current.getName();
|
||||
this.traceIdHigh = current.getTraceIdHigh();
|
||||
this.traceId = current.getTraceId();
|
||||
this.parents = current.getParents();
|
||||
this.spanId = current.getSpanId();
|
||||
this.remote = current.isRemote();
|
||||
this.exportable = current.isExportable();
|
||||
this.processId = current.getProcessId();
|
||||
this.tags = current.tags;
|
||||
this.logs = current.logs;
|
||||
this.startNanos = current.startNanos;
|
||||
this.durationMicros = current.durationMicros;
|
||||
this.baggage = current.baggage;
|
||||
this.savedSpan = savedSpan;
|
||||
this.shared = current.shared;
|
||||
}
|
||||
|
||||
Span(long begin, long end, String name, long traceId, List<Long> parents,
|
||||
long spanId, boolean remote, boolean exportable, String processId) {
|
||||
this(begin, end, name, traceId, parents, spanId, remote, exportable, processId,
|
||||
null, false);
|
||||
}
|
||||
|
||||
Span(long begin, long end, String name, long traceId, List<Long> parents,
|
||||
long spanId, boolean remote, boolean exportable, String processId,
|
||||
Span savedSpan, boolean shared) {
|
||||
this(new SpanBuilder()
|
||||
.begin(begin)
|
||||
.end(end)
|
||||
.name(name)
|
||||
.traceId(traceId)
|
||||
.parents(parents)
|
||||
.spanId(spanId)
|
||||
.remote(remote)
|
||||
.exportable(exportable)
|
||||
.processId(processId)
|
||||
.savedSpan(savedSpan)
|
||||
.shared(shared));
|
||||
}
|
||||
|
||||
Span(SpanBuilder builder) {
|
||||
if (builder.begin > 0) { // conventionally, 0 indicates unset
|
||||
this.startNanos = null; // don't know the start tick
|
||||
this.begin = builder.begin;
|
||||
} else {
|
||||
this.startNanos = nanoTime();
|
||||
this.begin = System.currentTimeMillis();
|
||||
}
|
||||
if (builder.end > 0) {
|
||||
this.end = builder.end;
|
||||
this.durationMicros = (this.end - this.begin) * 1000;
|
||||
}
|
||||
this.name = builder.name != null ? builder.name : "";
|
||||
this.traceIdHigh = builder.traceIdHigh;
|
||||
this.traceId = builder.traceId;
|
||||
this.parents.addAll(builder.parents);
|
||||
this.spanId = builder.spanId;
|
||||
this.remote = builder.remote;
|
||||
this.exportable = builder.exportable;
|
||||
this.processId = builder.processId;
|
||||
this.savedSpan = builder.savedSpan;
|
||||
this.tags = new ConcurrentHashMap<>();
|
||||
this.tags.putAll(builder.tags);
|
||||
this.logs = new ConcurrentLinkedQueue<>();
|
||||
this.logs.addAll(builder.logs);
|
||||
this.baggage = new ConcurrentHashMap<>();
|
||||
this.baggage.putAll(builder.baggage);
|
||||
this.shared = builder.shared;
|
||||
}
|
||||
|
||||
public static SpanBuilder builder() {
|
||||
return new SpanBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* The block has completed, stop the clock
|
||||
*/
|
||||
public synchronized void stop() {
|
||||
if (this.durationMicros == null) {
|
||||
if (this.begin == 0) {
|
||||
throw new IllegalStateException(
|
||||
"Span for " + this.name + " has not been started");
|
||||
}
|
||||
if (this.end == 0) {
|
||||
this.end = System.currentTimeMillis();
|
||||
}
|
||||
if (this.startNanos != null) { // set a precise duration
|
||||
this.durationMicros = Math.max(1, (nanoTime() - this.startNanos) / 1000);
|
||||
} else {
|
||||
this.durationMicros = (this.end - this.begin) * 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total amount of time elapsed since start was called, if running, or
|
||||
* difference between stop and start, in microseconds.
|
||||
*
|
||||
* Note that in case of the spans that have CS / CR events we will not
|
||||
* send to Zipkin the accumulated microseconds but will calculate the
|
||||
* duration basing on the timestamps of the CS / CR events.
|
||||
*
|
||||
* @return zero if not running, or a positive number of microseconds.
|
||||
*/
|
||||
@JsonIgnore
|
||||
public synchronized long getAccumulatedMicros() {
|
||||
if (this.durationMicros != null) {
|
||||
return this.durationMicros;
|
||||
} else { // stop() hasn't yet been called
|
||||
if (this.begin == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (this.startNanos != null) {
|
||||
return Math.max(1, (nanoTime() - this.startNanos) / 1000);
|
||||
} else {
|
||||
return (System.currentTimeMillis() - this.begin) * 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Visible for testing
|
||||
@JsonIgnore
|
||||
long nanoTime() {
|
||||
return System.nanoTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the span been started and not yet stopped?
|
||||
*/
|
||||
@JsonIgnore
|
||||
public synchronized boolean isRunning() {
|
||||
return this.begin != 0 && this.durationMicros == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a tag or data annotation associated with this span. The tag will be added only
|
||||
* if it has a value.
|
||||
*/
|
||||
public void tag(String key, String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
this.tags.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an {@link Log#event event} to the timeline associated with this span.
|
||||
*/
|
||||
public void logEvent(String event) {
|
||||
logEvent(System.currentTimeMillis(), event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link Log#event event} to a specific point (a timestamp in milliseconds) in the timeline
|
||||
* associated with this span.
|
||||
*/
|
||||
public void logEvent(long timestampMilliseconds, String event) {
|
||||
this.logs.add(new Log(timestampMilliseconds, event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a baggage item in the Span (and its SpanContext) as a key/value pair.
|
||||
*
|
||||
* Baggage enables powerful distributed context propagation functionality where arbitrary application data can be
|
||||
* carried along the full path of request execution throughout the system.
|
||||
*
|
||||
* Note 1: Baggage is only propagated to the future (recursive) children of this SpanContext.
|
||||
*
|
||||
* Note 2: Baggage is sent in-band with every subsequent local and remote calls, so this feature must be used with
|
||||
* care.
|
||||
*
|
||||
* @return this Span instance, for chaining
|
||||
*/
|
||||
public Span setBaggageItem(String key, String value) {
|
||||
this.baggage.put(key.toLowerCase(), value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the value of the baggage item identified by the given key, or null if no such item could be found
|
||||
*/
|
||||
public String getBaggageItem(String key) {
|
||||
return this.baggage.get(key.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Iterable<Map.Entry<String,String>> baggageItems() {
|
||||
return this.baggage.entrySet();
|
||||
}
|
||||
|
||||
public final Map<String,String> getBaggage() {
|
||||
return Collections.unmodifiableMap(this.baggage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tag data associated with this span (read only)
|
||||
* <p/>
|
||||
* <p/>
|
||||
* Will never be null.
|
||||
*/
|
||||
public Map<String, String> tags() {
|
||||
return Collections.unmodifiableMap(new LinkedHashMap<>(this.tags));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get any timestamped events (read only)
|
||||
* <p/>
|
||||
* <p/>
|
||||
* Will never be null.
|
||||
*/
|
||||
public List<Log> logs() {
|
||||
return Collections.unmodifiableList(new ArrayList<>(this.logs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the saved span. The one that was "current" before this span.
|
||||
* <p>
|
||||
* Might be null
|
||||
*/
|
||||
@JsonIgnore
|
||||
public Span getSavedSpan() {
|
||||
return this.savedSpan;
|
||||
}
|
||||
|
||||
public boolean hasSavedSpan() {
|
||||
return this.savedSpan != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A human-readable name assigned to this span instance.
|
||||
* <p>
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* A pseudo-unique (random) number assigned to this span instance.
|
||||
* <p>
|
||||
* <p>
|
||||
* The span id is immutable and cannot be changed. It is safe to access this from
|
||||
* multiple threads.
|
||||
*/
|
||||
public long getSpanId() {
|
||||
return this.spanId;
|
||||
}
|
||||
|
||||
/**
|
||||
* When non-zero, the trace containing this span uses 128-bit trace identifiers.
|
||||
*
|
||||
* <p>{@code traceIdHigh} corresponds to the high bits in big-endian format and
|
||||
* {@link #getTraceId()} corresponds to the low bits.
|
||||
*
|
||||
* <p>Ex. to convert the two fields to a 128bit opaque id array, you'd use code like below.
|
||||
* <pre>{@code
|
||||
* ByteBuffer traceId128 = ByteBuffer.allocate(16);
|
||||
* traceId128.putLong(span.getTraceIdHigh());
|
||||
* traceId128.putLong(span.getTraceId());
|
||||
* traceBytes = traceId128.array();
|
||||
* }</pre>
|
||||
*
|
||||
* @see #traceIdString()
|
||||
* @since 1.0.11
|
||||
*/
|
||||
public long getTraceIdHigh() {
|
||||
return this.traceIdHigh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unique 8-byte identifier for a trace, set on all spans within it.
|
||||
*
|
||||
* @see #getTraceIdHigh() for notes about 128-bit trace identifiers
|
||||
*/
|
||||
public long getTraceId() {
|
||||
return this.traceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a unique id for the process from which this span originated.
|
||||
* <p>
|
||||
* Might be null
|
||||
*/
|
||||
public String getProcessId() {
|
||||
return this.processId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent IDs of the span.
|
||||
* <p>
|
||||
* <p>
|
||||
* The collection will be empty if there are no parents.
|
||||
*/
|
||||
public List<Long> getParents() {
|
||||
return this.parents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag that tells us whether the span was started in another process. Useful in RPC
|
||||
* tracing when the receiver actually has to add annotations to the senders span.
|
||||
*/
|
||||
public boolean isRemote() {
|
||||
return this.remote;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the start time, in milliseconds
|
||||
*/
|
||||
public long getBegin() {
|
||||
return this.begin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stop time, in milliseconds
|
||||
*/
|
||||
public long getEnd() {
|
||||
return this.end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the span eligible for export? If not then we may not need accumulate annotations
|
||||
* (for instance).
|
||||
*/
|
||||
public boolean isExportable() {
|
||||
return this.exportable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Span and trace id got extracted from a carrier?
|
||||
* We are adding data to the same span created by a remote client
|
||||
*
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public boolean isShared() {
|
||||
return this.shared;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 16 or 32 character hex representation of the span's trace ID
|
||||
*
|
||||
* @since 1.0.11
|
||||
*/
|
||||
public String traceIdString() {
|
||||
if (this.traceIdHigh != 0) {
|
||||
char[] result = new char[32];
|
||||
writeHexLong(result, 0, this.traceIdHigh);
|
||||
writeHexLong(result, 16, this.traceId);
|
||||
return new String(result);
|
||||
}
|
||||
char[] result = new char[16];
|
||||
writeHexLong(result, 0, this.traceId);
|
||||
return new String(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the span to a {@link SpanBuilder} format
|
||||
*/
|
||||
public SpanBuilder toBuilder() {
|
||||
return builder().from(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents given long id as 16-character lower-hex string
|
||||
*
|
||||
* @see #traceIdString()
|
||||
*/
|
||||
public static String idToHex(long id) {
|
||||
char[] data = new char[16];
|
||||
writeHexLong(data, 0, id);
|
||||
return new String(data);
|
||||
}
|
||||
|
||||
/** Inspired by {@code okio.Buffer.writeLong} */
|
||||
static void writeHexLong(char[] data, int pos, long v) {
|
||||
writeHexByte(data, pos + 0, (byte) ((v >>> 56L) & 0xff));
|
||||
writeHexByte(data, pos + 2, (byte) ((v >>> 48L) & 0xff));
|
||||
writeHexByte(data, pos + 4, (byte) ((v >>> 40L) & 0xff));
|
||||
writeHexByte(data, pos + 6, (byte) ((v >>> 32L) & 0xff));
|
||||
writeHexByte(data, pos + 8, (byte) ((v >>> 24L) & 0xff));
|
||||
writeHexByte(data, pos + 10, (byte) ((v >>> 16L) & 0xff));
|
||||
writeHexByte(data, pos + 12, (byte) ((v >>> 8L) & 0xff));
|
||||
writeHexByte(data, pos + 14, (byte) (v & 0xff));
|
||||
}
|
||||
|
||||
static void writeHexByte(char[] data, int pos, byte b) {
|
||||
data[pos + 0] = HEX_DIGITS[(b >> 4) & 0xf];
|
||||
data[pos + 1] = HEX_DIGITS[b & 0xf];
|
||||
}
|
||||
|
||||
static final char[] HEX_DIGITS =
|
||||
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
|
||||
/**
|
||||
* Parses a 1 to 32 character lower-hex string with no prefix into an unsigned long, tossing any
|
||||
* bits higher than 64.
|
||||
*/
|
||||
public static long hexToId(String hexString) {
|
||||
Assert.hasText(hexString, "Can't convert empty hex string to long");
|
||||
int length = hexString.length();
|
||||
if (length < 1 || length > 32) throw new IllegalArgumentException("Malformed id: " + hexString);
|
||||
|
||||
// trim off any high bits
|
||||
int beginIndex = length > 16 ? length - 16 : 0;
|
||||
|
||||
return hexToId(hexString, beginIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a 16 character lower-hex string with no prefix into an unsigned long, starting at the
|
||||
* specified index.
|
||||
*
|
||||
* @since 1.0.11
|
||||
*/
|
||||
public static long hexToId(String lowerHex, int index) {
|
||||
Assert.hasText(lowerHex, "Can't convert empty hex string to long");
|
||||
long result = 0;
|
||||
for (int endIndex = Math.min(index + 16, lowerHex.length()); index < endIndex; index++) {
|
||||
char c = lowerHex.charAt(index);
|
||||
result <<= 4;
|
||||
if (c >= '0' && c <= '9') {
|
||||
result |= c - '0';
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
result |= c - 'a' + 10;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Malformed id: " + lowerHex);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[Trace: " + traceIdString() + ", Span: " + idToHex(this.spanId)
|
||||
+ ", Parent: " + getParentIdIfPresent() + ", exportable:" + this.exportable + "]";
|
||||
}
|
||||
|
||||
private String getParentIdIfPresent() {
|
||||
return this.getParents().isEmpty() ? "null" : idToHex(this.getParents().get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = 1;
|
||||
h *= 1000003;
|
||||
h ^= (this.traceIdHigh >>> 32) ^ this.traceIdHigh;
|
||||
h *= 1000003;
|
||||
h ^= (this.traceId >>> 32) ^ this.traceId;
|
||||
h *= 1000003;
|
||||
h ^= (this.spanId >>> 32) ^ this.spanId;
|
||||
h *= 1000003;
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (o instanceof Span) {
|
||||
Span that = (Span) o;
|
||||
return (this.traceIdHigh == that.traceIdHigh)
|
||||
&& (this.traceId == that.traceId)
|
||||
&& (this.spanId == that.spanId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class SpanBuilder {
|
||||
private long begin;
|
||||
private long end;
|
||||
private String name;
|
||||
private long traceIdHigh;
|
||||
private long traceId;
|
||||
private final ArrayList<Long> parents = new ArrayList<>();
|
||||
private long spanId;
|
||||
private boolean remote;
|
||||
private boolean exportable = true;
|
||||
private String processId;
|
||||
private Span savedSpan;
|
||||
private final List<Log> logs = new ArrayList<>();
|
||||
private final Map<String, String> tags = new LinkedHashMap<>();
|
||||
private final Map<String, String> baggage = new LinkedHashMap<>();
|
||||
private boolean shared;
|
||||
|
||||
SpanBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to record a begin time of a Span you didn't start. Don't call this when you are
|
||||
* starting the span.
|
||||
*
|
||||
* <p>In other words, don't call {@code builder.begin(System.currentTimeMillis());}. doing so is
|
||||
* redundant and will result in less precision when calculating elapsed time.
|
||||
*/
|
||||
public Span.SpanBuilder begin(long begin) {
|
||||
this.begin = begin;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder end(long end) {
|
||||
this.end = end;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder traceIdHigh(long traceIdHigh) {
|
||||
this.traceIdHigh = traceIdHigh;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder traceId(long traceId) {
|
||||
this.traceId = traceId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder parent(Long parent) {
|
||||
this.parents.add(parent);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder parents(Collection<Long> parents) {
|
||||
this.parents.clear();
|
||||
this.parents.addAll(parents);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder log(Log log) {
|
||||
this.logs.add(log);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder logs(Collection<Log> logs) {
|
||||
this.logs.clear();
|
||||
this.logs.addAll(logs);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder tag(String tagKey, String tagValue) {
|
||||
this.tags.put(tagKey, tagValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder tags(Map<String, String> tags) {
|
||||
this.tags.clear();
|
||||
this.tags.putAll(tags);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder baggage(String baggageKey, String baggageValue) {
|
||||
this.baggage.put(baggageKey.toLowerCase(), baggageValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder baggage(Map<String, String> baggage) {
|
||||
this.baggage.putAll(baggage);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder spanId(long spanId) {
|
||||
this.spanId = spanId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder remote(boolean remote) {
|
||||
this.remote = remote;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder exportable(boolean exportable) {
|
||||
this.exportable = exportable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder processId(String processId) {
|
||||
this.processId = processId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder savedSpan(Span savedSpan) {
|
||||
this.savedSpan = savedSpan;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder shared(boolean shared) {
|
||||
this.shared = shared;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Span.SpanBuilder} from the {@link Span}.
|
||||
*/
|
||||
public Span.SpanBuilder from(Span span) {
|
||||
return begin(span.begin).end(span.end).name(span.name)
|
||||
.traceIdHigh(span.traceIdHigh).traceId(span.traceId)
|
||||
.parents(span.getParents()).logs(span.logs).tags(span.tags).baggage(span.baggage)
|
||||
.spanId(span.spanId).remote(span.remote).exportable(span.exportable)
|
||||
.processId(span.processId).savedSpan(span.savedSpan);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a span. All collections lik baggage / tags / logs are *copied*, not continued.
|
||||
* In other words if you add a tag to the input {@link Span}, the created span
|
||||
* will not reflect that change.
|
||||
*/
|
||||
public Span build() {
|
||||
return new Span(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new Span(this).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +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;
|
||||
|
||||
/**
|
||||
* Strategy for accessing the current span. This is the primary interface for use by user
|
||||
* code (if it needs access to spans at all - in general it is better to leave span access
|
||||
* to specialized and cross-cutting instrumentation code).
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface SpanAccessor {
|
||||
|
||||
/**
|
||||
* Retrieves the span that is present in the context. If currently there is
|
||||
* no tracing going on, then this method will return {@code null}.
|
||||
*/
|
||||
Span getCurrentSpan();
|
||||
|
||||
/**
|
||||
* Returns {@code true} when a span is present in the current context. In other
|
||||
* words if a span was started or continued then this method returns {@code true}.
|
||||
*/
|
||||
boolean isTracing();
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -16,25 +16,29 @@
|
||||
|
||||
package org.springframework.cloud.sleuth;
|
||||
|
||||
import zipkin2.Span;
|
||||
|
||||
/**
|
||||
* Adds ability to adjust a span before reporting it.
|
||||
*
|
||||
* IMPORTANT: Your {@link SpanReporter} should inject the collection of {@link SpanAdjuster} and
|
||||
* allow {@link Span} manipulation before the actual reporting is done.
|
||||
* <b>IMPORTANT</b> - if you override the default {@link brave.Tracing} implementation,
|
||||
* remember to ensure that you pass to it an adjusted version of the {@link zipkin2.reporter.Reporter<zipkin2.Span>}
|
||||
* bean. In other words you must reuse the list of available {@link SpanAdjuster}s and
|
||||
* wrap the provided {@link zipkin2.reporter.Reporter} interface with it.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.1.4
|
||||
*/
|
||||
public interface SpanAdjuster {
|
||||
/**
|
||||
* You can adjust the {@link Span} by creating a new one using the {@link Span.SpanBuilder}
|
||||
* You can adjust the {@link zipkin2.Span} by creating a new one using the {@link Span#toBuilder()}
|
||||
* before reporting it.
|
||||
*
|
||||
* In Sleuth we're generating spans with a fixed name. Some users want to modify the name
|
||||
* With the legacy Sleuth approach we're generating spans with a fixed name. Some users want to modify the name
|
||||
* depending on some values of tags. Implementation of this interface can be used to alter
|
||||
* then name. Example:
|
||||
*
|
||||
* {@code span -> span.toBuilder().name(scrub(span.getName())).build();}
|
||||
*/
|
||||
Span adjust(Span span);
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package org.springframework.cloud.sleuth;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Adopted from: https://github.com/opentracing/opentracing-java/blob/0.16.0/opentracing-api/src/main/java/io/opentracing/SpanContext.java
|
||||
*
|
||||
* SpanContext represents Span state that must propagate to descendant Spans and across process boundaries.
|
||||
*
|
||||
* SpanContext is logically divided into two pieces: (1) the user-level "Baggage" that propagates across Span
|
||||
* boundaries and (2) any Tracer-implementation-specific fields that are needed to identify or otherwise contextualize
|
||||
* the associated Span instance (e.g., a <trace_id, span_id, sampled> tuple).
|
||||
*
|
||||
* The {@link SpanContext#baggageItems()} returns the map of user-level "Baggage".
|
||||
*
|
||||
* @see Span#setBaggageItem(String, String)
|
||||
* @see Span#getBaggageItem(String)
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public interface SpanContext {
|
||||
|
||||
/**
|
||||
* @return all zero or more baggage items propagating along with the associated Span
|
||||
*
|
||||
* @see Span#setBaggageItem(String, String)
|
||||
* @see Span#getBaggageItem(String)
|
||||
*/
|
||||
Iterable<Map.Entry<String, String>> baggageItems();
|
||||
}
|
||||
@@ -1,42 +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;
|
||||
|
||||
/**
|
||||
* Adopted from <a href=
|
||||
* "https://github.com/opentracing/opentracing-java/pull/11/files#diff-eb9c3460aba76aabc0de04b05e4a2b3d">
|
||||
* </a>OpenTracing</a>
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface SpanExtractor<T> {
|
||||
/**
|
||||
* Returns a SpanBuilder provided a “carrier” object from which to extract identifying
|
||||
* information needed by the new Span instance.
|
||||
*
|
||||
* If the carrier object has no such span stored within it, a new Span is created.
|
||||
*
|
||||
* Unless there’s an error, it returns a Span. The Span generated from the builder can
|
||||
* be used in the host process like any other.
|
||||
*
|
||||
* (Note that some OpenTracing implementations consider the Spans on either side of an
|
||||
* RPC to have the same identity, and others consider the caller to be the parent and
|
||||
* the receiver to be the child).
|
||||
*/
|
||||
Span joinTrace(T carrier);
|
||||
}
|
||||
@@ -1,43 +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;
|
||||
|
||||
/**
|
||||
* Adopted from <a href=
|
||||
* "https://github.com/opentracing/opentracing-java/blob/master/opentracing/src/main/java/opentracing/Tracer.java">
|
||||
* </a>OpenTracing</a>
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface SpanInjector<T> {
|
||||
/**
|
||||
* Takes two arguments:
|
||||
* <ul>
|
||||
* <li>a Span instance, and</li>
|
||||
* <li>a “carrier” object in which to inject that Span for cross-process propagation.
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* A “carrier” object is some sort of http or rpc envelope, for example HeaderGroup
|
||||
* (from Apache HttpComponents).
|
||||
*
|
||||
* Attempting to inject to a carrier that has been registered/configured to this
|
||||
* Tracer will result in a IllegalStateException.
|
||||
*/
|
||||
void inject(Span span, T carrier);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -24,7 +24,7 @@ import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation to provide the name for the span. You should annotate all your
|
||||
* custom {@link java.lang.Runnable Runnable} or {@link java.util.concurrent.Callable Callable} classes
|
||||
* custom {@link Runnable Runnable} or {@link java.util.concurrent.Callable Callable} classes
|
||||
* for the instrumentation logic to pick up how to name the span.
|
||||
* <p>
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
|
||||
@@ -1,34 +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;
|
||||
|
||||
/**
|
||||
* Contract for reporting Sleuth spans for collection. For example to Zipkin.
|
||||
*
|
||||
* IMPORTANT: Your {@link SpanReporter} should inject the {@link SpanAdjuster} and
|
||||
* allow {@link Span} manipulation before the actual reporting is done.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface SpanReporter {
|
||||
/**
|
||||
* Reports a completed span out of band, usually out of process.
|
||||
* This is typically to a trace depot (ex zipkin) or a log file.
|
||||
*/
|
||||
void report(Span span);
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package org.springframework.cloud.sleuth;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Adopted from: https://github.com/opentracing/opentracing-java/blob/0.16.0/opentracing-api/src/main/java/io/opentracing/propagation/TextMap.java
|
||||
*
|
||||
* TextMap is a built-in carrier for {@link SpanInjector} and {@link SpanExtractor}. TextMap implementations allows Tracers to
|
||||
* read and write key:value String pairs from arbitrary underlying sources of data.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public interface SpanTextMap extends Iterable<Map.Entry<String, String>> {
|
||||
/**
|
||||
* Gets an iterator over arbitrary key:value pairs from the TextMapReader.
|
||||
*
|
||||
* @return entries in the TextMap backing store; note that for some Formats, the iterator may include entries that
|
||||
* were never injected by a Tracer implementation (e.g., unrelated HTTP headers)
|
||||
*/
|
||||
Iterator<Map.Entry<String,String>> iterator();
|
||||
|
||||
/**
|
||||
* Puts a key:value pair into the TextMapWriter's backing store.
|
||||
*
|
||||
* @param key a String, possibly with constraints dictated by the particular Format this TextMap is paired with
|
||||
* @param value a String, possibly with constraints dictated by the particular Format this TextMap is paired with
|
||||
*/
|
||||
void put(String key, String value);
|
||||
}
|
||||
@@ -1,100 +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;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* Callable that passes Span between threads. The Span name is
|
||||
* taken either from the passed value or from the {@link SpanNamer}
|
||||
* interface.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class TraceCallable<V> implements Callable<V> {
|
||||
|
||||
private final Tracer tracer;
|
||||
private final SpanNamer spanNamer;
|
||||
private final Callable<V> delegate;
|
||||
private final String name;
|
||||
private final Span parent;
|
||||
|
||||
public TraceCallable(Tracer tracer, SpanNamer spanNamer, Callable<V> delegate) {
|
||||
this(tracer, spanNamer, delegate, null);
|
||||
}
|
||||
|
||||
public TraceCallable(Tracer tracer, SpanNamer spanNamer, Callable<V> delegate, String name) {
|
||||
this.tracer = tracer;
|
||||
this.spanNamer = spanNamer;
|
||||
this.delegate = delegate;
|
||||
this.name = name;
|
||||
this.parent = tracer.getCurrentSpan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V call() throws Exception {
|
||||
Span span = startSpan();
|
||||
try {
|
||||
return this.getDelegate().call();
|
||||
}
|
||||
finally {
|
||||
close(span);
|
||||
}
|
||||
}
|
||||
|
||||
protected Span startSpan() {
|
||||
return this.tracer.createSpan(getSpanName(), this.parent);
|
||||
}
|
||||
|
||||
protected String getSpanName() {
|
||||
if (this.name != null) {
|
||||
return this.name;
|
||||
}
|
||||
return this.spanNamer.name(this.delegate, "async");
|
||||
}
|
||||
|
||||
protected void close(Span span) {
|
||||
this.tracer.close(span);
|
||||
}
|
||||
|
||||
protected Span continueSpan(Span span) {
|
||||
return this.tracer.continueSpan(span);
|
||||
}
|
||||
|
||||
protected Span detachSpan(Span span) {
|
||||
return this.tracer.detach(span);
|
||||
}
|
||||
|
||||
public Tracer getTracer() {
|
||||
return this.tracer;
|
||||
}
|
||||
|
||||
public Callable<V> getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public Span getParent() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -22,7 +22,7 @@ import java.util.LinkedHashSet;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Well-known {@link org.springframework.cloud.sleuth.Span#tag(String, String) span tag}
|
||||
* Well-known {@link brave.Span#tag(String, String) span tag}
|
||||
* keys.
|
||||
*
|
||||
* <h3>Overhead of adding Trace Data</h3>
|
||||
|
||||
@@ -1,110 +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;
|
||||
|
||||
/**
|
||||
* Runnable that passes Span between threads. The Span name is
|
||||
* taken either from the passed value or from the {@link SpanNamer}
|
||||
* interface.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class TraceRunnable implements Runnable {
|
||||
|
||||
/**
|
||||
* Since we don't know the exact operation name we provide a default
|
||||
* name for the Span
|
||||
*/
|
||||
private static final String DEFAULT_SPAN_NAME = "async";
|
||||
|
||||
private final Tracer tracer;
|
||||
private final SpanNamer spanNamer;
|
||||
private final Runnable delegate;
|
||||
private final String name;
|
||||
private final Span parent;
|
||||
|
||||
public TraceRunnable(Tracer tracer, SpanNamer spanNamer, Runnable delegate) {
|
||||
this(tracer, spanNamer, delegate, null);
|
||||
}
|
||||
|
||||
public TraceRunnable(Tracer tracer, SpanNamer spanNamer, Runnable delegate, String name) {
|
||||
this.tracer = tracer;
|
||||
this.spanNamer = spanNamer;
|
||||
this.delegate = delegate;
|
||||
this.name = name;
|
||||
this.parent = tracer.getCurrentSpan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Span span = startSpan();
|
||||
try {
|
||||
this.getDelegate().run();
|
||||
}
|
||||
finally {
|
||||
close(span);
|
||||
}
|
||||
}
|
||||
|
||||
protected Span startSpan() {
|
||||
return this.tracer.createSpan(getSpanName(), this.parent);
|
||||
}
|
||||
|
||||
protected String getSpanName() {
|
||||
if (this.name != null) {
|
||||
return this.name;
|
||||
}
|
||||
return this.spanNamer.name(this.delegate, DEFAULT_SPAN_NAME);
|
||||
}
|
||||
|
||||
protected void close(Span span) {
|
||||
// race conditions - check #447
|
||||
if (!this.tracer.isTracing()) {
|
||||
this.tracer.continueSpan(span);
|
||||
}
|
||||
this.tracer.close(span);
|
||||
}
|
||||
|
||||
protected Span continueSpan(Span span) {
|
||||
return this.tracer.continueSpan(span);
|
||||
}
|
||||
|
||||
protected Span detachSpan(Span span) {
|
||||
if (this.tracer.isTracing()) {
|
||||
return this.tracer.detach(span);
|
||||
}
|
||||
return span;
|
||||
}
|
||||
|
||||
public Tracer getTracer() {
|
||||
return this.tracer;
|
||||
}
|
||||
|
||||
public Runnable getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public Span getParent() {
|
||||
return this.parent;
|
||||
}
|
||||
}
|
||||
@@ -1,145 +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;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* The Tracer class is the primary way for instrumentation code (note user code) to
|
||||
* interact with the library. It provides methods to create and manipulate spans.
|
||||
* <p>
|
||||
*
|
||||
* A 'span' represents a length of time. It has many other attributes such as a name, ID,
|
||||
* and even potentially a set of key/value strings attached to it.
|
||||
* <p>
|
||||
*
|
||||
* Each thread in your application has a single currently active currentSpan associated
|
||||
* with it. When this is non-null, it represents the current operation that the thread is
|
||||
* doing. spans are NOT thread-safe, and must never be used by multiple threads at once.
|
||||
* With care, it is possible to safely pass a span object between threads, but in most
|
||||
* cases this is not necessary.
|
||||
* <p>
|
||||
*
|
||||
* Most crucial methods in terms of span lifecycle are:
|
||||
* <ul>
|
||||
* <li>The {@linkplain Tracer#createSpan(String) createSpan} method in this class
|
||||
* starts a new span.</li>
|
||||
* <li>The {@linkplain Tracer#createSpan(String, Span) createSpan} method creates a new span
|
||||
* which has this thread's currentSpan as one of its parents</li>
|
||||
* <li>The {@linkplain Tracer#continueSpan(Span) continueSpan} method creates a
|
||||
* new instance of span that logically is a continuation of the provided span.</li>
|
||||
* </ul>
|
||||
*
|
||||
* Closing a TraceScope does a few things:
|
||||
* <ul>
|
||||
* <li>It closes the span which the scope was managing.</li>
|
||||
* <li>Set currentSpan to the previous currentSpan (which may be null).</li>
|
||||
* </ul>
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface Tracer extends SpanAccessor {
|
||||
|
||||
/**
|
||||
* Creates a new Span.
|
||||
* <p/>
|
||||
* If this thread has a currently active span, it will be the parent of the span we
|
||||
* create here. If there is no currently active trace span, the trace scope we
|
||||
* create will be empty.
|
||||
*
|
||||
* @param name The name field for the new span to create.
|
||||
*/
|
||||
Span createSpan(String name);
|
||||
|
||||
/**
|
||||
* Creates a new Span with a specific parent. The parent might be in another
|
||||
* process or thread.
|
||||
* <p/>
|
||||
* If this thread has a currently active trace span, it must be the 'parent' span that
|
||||
* you pass in here as a parameter. The trace scope we create here will contain a new
|
||||
* span which is a child of 'parent'.
|
||||
*
|
||||
* @param name The name field for the new span to create.
|
||||
*/
|
||||
Span createSpan(String name, Span parent);
|
||||
|
||||
/**
|
||||
* Start a new span if the sampler allows it or if we are already tracing in this
|
||||
* thread. A sampler can be used to limit the number of traces created.
|
||||
*
|
||||
* @param name the name of the span
|
||||
* @param sampler a sampler to decide whether to create the span or not
|
||||
*/
|
||||
Span createSpan(String name, Sampler sampler);
|
||||
|
||||
/**
|
||||
* Contributes to a span started in another thread. The returned span shares
|
||||
* mutable state with the input.
|
||||
*/
|
||||
Span continueSpan(Span span);
|
||||
|
||||
/**
|
||||
* Adds a tag to the current span if tracing is currently on.
|
||||
* <p>
|
||||
* Every span may also have zero or more key/value Tags, which do not have
|
||||
* timestamps and simply annotate the spans.
|
||||
*
|
||||
* Check {@link TraceKeys} for examples of most common tag keys
|
||||
*/
|
||||
void addTag(String key, String value);
|
||||
|
||||
/**
|
||||
* Remove this span from the current thread, but don't stop it yet nor send it for
|
||||
* collection. This is useful if the span object is then passed to another thread for
|
||||
* use with {@link Tracer#continueSpan(Span)}.
|
||||
* <p>
|
||||
* Example of usage:
|
||||
* <pre>{@code
|
||||
* // Span "A" was present in thread "X". Let's assume that we're in thread "Y" to which span "A" got passed
|
||||
* Span continuedSpan = tracer.continueSpan(spanA);
|
||||
* // Now span "A" got continued in thread "Y".
|
||||
* ... // Some work is done... state of span "A" could get mutated
|
||||
* Span previouslyStoredSpan = tracer.detach(continuedSpan);
|
||||
* // Span "A" got removed from the thread Y but it wasn't yet sent for collection.
|
||||
* // Additional work can be done on span "A" in thread "X" and finally it can get closed and sent for collection
|
||||
* tracer.close(spanA);
|
||||
* }</pre>
|
||||
*
|
||||
* @return the saved trace if there was one before the trace started (null otherwise)
|
||||
*/
|
||||
Span detach(Span span);
|
||||
|
||||
/**
|
||||
* Remove this span from the current thread, stop it and send it for collection.
|
||||
*
|
||||
* @param span the span to close
|
||||
* @return the saved span if there was one before the trace started (null otherwise)
|
||||
*/
|
||||
Span close(Span span);
|
||||
|
||||
/**
|
||||
* Returns a wrapped {@link Callable} which will be recorded as a span
|
||||
* in the current trace.
|
||||
*/
|
||||
<V> Callable<V> wrap(Callable<V> callable);
|
||||
|
||||
/**
|
||||
* Returns a wrapped {@link Runnable} which will be recorded as a span
|
||||
* in the current trace.
|
||||
*/
|
||||
Runnable wrap(Runnable runnable);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -15,13 +15,11 @@
|
||||
*/
|
||||
package org.springframework.cloud.sleuth.annotation;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracing;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.util.SpanNameUtil;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -34,11 +32,11 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
class DefaultSpanCreator implements SpanCreator {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
private static final Log log = LogFactory.getLog(DefaultSpanCreator.class);
|
||||
|
||||
private final Tracer tracer;
|
||||
private final Tracing tracer;
|
||||
|
||||
DefaultSpanCreator(Tracer tracer) {
|
||||
DefaultSpanCreator(Tracing tracer) {
|
||||
this.tracer = tracer;
|
||||
}
|
||||
|
||||
@@ -50,14 +48,7 @@ class DefaultSpanCreator implements SpanCreator {
|
||||
log.debug("For the class [" + pjp.getThis().getClass() + "] method "
|
||||
+ "[" + pjp.getMethod().getName() + "] will name the span [" + changedName + "]");
|
||||
}
|
||||
return createSpan(changedName);
|
||||
}
|
||||
|
||||
private Span createSpan(String name) {
|
||||
if (this.tracer.isTracing()) {
|
||||
return this.tracer.createSpan(name, this.tracer.getCurrentSpan());
|
||||
}
|
||||
return this.tracer.createSpan(name);
|
||||
return this.tracer.tracer().nextSpan().name(changedName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.annotation;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -37,8 +39,6 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.cloud.sleuth.ErrorParser;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -154,7 +154,7 @@ class SleuthAdvisorConfig extends AbstractPointcutAdvisor implements BeanFactor
|
||||
return;
|
||||
}
|
||||
Annotation annotation = AnnotationUtils.findAnnotation(method,
|
||||
AnnotationMethodsResolver.this.annotationType);
|
||||
SleuthAdvisorConfig.AnnotationMethodsResolver.this.annotationType);
|
||||
if (annotation != null) { found.set(true); }
|
||||
}
|
||||
});
|
||||
@@ -168,15 +168,15 @@ class SleuthAdvisorConfig extends AbstractPointcutAdvisor implements BeanFactor
|
||||
* Interceptor that creates or continues a span depending on the provided
|
||||
* annotation. Also it adds logs and tags if necessary.
|
||||
*/
|
||||
class SleuthInterceptor implements IntroductionInterceptor, BeanFactoryAware {
|
||||
class SleuthInterceptor implements IntroductionInterceptor, BeanFactoryAware {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
private static final Log logger = LogFactory.getLog(SleuthInterceptor.class);
|
||||
private static final String CLASS_KEY = "class";
|
||||
private static final String METHOD_KEY = "method";
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
private SpanCreator spanCreator;
|
||||
private Tracer tracer;
|
||||
private Tracing tracing;
|
||||
private SpanTagAnnotationHandler spanTagAnnotationHandler;
|
||||
private ErrorParser errorParser;
|
||||
|
||||
@@ -193,13 +193,13 @@ class SleuthInterceptor implements IntroductionInterceptor, BeanFactoryAware {
|
||||
if (newSpan == null && continueSpan == null) {
|
||||
return invocation.proceed();
|
||||
}
|
||||
Span span = tracer().getCurrentSpan();
|
||||
Span span = tracing().tracer().currentSpan();
|
||||
if (newSpan != null || span == null) {
|
||||
span = spanCreator().createSpan(invocation, newSpan);
|
||||
}
|
||||
String log = log(continueSpan);
|
||||
boolean hasLog = StringUtils.hasText(log);
|
||||
try {
|
||||
if (newSpan != null) {
|
||||
span = spanCreator().createSpan(invocation, newSpan);
|
||||
}
|
||||
try (Tracer.SpanInScope ws = tracing().tracer().withSpanInScope(span)) {
|
||||
if (hasLog) {
|
||||
logEvent(span, log + ".before");
|
||||
}
|
||||
@@ -213,7 +213,7 @@ class SleuthInterceptor implements IntroductionInterceptor, BeanFactoryAware {
|
||||
if (hasLog) {
|
||||
logEvent(span, log + ".afterFailure");
|
||||
}
|
||||
errorParser().parseErrorTags(tracer().getCurrentSpan(), e);
|
||||
errorParser().parseErrorTags(span, e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (span != null) {
|
||||
@@ -221,15 +221,15 @@ class SleuthInterceptor implements IntroductionInterceptor, BeanFactoryAware {
|
||||
logEvent(span, log + ".after");
|
||||
}
|
||||
if (newSpan != null) {
|
||||
tracer().close(span);
|
||||
span.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addTags(MethodInvocation invocation, Span span) {
|
||||
tracer().addTag(CLASS_KEY, invocation.getThis().getClass().getSimpleName());
|
||||
tracer().addTag(METHOD_KEY, invocation.getMethod().getName());
|
||||
span.tag(CLASS_KEY, invocation.getThis().getClass().getSimpleName());
|
||||
span.tag(METHOD_KEY, invocation.getMethod().getName());
|
||||
}
|
||||
|
||||
private void logEvent(Span span, String name) {
|
||||
@@ -239,7 +239,7 @@ class SleuthInterceptor implements IntroductionInterceptor, BeanFactoryAware {
|
||||
+ "the same class then the aspect will not be properly resolved");
|
||||
return;
|
||||
}
|
||||
span.logEvent(name);
|
||||
span.annotate(name);
|
||||
}
|
||||
|
||||
private String log(ContinueSpan continueSpan) {
|
||||
@@ -249,11 +249,11 @@ class SleuthInterceptor implements IntroductionInterceptor, BeanFactoryAware {
|
||||
return "";
|
||||
}
|
||||
|
||||
private Tracer tracer() {
|
||||
if (this.tracer == null) {
|
||||
this.tracer = this.beanFactory.getBean(Tracer.class);
|
||||
private Tracing tracing() {
|
||||
if (this.tracing == null) {
|
||||
this.tracing = this.beanFactory.getBean(Tracing.class);
|
||||
}
|
||||
return this.tracer;
|
||||
return this.tracing;
|
||||
}
|
||||
|
||||
private SpanCreator spanCreator() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -15,12 +15,12 @@
|
||||
*/
|
||||
package org.springframework.cloud.sleuth.annotation;
|
||||
|
||||
import brave.Tracing;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -36,32 +36,28 @@ import org.springframework.context.annotation.Configuration;
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnBean(Tracer.class)
|
||||
@ConditionalOnBean(Tracing.class)
|
||||
@ConditionalOnProperty(name = "spring.sleuth.annotation.enabled", matchIfMissing = true)
|
||||
@AutoConfigureAfter(TraceAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(SleuthAnnotationProperties.class)
|
||||
public class SleuthAnnotationAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
SpanCreator spanCreator(Tracer tracer) {
|
||||
return new DefaultSpanCreator(tracer);
|
||||
@ConditionalOnMissingBean SpanCreator spanCreator(Tracing tracing) {
|
||||
return new DefaultSpanCreator(tracing);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
TagValueExpressionResolver spelTagValueExpressionResolver() {
|
||||
@ConditionalOnMissingBean TagValueExpressionResolver spelTagValueExpressionResolver() {
|
||||
return new SpelTagValueExpressionResolver();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
TagValueResolver noOpTagValueResolver() {
|
||||
@ConditionalOnMissingBean TagValueResolver noOpTagValueResolver() {
|
||||
return new NoOpTagValueResolver();
|
||||
}
|
||||
|
||||
@Bean
|
||||
SleuthAdvisorConfig sleuthAdvisorConfig() {
|
||||
@Bean SleuthAdvisorConfig sleuthAdvisorConfig() {
|
||||
return new SleuthAdvisorConfig();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.cloud.sleuth.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -35,7 +34,7 @@ import org.springframework.core.annotation.AnnotationUtils;
|
||||
*/
|
||||
class SleuthAnnotationUtils {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
private static final Log log = LogFactory.getLog(SleuthAnnotationUtils.class);
|
||||
|
||||
static boolean isMethodAnnotated(Method method) {
|
||||
return findAnnotation(method, NewSpan.class) != null ||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.annotation;
|
||||
|
||||
import brave.Span;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
|
||||
/**
|
||||
* A contract for creating a new span for a given join point
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -16,17 +16,17 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.annotation;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracing;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -43,10 +43,10 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
class SpanTagAnnotationHandler {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
private static final Log log = LogFactory.getLog(SpanTagAnnotationHandler.class);
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
private Tracer tracer;
|
||||
private Tracing tracing;
|
||||
|
||||
SpanTagAnnotationHandler(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
@@ -122,11 +122,20 @@ class SpanTagAnnotationHandler {
|
||||
for (SleuthAnnotatedParameter container : toBeAdded) {
|
||||
String tagValue = resolveTagValue(container.annotation, container.argument);
|
||||
String tagKey = resolveTagKey(container);
|
||||
tracer().addTag(tagKey, tagValue);
|
||||
span().tag(tagKey, tagValue);
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveTagKey(SleuthAnnotatedParameter container) {
|
||||
private Span span() {
|
||||
Span span = tracing().tracer().currentSpan();
|
||||
if (span != null) {
|
||||
return span;
|
||||
}
|
||||
return tracing().tracer().nextSpan();
|
||||
}
|
||||
|
||||
private String resolveTagKey(
|
||||
SleuthAnnotatedParameter container) {
|
||||
return StringUtils.hasText(container.annotation.value()) ?
|
||||
container.annotation.value() : container.annotation.key();
|
||||
}
|
||||
@@ -145,11 +154,11 @@ class SpanTagAnnotationHandler {
|
||||
return argument.toString();
|
||||
}
|
||||
|
||||
private Tracer tracer() {
|
||||
if (this.tracer == null) {
|
||||
this.tracer = this.beanFactory.getBean(Tracer.class);
|
||||
private Tracing tracing() {
|
||||
if (this.tracing == null) {
|
||||
this.tracing = this.beanFactory.getBean(Tracing.class);
|
||||
}
|
||||
return this.tracer;
|
||||
return this.tracing;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.annotation;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.expression.Expression;
|
||||
@@ -32,7 +30,7 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
* @since 1.2.0
|
||||
*/
|
||||
class SpelTagValueExpressionResolver implements TagValueExpressionResolver {
|
||||
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
private static final Log log = LogFactory.getLog(SpelTagValueExpressionResolver.class);
|
||||
|
||||
@Override
|
||||
public String resolve(String expression, Object parameter) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.autoconfig;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
@@ -27,10 +30,26 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
public class SleuthProperties {
|
||||
|
||||
private boolean enabled = true;
|
||||
/** When true, generate 128-bit trace IDs instead of 64-bit ones. */
|
||||
private boolean traceId128 = false;
|
||||
/** When true, your tracing system allows sharing a span ID between a client and server span */
|
||||
private boolean supportsJoin = true;
|
||||
|
||||
/**
|
||||
* List of baggage key names that should be propagated out of process.
|
||||
* These keys will be prefixed with `baggage` before the actual key.
|
||||
* This property is set in order to be backward compatible with previous
|
||||
* Sleuth versions.
|
||||
*
|
||||
* @see brave.propagation.ExtraFieldPropagation.FactoryBuilder#addPrefixedFields(String, java.util.Collection)
|
||||
*/
|
||||
private List<String> baggageKeys = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* List of fields that are referenced the same in-process as it is on the wire. For example, the
|
||||
* name "x-vcap-request-id" would be set as-is including the prefix.
|
||||
*
|
||||
* <p>Note: {@code fieldName} will be implicitly lower-cased.
|
||||
*
|
||||
* @see brave.propagation.ExtraFieldPropagation.FactoryBuilder#addField(String)
|
||||
*/
|
||||
private List<String> propagationKeys = new ArrayList<>();
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
@@ -40,19 +59,19 @@ public class SleuthProperties {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isTraceId128() {
|
||||
return this.traceId128;
|
||||
public List<String> getBaggageKeys() {
|
||||
return this.baggageKeys;
|
||||
}
|
||||
|
||||
public void setTraceId128(boolean traceId128) {
|
||||
this.traceId128 = traceId128;
|
||||
public void setBaggageKeys(List<String> baggageKeys) {
|
||||
this.baggageKeys = baggageKeys;
|
||||
}
|
||||
|
||||
public boolean isSupportsJoin() {
|
||||
return this.supportsJoin;
|
||||
public List<String> getPropagationKeys() {
|
||||
return this.propagationKeys;
|
||||
}
|
||||
|
||||
public void setSupportsJoin(boolean supportsJoin) {
|
||||
this.supportsJoin = supportsJoin;
|
||||
public void setPropagationKeys(List<String> propagationKeys) {
|
||||
this.propagationKeys = propagationKeys;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,32 @@
|
||||
/*
|
||||
* 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.autoconfig;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import brave.CurrentSpanCustomizer;
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import brave.context.log4j2.ThreadContextCurrentTraceContext;
|
||||
import brave.propagation.B3Propagation;
|
||||
import brave.propagation.CurrentTraceContext;
|
||||
import brave.propagation.ExtraFieldPropagation;
|
||||
import brave.propagation.Propagation;
|
||||
import brave.sampler.Sampler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.ErrorParser;
|
||||
import org.springframework.cloud.sleuth.ExceptionMessageErrorParser;
|
||||
import org.springframework.cloud.sleuth.NoOpSpanAdjuster;
|
||||
import org.springframework.cloud.sleuth.NoOpSpanReporter;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.SpanAdjuster;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.SpanReporter;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.log.SpanLogger;
|
||||
import org.springframework.cloud.sleuth.sampler.NeverSampler;
|
||||
import org.springframework.cloud.sleuth.trace.DefaultTracer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import zipkin2.Span;
|
||||
import zipkin2.reporter.Reporter;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
|
||||
@@ -45,58 +34,101 @@ import org.springframework.context.annotation.Configuration;
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value="spring.sleuth.enabled", matchIfMissing=true)
|
||||
@EnableConfigurationProperties({TraceKeys.class, SleuthProperties.class})
|
||||
@EnableConfigurationProperties({ TraceKeys.class, SleuthProperties.class })
|
||||
public class TraceAutoConfiguration {
|
||||
@Autowired
|
||||
SleuthProperties properties;
|
||||
|
||||
@Autowired(required = false) List<SpanAdjuster> spanAdjusters = new ArrayList<>();
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Random randomForSpanIds() {
|
||||
return new Random();
|
||||
Tracing sleuthTracing(@Value("${spring.zipkin.service.name:${spring.application.name:default}}") String serviceName,
|
||||
Propagation.Factory factory,
|
||||
CurrentTraceContext currentTraceContext,
|
||||
Reporter<zipkin2.Span> reporter,
|
||||
Sampler sampler) {
|
||||
return Tracing.newBuilder()
|
||||
.sampler(sampler)
|
||||
.localServiceName(serviceName)
|
||||
.propagationFactory(factory)
|
||||
.currentTraceContext(currentTraceContext)
|
||||
.spanReporter(adjustedReporter(reporter)).build();
|
||||
}
|
||||
|
||||
private Reporter<zipkin2.Span> adjustedReporter(Reporter<zipkin2.Span> delegate) {
|
||||
return span -> {
|
||||
Span spanToAdjust = span;
|
||||
for (SpanAdjuster spanAdjuster : this.spanAdjusters) {
|
||||
spanToAdjust = spanAdjuster.adjust(spanToAdjust);
|
||||
}
|
||||
delegate.report(spanToAdjust);
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Sampler defaultTraceSampler() {
|
||||
return NeverSampler.INSTANCE;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(Tracer.class)
|
||||
public Tracer sleuthTracer(Sampler sampler, Random random,
|
||||
SpanNamer spanNamer, SpanLogger spanLogger,
|
||||
SpanReporter spanReporter, TraceKeys traceKeys) {
|
||||
return new DefaultTracer(sampler, random, spanNamer, spanLogger,
|
||||
spanReporter, this.properties.isTraceId128(), traceKeys);
|
||||
Tracer sleuthTracer(Tracing tracing) {
|
||||
return tracing.tracer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SpanNamer spanNamer() {
|
||||
Sampler sleuthTraceSampler() {
|
||||
return Sampler.NEVER_SAMPLE;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean SpanNamer sleuthSpanNamer() {
|
||||
return new DefaultSpanNamer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SpanReporter defaultSpanReporter() {
|
||||
return new NoOpSpanReporter();
|
||||
Propagation.Factory sleuthPropagation(SleuthProperties sleuthProperties) {
|
||||
if (sleuthProperties.getBaggageKeys().isEmpty() && sleuthProperties.getPropagationKeys().isEmpty()) {
|
||||
return B3Propagation.FACTORY;
|
||||
}
|
||||
ExtraFieldPropagation.FactoryBuilder factoryBuilder = ExtraFieldPropagation
|
||||
.newFactoryBuilder(B3Propagation.FACTORY);
|
||||
if (!sleuthProperties.getBaggageKeys().isEmpty()) {
|
||||
factoryBuilder = factoryBuilder
|
||||
// for HTTP
|
||||
.addPrefixedFields("baggage-", sleuthProperties.getBaggageKeys())
|
||||
// for messaging
|
||||
.addPrefixedFields("baggage_", sleuthProperties.getBaggageKeys());
|
||||
}
|
||||
if (!sleuthProperties.getPropagationKeys().isEmpty()) {
|
||||
for (String key : sleuthProperties.getPropagationKeys()) {
|
||||
factoryBuilder = factoryBuilder.addField(key);
|
||||
}
|
||||
}
|
||||
return factoryBuilder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SpanAdjuster defaultSpanAdjuster() {
|
||||
return new NoOpSpanAdjuster();
|
||||
CurrentTraceContext sleuthCurrentTraceContext() {
|
||||
return ThreadContextCurrentTraceContext.create();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ErrorParser defaultErrorParser() {
|
||||
Reporter<zipkin2.Span> noOpSpanReporter() {
|
||||
return Reporter.NOOP;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
ErrorParser sleuthErrorParser() {
|
||||
return new ExceptionMessageErrorParser();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
CurrentSpanCustomizer sleuthCurrentSpanCustomizer(Tracing tracing) {
|
||||
return CurrentSpanCustomizer.create(tracing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ import org.springframework.core.env.PropertySource;
|
||||
* </ul>
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.0.0
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class TraceEnvironmentPostProcessor implements EnvironmentPostProcessor {
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public class AsyncCustomAutoConfiguration implements BeanPostProcessor {
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (bean instanceof AsyncConfigurer) {
|
||||
if (bean instanceof AsyncConfigurer && !(bean instanceof LazyTraceAsyncCustomizer)) {
|
||||
AsyncConfigurer configurer = (AsyncConfigurer) bean;
|
||||
return new LazyTraceAsyncCustomizer(this.beanFactory, configurer);
|
||||
}
|
||||
|
||||
@@ -18,15 +18,15 @@ package org.springframework.cloud.sleuth.instrument.async;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
@@ -46,8 +46,8 @@ import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value = "spring.sleuth.async.enabled", matchIfMissing = true)
|
||||
@ConditionalOnBean(Tracer.class)
|
||||
@AutoConfigureAfter(AsyncCustomAutoConfiguration.class)
|
||||
@ConditionalOnBean(Tracing.class)
|
||||
//@AutoConfigureAfter(AsyncCustomAutoConfiguration.class)
|
||||
public class AsyncDefaultAutoConfiguration {
|
||||
|
||||
@Autowired private BeanFactory beanFactory;
|
||||
@@ -66,8 +66,8 @@ public class AsyncDefaultAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TraceAsyncAspect traceAsyncAspect(Tracer tracer, TraceKeys traceKeys, SpanNamer spanNamer) {
|
||||
return new TraceAsyncAspect(tracer, traceKeys, spanNamer);
|
||||
public TraceAsyncAspect traceAsyncAspect(Tracer tracer, SpanNamer spanNamer, TraceKeys traceKeys) {
|
||||
return new TraceAsyncAspect(tracer, spanNamer, traceKeys);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -42,7 +42,8 @@ import org.springframework.util.ReflectionUtils;
|
||||
*/
|
||||
class ExecutorBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ExecutorBeanPostProcessor.class);
|
||||
private static final Log log = LogFactory.getLog(
|
||||
ExecutorBeanPostProcessor.class);
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -42,6 +42,9 @@ public class LazyTraceAsyncCustomizer extends AsyncConfigurerSupport {
|
||||
|
||||
@Override
|
||||
public Executor getAsyncExecutor() {
|
||||
if (this.delegate.getAsyncExecutor() instanceof LazyTraceExecutor) {
|
||||
return this.delegate.getAsyncExecutor();
|
||||
}
|
||||
return new LazyTraceExecutor(this.beanFactory, this.delegate.getAsyncExecutor());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -16,35 +16,33 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.async;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import brave.Tracer;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.ErrorParser;
|
||||
import org.springframework.cloud.sleuth.ExceptionMessageErrorParser;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
/**
|
||||
* {@link Executor} that wraps {@link Runnable} in a
|
||||
* {@link org.springframework.cloud.sleuth.TraceRunnable TraceRunnable} that sets a
|
||||
* local component tag on the span.
|
||||
* {@link Executor} that wraps {@link Runnable} in a trace representation
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LazyTraceExecutor implements Executor {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
private static final Log log = LogFactory.getLog(LazyTraceExecutor.class);
|
||||
|
||||
private Tracer tracer;
|
||||
private final BeanFactory beanFactory;
|
||||
private final Executor delegate;
|
||||
private TraceKeys traceKeys;
|
||||
private SpanNamer spanNamer;
|
||||
private ErrorParser errorParser;
|
||||
|
||||
public LazyTraceExecutor(BeanFactory beanFactory, Executor delegate) {
|
||||
this.beanFactory = beanFactory;
|
||||
@@ -62,21 +60,7 @@ public class LazyTraceExecutor implements Executor {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.delegate.execute(new SpanContinuingTraceRunnable(this.tracer, traceKeys(), spanNamer(), command));
|
||||
}
|
||||
|
||||
// due to some race conditions trace keys might not be ready yet
|
||||
private TraceKeys traceKeys() {
|
||||
if (this.traceKeys == null) {
|
||||
try {
|
||||
this.traceKeys = this.beanFactory.getBean(TraceKeys.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
log.warn("TraceKeys bean not found - will provide a manually created instance");
|
||||
return new TraceKeys();
|
||||
}
|
||||
}
|
||||
return this.traceKeys;
|
||||
this.delegate.execute(new TraceRunnable(this.tracer, spanNamer(), errorParser(), command));
|
||||
}
|
||||
|
||||
// due to some race conditions trace keys might not be ready yet
|
||||
@@ -93,4 +77,18 @@ public class LazyTraceExecutor implements Executor {
|
||||
return this.spanNamer;
|
||||
}
|
||||
|
||||
// due to some race conditions trace keys might not be ready yet
|
||||
private ErrorParser errorParser() {
|
||||
if (this.errorParser == null) {
|
||||
try {
|
||||
this.errorParser = this.beanFactory.getBean(ErrorParser.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
log.warn("ErrorParser bean not found - will provide a manually created instance");
|
||||
return new ExceptionMessageErrorParser();
|
||||
}
|
||||
}
|
||||
return this.errorParser;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -16,27 +16,27 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.async;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
import brave.Tracer;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.ErrorParser;
|
||||
import org.springframework.cloud.sleuth.ExceptionMessageErrorParser;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.core.task.TaskDecorator;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
/**
|
||||
* {@link ThreadPoolTaskExecutor} that continues a span if one was passed or creates a new one
|
||||
* Trace representation of {@link ThreadPoolTaskExecutor}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.10
|
||||
@@ -44,13 +44,13 @@ import org.springframework.util.concurrent.ListenableFuture;
|
||||
@SuppressWarnings("serial")
|
||||
public class LazyTraceThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
private static final Log log = LogFactory.getLog(LazyTraceThreadPoolTaskExecutor.class);
|
||||
|
||||
private Tracer tracer;
|
||||
private final BeanFactory beanFactory;
|
||||
private final ThreadPoolTaskExecutor delegate;
|
||||
private TraceKeys traceKeys;
|
||||
private SpanNamer spanNamer;
|
||||
private ErrorParser errorParser;
|
||||
|
||||
public LazyTraceThreadPoolTaskExecutor(BeanFactory beanFactory,
|
||||
ThreadPoolTaskExecutor delegate) {
|
||||
@@ -60,32 +60,32 @@ public class LazyTraceThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
|
||||
|
||||
@Override
|
||||
public void execute(Runnable task) {
|
||||
this.delegate.execute(new SpanContinuingTraceRunnable(tracer(), traceKeys(), spanNamer(), task));
|
||||
this.delegate.execute(new TraceRunnable(tracer(), spanNamer(), errorParser(), task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable task, long startTimeout) {
|
||||
this.delegate.execute(new SpanContinuingTraceRunnable(tracer(), traceKeys(), spanNamer(), task), startTimeout);
|
||||
this.delegate.execute(new TraceRunnable(tracer(), spanNamer(), errorParser(), task), startTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<?> submit(Runnable task) {
|
||||
return this.delegate.submit(new SpanContinuingTraceRunnable(tracer(), traceKeys(), spanNamer(), task));
|
||||
return this.delegate.submit(new TraceRunnable(tracer(), spanNamer(), errorParser(), task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Future<T> submit(Callable<T> task) {
|
||||
return this.delegate.submit(new SpanContinuingTraceCallable<>(tracer(), traceKeys(), spanNamer(), task));
|
||||
return this.delegate.submit(new TraceCallable<>(tracer(), spanNamer(), errorParser(), task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<?> submitListenable(Runnable task) {
|
||||
return this.delegate.submitListenable(new SpanContinuingTraceRunnable(tracer(), traceKeys(), spanNamer(), task));
|
||||
return this.delegate.submitListenable(new TraceRunnable(tracer(), spanNamer(), errorParser(), task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
|
||||
return this.delegate.submitListenable(new SpanContinuingTraceCallable<>(tracer(), traceKeys(), spanNamer(), task));
|
||||
return this.delegate.submitListenable(new TraceCallable<>(tracer(), spanNamer(), errorParser(), task));
|
||||
}
|
||||
|
||||
@Override public boolean prefersShortLivedTasks() {
|
||||
@@ -236,19 +236,6 @@ public class LazyTraceThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
|
||||
return this.tracer;
|
||||
}
|
||||
|
||||
private TraceKeys traceKeys() {
|
||||
if (this.traceKeys == null) {
|
||||
try {
|
||||
this.traceKeys = this.beanFactory.getBean(TraceKeys.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
log.warn("TraceKeys bean not found - will provide a manually created instance");
|
||||
return new TraceKeys();
|
||||
}
|
||||
}
|
||||
return this.traceKeys;
|
||||
}
|
||||
|
||||
private SpanNamer spanNamer() {
|
||||
if (this.spanNamer == null) {
|
||||
try {
|
||||
@@ -261,4 +248,17 @@ public class LazyTraceThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
|
||||
}
|
||||
return this.spanNamer;
|
||||
}
|
||||
|
||||
private ErrorParser errorParser() {
|
||||
if (this.errorParser == null) {
|
||||
try {
|
||||
this.errorParser = this.beanFactory.getBean(ErrorParser.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
log.warn("ErrorParser bean not found - will provide a manually created instance");
|
||||
return new ExceptionMessageErrorParser();
|
||||
}
|
||||
}
|
||||
return this.errorParser;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +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.instrument.async;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.TraceCallable;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
|
||||
/**
|
||||
* Callable that starts a span that is a local component span.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LocalComponentTraceCallable<V> extends TraceCallable<V> {
|
||||
|
||||
protected static final String ASYNC_COMPONENT = "async";
|
||||
|
||||
private final TraceKeys traceKeys;
|
||||
|
||||
public LocalComponentTraceCallable(Tracer tracer, TraceKeys traceKeys,
|
||||
SpanNamer spanNamer, Callable<V> delegate) {
|
||||
super(tracer, spanNamer, delegate);
|
||||
this.traceKeys = traceKeys;
|
||||
}
|
||||
|
||||
public LocalComponentTraceCallable(Tracer tracer, TraceKeys traceKeys,
|
||||
SpanNamer spanNamer, String name, Callable<V> delegate) {
|
||||
super(tracer, spanNamer, delegate, name);
|
||||
this.traceKeys = traceKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V call() throws Exception {
|
||||
Span span = startSpan();
|
||||
try {
|
||||
return this.getDelegate().call();
|
||||
}
|
||||
finally {
|
||||
close(span);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Span startSpan() {
|
||||
Span span = getTracer().createSpan(getSpanName(), getParent());
|
||||
getTracer().addTag(Span.SPAN_LOCAL_COMPONENT_TAG_NAME, ASYNC_COMPONENT);
|
||||
getTracer().addTag(this.traceKeys.getAsync().getPrefix() +
|
||||
this.traceKeys.getAsync().getThreadNameKey(), Thread.currentThread().getName());
|
||||
return span;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +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.instrument.async;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.TraceRunnable;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
|
||||
/**
|
||||
* Runnable that starts a span that is a local component span.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LocalComponentTraceRunnable extends TraceRunnable {
|
||||
|
||||
protected static final String ASYNC_COMPONENT = "async";
|
||||
|
||||
private final TraceKeys traceKeys;
|
||||
|
||||
public LocalComponentTraceRunnable(Tracer tracer, TraceKeys traceKeys,
|
||||
SpanNamer spanNamer, Runnable delegate) {
|
||||
super(tracer, spanNamer, delegate);
|
||||
this.traceKeys = traceKeys;
|
||||
}
|
||||
|
||||
public LocalComponentTraceRunnable(Tracer tracer, TraceKeys traceKeys,
|
||||
SpanNamer spanNamer, Runnable delegate, String name) {
|
||||
super(tracer, spanNamer, delegate, name);
|
||||
this.traceKeys = traceKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Span span = startSpan();
|
||||
try {
|
||||
this.getDelegate().run();
|
||||
}
|
||||
finally {
|
||||
close(span);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Span startSpan() {
|
||||
Span span = getTracer().createSpan(getSpanName(), getParent());
|
||||
getTracer().addTag(Span.SPAN_LOCAL_COMPONENT_TAG_NAME, ASYNC_COMPONENT);
|
||||
getTracer().addTag(this.traceKeys.getAsync().getPrefix() +
|
||||
this.traceKeys.getAsync().getThreadNameKey(), Thread.currentThread().getName());
|
||||
return span;
|
||||
}
|
||||
}
|
||||
@@ -1,77 +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.instrument.async;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.TraceCallable;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
/**
|
||||
* Runnable that continues a span if there is one and creates new that is a
|
||||
* local component span if there was no tracing present.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.10
|
||||
*/
|
||||
public class SpanContinuingTraceCallable<V> extends TraceCallable<V> {
|
||||
|
||||
private final LocalComponentTraceCallable<V> traceCallable;
|
||||
|
||||
public SpanContinuingTraceCallable(Tracer tracer, TraceKeys traceKeys,
|
||||
SpanNamer spanNamer, Callable<V> delegate) {
|
||||
super(tracer, spanNamer, delegate);
|
||||
this.traceCallable = new LocalComponentTraceCallable<>(tracer, traceKeys, spanNamer, delegate);
|
||||
}
|
||||
|
||||
public SpanContinuingTraceCallable(Tracer tracer, TraceKeys traceKeys,
|
||||
SpanNamer spanNamer, String name, Callable<V> delegate) {
|
||||
super(tracer, spanNamer, delegate, name);
|
||||
this.traceCallable = new LocalComponentTraceCallable<>(tracer, traceKeys, spanNamer, name, delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V call() throws Exception {
|
||||
Span span = startSpan();
|
||||
try {
|
||||
return this.getDelegate().call();
|
||||
}
|
||||
finally {
|
||||
close(span);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Span startSpan() {
|
||||
Span span = this.getParent();
|
||||
if (span == null) {
|
||||
return this.traceCallable.startSpan();
|
||||
}
|
||||
return continueSpan(span);
|
||||
}
|
||||
|
||||
@Override protected void close(Span span) {
|
||||
if (this.getParent() == null) {
|
||||
super.close(span);
|
||||
} else {
|
||||
super.detachSpan(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +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.instrument.async;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.TraceRunnable;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
/**
|
||||
* Runnable that continues a span if there is one and creates new that is a
|
||||
* local component span if there was no tracing present.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.10
|
||||
*/
|
||||
public class SpanContinuingTraceRunnable extends TraceRunnable {
|
||||
|
||||
private final LocalComponentTraceRunnable traceRunnable;
|
||||
|
||||
public SpanContinuingTraceRunnable(Tracer tracer, TraceKeys traceKeys,
|
||||
SpanNamer spanNamer, Runnable delegate) {
|
||||
super(tracer, spanNamer, delegate);
|
||||
this.traceRunnable = new LocalComponentTraceRunnable(tracer, traceKeys, spanNamer, delegate);
|
||||
}
|
||||
|
||||
public SpanContinuingTraceRunnable(Tracer tracer, TraceKeys traceKeys,
|
||||
SpanNamer spanNamer, Runnable delegate, String name) {
|
||||
super(tracer, spanNamer, delegate, name);
|
||||
this.traceRunnable = new LocalComponentTraceRunnable(tracer, traceKeys, spanNamer, delegate, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Span span = startSpan();
|
||||
try {
|
||||
this.getDelegate().run();
|
||||
}
|
||||
finally {
|
||||
close(span);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Span startSpan() {
|
||||
Span span = this.getParent();
|
||||
if (span == null) {
|
||||
return this.traceRunnable.startSpan();
|
||||
}
|
||||
return continueSpan(span);
|
||||
}
|
||||
|
||||
@Override protected void close(Span span) {
|
||||
if (this.getParent() == null) {
|
||||
super.close(span);
|
||||
} else {
|
||||
super.detachSpan(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -18,16 +18,14 @@ package org.springframework.cloud.sleuth.instrument.async;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.InternalApi;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.util.SpanNameUtil;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
@@ -43,61 +41,32 @@ import org.springframework.util.ReflectionUtils;
|
||||
@Aspect
|
||||
public class TraceAsyncAspect {
|
||||
|
||||
private static final String ASYNC_COMPONENT = "async";
|
||||
|
||||
private final Tracer tracer;
|
||||
private final SpanNamer spanNamer;
|
||||
private final TraceKeys traceKeys;
|
||||
private final BeanFactory beanFactory;
|
||||
private SpanNamer spanNamer;
|
||||
|
||||
@Deprecated
|
||||
public TraceAsyncAspect(Tracer tracer, TraceKeys traceKeys, BeanFactory beanFactory) {
|
||||
public TraceAsyncAspect(Tracer tracer, SpanNamer spanNamer, TraceKeys traceKeys) {
|
||||
this.tracer = tracer;
|
||||
this.traceKeys = traceKeys;
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
public TraceAsyncAspect(Tracer tracer, TraceKeys traceKeys, SpanNamer spanNamer) {
|
||||
this.tracer = tracer;
|
||||
this.traceKeys = traceKeys;
|
||||
this.spanNamer = spanNamer;
|
||||
this.beanFactory = null;
|
||||
this.traceKeys = traceKeys;
|
||||
}
|
||||
|
||||
@Around("execution (@org.springframework.scheduling.annotation.Async * *.*(..))")
|
||||
public Object traceBackgroundThread(final ProceedingJoinPoint pjp) throws Throwable {
|
||||
String spanName = spanNamer().name(getMethod(pjp, pjp.getTarget()),
|
||||
String spanName = this.spanNamer.name(getMethod(pjp, pjp.getTarget()),
|
||||
SpanNameUtil.toLowerHyphen(pjp.getSignature().getName()));
|
||||
Span span = span(spanName);
|
||||
renameAsyncSpan(spanName, span);
|
||||
this.tracer.addTag(Span.SPAN_LOCAL_COMPONENT_TAG_NAME, ASYNC_COMPONENT);
|
||||
this.tracer.addTag(this.traceKeys.getAsync().getPrefix() +
|
||||
this.traceKeys.getAsync().getClassNameKey(), pjp.getTarget().getClass().getSimpleName());
|
||||
this.tracer.addTag(this.traceKeys.getAsync().getPrefix() +
|
||||
this.traceKeys.getAsync().getMethodNameKey(), pjp.getSignature().getName());
|
||||
try {
|
||||
Span span = this.tracer.currentSpan().name(spanName);
|
||||
try(Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
||||
span.tag(this.traceKeys.getAsync().getPrefix() +
|
||||
this.traceKeys.getAsync().getClassNameKey(), pjp.getTarget().getClass().getSimpleName());
|
||||
span.tag(this.traceKeys.getAsync().getPrefix() +
|
||||
this.traceKeys.getAsync().getMethodNameKey(), pjp.getSignature().getName());
|
||||
return pjp.proceed();
|
||||
} finally {
|
||||
this.tracer.close(span);
|
||||
span.finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void renameAsyncSpan(String spanName, Span span) {
|
||||
// if there's a tag "lc" -> "async", that means the span came from
|
||||
// a LazyTraceExecutor component that creates a span that contains very few
|
||||
// information. If that's the case we want to rename it to have a different name
|
||||
if (ASYNC_COMPONENT.equals(span.tags().get(Span.SPAN_LOCAL_COMPONENT_TAG_NAME))) {
|
||||
InternalApi.renameSpan(span, spanName);
|
||||
}
|
||||
}
|
||||
|
||||
private Span span(String spanName) {
|
||||
if (this.tracer.isTracing()) {
|
||||
return this.tracer.getCurrentSpan();
|
||||
}
|
||||
return this.tracer.createSpan(spanName);
|
||||
}
|
||||
|
||||
private Method getMethod(ProceedingJoinPoint pjp, Object object) {
|
||||
MethodSignature signature = (MethodSignature) pjp.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
@@ -105,11 +74,4 @@ public class TraceAsyncAspect {
|
||||
.findMethod(object.getClass(), method.getName(), method.getParameterTypes());
|
||||
}
|
||||
|
||||
SpanNamer spanNamer() {
|
||||
if (this.spanNamer == null && this.beanFactory != null) {
|
||||
this.spanNamer = this.beanFactory.getBean(SpanNamer.class);
|
||||
}
|
||||
return this.spanNamer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -14,12 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web.client;
|
||||
package org.springframework.cloud.sleuth.instrument.async;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import brave.Tracing;
|
||||
import org.springframework.core.task.AsyncListenableTaskExecutor;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
@@ -29,48 +29,48 @@ import org.springframework.util.concurrent.ListenableFuture;
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @see Tracer#wrap(Runnable)
|
||||
* @see Tracer#wrap(Callable)
|
||||
* @see brave.propagation.CurrentTraceContext#wrap(Runnable)
|
||||
* @see brave.propagation.CurrentTraceContext#wrap(Callable)
|
||||
*/
|
||||
public class TraceAsyncListenableTaskExecutor implements AsyncListenableTaskExecutor {
|
||||
|
||||
private final AsyncListenableTaskExecutor delegate;
|
||||
private final Tracer tracer;
|
||||
private final Tracing tracing;
|
||||
|
||||
TraceAsyncListenableTaskExecutor(AsyncListenableTaskExecutor delegate,
|
||||
Tracer tracer) {
|
||||
Tracing tracing) {
|
||||
this.delegate = delegate;
|
||||
this.tracer = tracer;
|
||||
this.tracing = tracing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<?> submitListenable(Runnable task) {
|
||||
return this.delegate.submitListenable(this.tracer.wrap(task));
|
||||
return this.delegate.submitListenable(this.tracing.currentTraceContext().wrap(task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
|
||||
return this.delegate.submitListenable(this.tracer.wrap(task));
|
||||
return this.delegate.submitListenable(this.tracing.currentTraceContext().wrap(task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable task, long startTimeout) {
|
||||
this.delegate.execute(this.tracer.wrap(task), startTimeout);
|
||||
this.delegate.execute(this.tracing.currentTraceContext().wrap(task), startTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<?> submit(Runnable task) {
|
||||
return this.delegate.submit(this.tracer.wrap(task));
|
||||
return this.delegate.submit(this.tracing.currentTraceContext().wrap(task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Future<T> submit(Callable<T> task) {
|
||||
return this.delegate.submit(this.tracer.wrap(task));
|
||||
return this.delegate.submit(this.tracing.currentTraceContext().wrap(task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable task) {
|
||||
this.delegate.execute(this.tracer.wrap(task));
|
||||
this.delegate.execute(this.tracing.currentTraceContext().wrap(task));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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.instrument.async;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import org.springframework.cloud.sleuth.ErrorParser;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
|
||||
/**
|
||||
* Callable that passes Span between threads. The Span name is
|
||||
* taken either from the passed value or from the {@link SpanNamer}
|
||||
* interface.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class TraceCallable<V> implements Callable<V> {
|
||||
|
||||
/**
|
||||
* Since we don't know the exact operation name we provide a default
|
||||
* name for the Span
|
||||
*/
|
||||
private static final String DEFAULT_SPAN_NAME = "async";
|
||||
|
||||
private final Tracer tracer;
|
||||
private final Callable<V> delegate;
|
||||
private final Span span;
|
||||
private final ErrorParser errorParser;
|
||||
|
||||
public TraceCallable(Tracer tracer, SpanNamer spanNamer, ErrorParser errorParser, Callable<V> delegate) {
|
||||
this(tracer, spanNamer, errorParser, delegate, null);
|
||||
}
|
||||
|
||||
public TraceCallable(Tracer tracer, SpanNamer spanNamer, ErrorParser errorParser, Callable<V> delegate, String name) {
|
||||
this.tracer = tracer;
|
||||
this.delegate = delegate;
|
||||
String spanName = name != null ? name : spanNamer.name(delegate, DEFAULT_SPAN_NAME);
|
||||
this.span = this.tracer.nextSpan().name(spanName);
|
||||
this.errorParser = errorParser;
|
||||
}
|
||||
|
||||
@Override public V call() throws Exception {
|
||||
Throwable error = null;
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(this.span.start())) {
|
||||
return this.delegate.call();
|
||||
} catch (Exception | Error e) {
|
||||
error = e;
|
||||
throw e;
|
||||
} finally {
|
||||
this.errorParser.parseErrorTags(this.span, error);
|
||||
this.span.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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.instrument.async;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.Tracer.SpanInScope;
|
||||
import org.springframework.cloud.sleuth.ErrorParser;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
|
||||
/**
|
||||
* Runnable that passes Span between threads. The Span name is
|
||||
* taken either from the passed value or from the {@link SpanNamer}
|
||||
* interface.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class TraceRunnable implements Runnable {
|
||||
|
||||
/**
|
||||
* Since we don't know the exact operation name we provide a default
|
||||
* name for the Span
|
||||
*/
|
||||
private static final String DEFAULT_SPAN_NAME = "async";
|
||||
|
||||
private final Tracer tracer;
|
||||
private final Runnable delegate;
|
||||
private final Span span;
|
||||
private final ErrorParser errorParser;
|
||||
|
||||
public TraceRunnable(Tracer tracer, SpanNamer spanNamer, ErrorParser errorParser, Runnable delegate) {
|
||||
this(tracer, spanNamer, errorParser, delegate, null);
|
||||
}
|
||||
|
||||
public TraceRunnable(Tracer tracer, SpanNamer spanNamer, ErrorParser errorParser, Runnable delegate, String name) {
|
||||
this.tracer = tracer;
|
||||
this.delegate = delegate;
|
||||
String spanName = name != null ? name : spanNamer.name(delegate, DEFAULT_SPAN_NAME);
|
||||
this.span = this.tracer.nextSpan().name(spanName);
|
||||
this.errorParser = errorParser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Throwable error = null;
|
||||
try (SpanInScope ws = this.tracer.withSpanInScope(this.span.start())) {
|
||||
this.delegate.run();
|
||||
} catch (RuntimeException | Error e) {
|
||||
error = e;
|
||||
throw e;
|
||||
} finally {
|
||||
this.errorParser.parseErrorTags(this.span, error);
|
||||
this.span.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,10 +25,10 @@ import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import brave.Tracer;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.ErrorParser;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
/**
|
||||
* A decorator class for {@link ExecutorService} to support tracing in Executors
|
||||
@@ -40,34 +40,23 @@ public class TraceableExecutorService implements ExecutorService {
|
||||
final ExecutorService delegate;
|
||||
Tracer tracer;
|
||||
private final String spanName;
|
||||
TraceKeys traceKeys;
|
||||
SpanNamer spanNamer;
|
||||
BeanFactory beanFactory;
|
||||
|
||||
public TraceableExecutorService(final ExecutorService delegate, final Tracer tracer,
|
||||
TraceKeys traceKeys, SpanNamer spanNamer) {
|
||||
this(delegate, tracer, traceKeys, spanNamer, null);
|
||||
}
|
||||
ErrorParser errorParser;
|
||||
|
||||
public TraceableExecutorService(BeanFactory beanFactory, final ExecutorService delegate) {
|
||||
this.delegate = delegate;
|
||||
this.beanFactory = beanFactory;
|
||||
this.spanName = null;
|
||||
this(beanFactory, delegate, null);
|
||||
}
|
||||
|
||||
public TraceableExecutorService(final ExecutorService delegate, final Tracer tracer,
|
||||
TraceKeys traceKeys, SpanNamer spanNamer, String spanName) {
|
||||
public TraceableExecutorService(BeanFactory beanFactory, final ExecutorService delegate, String spanName) {
|
||||
this.delegate = delegate;
|
||||
this.tracer = tracer;
|
||||
this.beanFactory = beanFactory;
|
||||
this.spanName = spanName;
|
||||
this.traceKeys = traceKeys;
|
||||
this.spanNamer = spanNamer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
final Runnable r = new LocalComponentTraceRunnable(tracer(), traceKeys(),
|
||||
spanNamer(), command, this.spanName);
|
||||
final Runnable r = new TraceRunnable(tracer(), spanNamer(), errorParser(), command, this.spanName);
|
||||
this.delegate.execute(r);
|
||||
}
|
||||
|
||||
@@ -98,22 +87,19 @@ public class TraceableExecutorService implements ExecutorService {
|
||||
|
||||
@Override
|
||||
public <T> Future<T> submit(Callable<T> task) {
|
||||
Callable<T> c = new SpanContinuingTraceCallable<>(tracer(), traceKeys(),
|
||||
spanNamer(), this.spanName, task);
|
||||
Callable<T> c = new TraceCallable<>(tracer(), spanNamer(), errorParser(), task, this.spanName);
|
||||
return this.delegate.submit(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Future<T> submit(Runnable task, T result) {
|
||||
Runnable r = new SpanContinuingTraceRunnable(tracer(), traceKeys(),
|
||||
spanNamer(), task, this.spanName);
|
||||
Runnable r = new TraceRunnable(tracer(), spanNamer(), errorParser(), task, this.spanName);
|
||||
return this.delegate.submit(r, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<?> submit(Runnable task) {
|
||||
Runnable r = new LocalComponentTraceRunnable(tracer(), traceKeys(),
|
||||
spanNamer(), task, this.spanName);
|
||||
Runnable r = new TraceRunnable(tracer(), spanNamer(), errorParser(), task, this.spanName);
|
||||
return this.delegate.submit(r);
|
||||
}
|
||||
|
||||
@@ -142,9 +128,8 @@ public class TraceableExecutorService implements ExecutorService {
|
||||
private <T> Collection<? extends Callable<T>> wrapCallableCollection(Collection<? extends Callable<T>> tasks) {
|
||||
List<Callable<T>> ts = new ArrayList<>();
|
||||
for (Callable<T> task : tasks) {
|
||||
if (!(task instanceof SpanContinuingTraceCallable)) {
|
||||
ts.add(new SpanContinuingTraceCallable<>(tracer(), traceKeys(),
|
||||
spanNamer(), this.spanName, task));
|
||||
if (!(task instanceof TraceCallable)) {
|
||||
ts.add(new TraceCallable<>(tracer(), spanNamer(), errorParser(), task, this.spanName));
|
||||
}
|
||||
}
|
||||
return ts;
|
||||
@@ -157,18 +142,17 @@ public class TraceableExecutorService implements ExecutorService {
|
||||
return this.tracer;
|
||||
}
|
||||
|
||||
TraceKeys traceKeys() {
|
||||
if (this.traceKeys == null && this.beanFactory != null) {
|
||||
this.traceKeys = this.beanFactory.getBean(TraceKeys.class);
|
||||
}
|
||||
return this.traceKeys;
|
||||
}
|
||||
|
||||
SpanNamer spanNamer() {
|
||||
if (this.spanNamer == null && this.beanFactory != null) {
|
||||
this.spanNamer = this.beanFactory.getBean(SpanNamer.class);
|
||||
}
|
||||
return this.spanNamer;
|
||||
}
|
||||
|
||||
|
||||
ErrorParser errorParser() {
|
||||
if (this.errorParser == null) {
|
||||
this.errorParser = this.beanFactory.getBean(ErrorParser.class);
|
||||
}
|
||||
return this.errorParser;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,12 @@
|
||||
package org.springframework.cloud.sleuth.instrument.async;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
|
||||
/**
|
||||
* A decorator class for {@link ScheduledExecutorService} to support tracing in Executors
|
||||
@@ -33,9 +32,8 @@ import org.springframework.cloud.sleuth.TraceKeys;
|
||||
*/
|
||||
public class TraceableScheduledExecutorService extends TraceableExecutorService implements ScheduledExecutorService {
|
||||
|
||||
public TraceableScheduledExecutorService(ScheduledExecutorService delegate,
|
||||
Tracer tracer, TraceKeys traceKeys, SpanNamer spanNamer) {
|
||||
super(delegate, tracer, traceKeys, spanNamer);
|
||||
public TraceableScheduledExecutorService(BeanFactory beanFactory, final ExecutorService delegate) {
|
||||
super(beanFactory, delegate);
|
||||
}
|
||||
|
||||
private ScheduledExecutorService getScheduledExecutorService() {
|
||||
@@ -44,25 +42,25 @@ public class TraceableScheduledExecutorService extends TraceableExecutorService
|
||||
|
||||
@Override
|
||||
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
|
||||
Runnable r = new SpanContinuingTraceRunnable(this.tracer, this.traceKeys, this.spanNamer, command);
|
||||
Runnable r = new TraceRunnable(tracer(), spanNamer(), errorParser(), command);
|
||||
return getScheduledExecutorService().schedule(r, delay, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
|
||||
Callable<V> c = new SpanContinuingTraceCallable<>(this.tracer, this.traceKeys, this.spanNamer, callable);
|
||||
Callable<V> c = new TraceCallable<>(tracer(), spanNamer(), errorParser(), callable);
|
||||
return getScheduledExecutorService().schedule(c, delay, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
|
||||
Runnable r = new SpanContinuingTraceRunnable(this.tracer, this.traceKeys, this.spanNamer, command);
|
||||
Runnable r = new TraceRunnable(tracer(), spanNamer(), errorParser(), command);
|
||||
return getScheduledExecutorService().scheduleAtFixedRate(r, initialDelay, period, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
|
||||
Runnable r = new SpanContinuingTraceRunnable(this.tracer, this.traceKeys, this.spanNamer, command);
|
||||
Runnable r = new TraceRunnable(tracer(), spanNamer(), errorParser(), command);
|
||||
return getScheduledExecutorService().scheduleWithFixedDelay(r, initialDelay, delay, unit);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package org.springframework.cloud.sleuth.instrument.hystrix;
|
||||
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.ErrorParser;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -24,13 +26,14 @@ import com.netflix.hystrix.HystrixCommand;
|
||||
@Configuration
|
||||
@AutoConfigureAfter(TraceAutoConfiguration.class)
|
||||
@ConditionalOnClass(HystrixCommand.class)
|
||||
@ConditionalOnBean(Tracer.class)
|
||||
@ConditionalOnBean(Tracing.class)
|
||||
@ConditionalOnProperty(value = "spring.sleuth.hystrix.strategy.enabled", matchIfMissing = true)
|
||||
public class SleuthHystrixAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
SleuthHystrixConcurrencyStrategy sleuthHystrixConcurrencyStrategy(Tracer tracer, TraceKeys traceKeys) {
|
||||
return new SleuthHystrixConcurrencyStrategy(tracer, traceKeys);
|
||||
@Bean SleuthHystrixConcurrencyStrategy sleuthHystrixConcurrencyStrategy(Tracer tracer,
|
||||
SpanNamer spanNamer, ErrorParser errorParser) {
|
||||
return new SleuthHystrixConcurrencyStrategy(tracer, spanNamer,
|
||||
errorParser);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -16,12 +16,12 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.hystrix;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import brave.Tracer;
|
||||
import com.netflix.hystrix.HystrixThreadPoolKey;
|
||||
import com.netflix.hystrix.HystrixThreadPoolProperties;
|
||||
import com.netflix.hystrix.strategy.HystrixPlugins;
|
||||
@@ -35,9 +35,9 @@ import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
|
||||
import com.netflix.hystrix.strategy.properties.HystrixProperty;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.ErrorParser;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.instrument.async.TraceCallable;
|
||||
|
||||
/**
|
||||
* A {@link HystrixConcurrencyStrategy} that wraps a {@link Callable} in a
|
||||
@@ -54,12 +54,15 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy
|
||||
.getLog(SleuthHystrixConcurrencyStrategy.class);
|
||||
|
||||
private final Tracer tracer;
|
||||
private final TraceKeys traceKeys;
|
||||
private final SpanNamer spanNamer;
|
||||
private final ErrorParser errorParser;
|
||||
private HystrixConcurrencyStrategy delegate;
|
||||
|
||||
public SleuthHystrixConcurrencyStrategy(Tracer tracer, TraceKeys traceKeys) {
|
||||
public SleuthHystrixConcurrencyStrategy(Tracer tracer,
|
||||
SpanNamer spanNamer, ErrorParser errorParser) {
|
||||
this.tracer = tracer;
|
||||
this.traceKeys = traceKeys;
|
||||
this.spanNamer = spanNamer;
|
||||
this.errorParser = errorParser;
|
||||
try {
|
||||
this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
|
||||
if (this.delegate instanceof SleuthHystrixConcurrencyStrategy) {
|
||||
@@ -103,15 +106,16 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy
|
||||
|
||||
@Override
|
||||
public <T> Callable<T> wrapCallable(Callable<T> callable) {
|
||||
if (callable instanceof HystrixTraceCallable) {
|
||||
if (callable instanceof TraceCallable) {
|
||||
return callable;
|
||||
}
|
||||
Callable<T> wrappedCallable = this.delegate != null
|
||||
? this.delegate.wrapCallable(callable) : callable;
|
||||
if (wrappedCallable instanceof HystrixTraceCallable) {
|
||||
if (wrappedCallable instanceof TraceCallable) {
|
||||
return wrappedCallable;
|
||||
}
|
||||
return new HystrixTraceCallable<>(this.tracer, this.traceKeys, wrappedCallable);
|
||||
return new TraceCallable<>(this.tracer, this.spanNamer,
|
||||
this.errorParser, wrappedCallable, HYSTRIX_COMPONENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -140,68 +144,4 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy
|
||||
HystrixRequestVariableLifecycle<T> rv) {
|
||||
return this.delegate.getRequestVariable(rv);
|
||||
}
|
||||
|
||||
// Visible for testing
|
||||
static class HystrixTraceCallable<S> implements Callable<S> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
|
||||
private final Tracer tracer;
|
||||
private final TraceKeys traceKeys;
|
||||
private final Callable<S> callable;
|
||||
private final Span parent;
|
||||
|
||||
public HystrixTraceCallable(Tracer tracer, TraceKeys traceKeys,
|
||||
Callable<S> callable) {
|
||||
this.tracer = tracer;
|
||||
this.traceKeys = traceKeys;
|
||||
this.callable = callable;
|
||||
this.parent = tracer.getCurrentSpan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public S call() throws Exception {
|
||||
Span span = this.parent;
|
||||
boolean created = false;
|
||||
if (span != null) {
|
||||
span = this.tracer.continueSpan(span);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Continuing span " + span);
|
||||
}
|
||||
}
|
||||
else {
|
||||
span = this.tracer.createSpan(HYSTRIX_COMPONENT);
|
||||
created = true;
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Creating new span " + span);
|
||||
}
|
||||
}
|
||||
if (!span.tags().containsKey(Span.SPAN_LOCAL_COMPONENT_TAG_NAME)) {
|
||||
this.tracer.addTag(Span.SPAN_LOCAL_COMPONENT_TAG_NAME, HYSTRIX_COMPONENT);
|
||||
}
|
||||
String asyncKey = this.traceKeys.getAsync().getPrefix()
|
||||
+ this.traceKeys.getAsync().getThreadNameKey();
|
||||
if (!span.tags().containsKey(asyncKey)) {
|
||||
this.tracer.addTag(asyncKey, Thread.currentThread().getName());
|
||||
}
|
||||
try {
|
||||
return this.callable.call();
|
||||
}
|
||||
finally {
|
||||
if (created) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Closing span since it was created" + span);
|
||||
}
|
||||
this.tracer.close(span);
|
||||
}
|
||||
else if(this.tracer.isTracing()) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Detaching span since it was continued " + span);
|
||||
}
|
||||
this.tracer.detach(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -16,11 +16,10 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.hystrix;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import com.netflix.hystrix.HystrixCommand;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
|
||||
/**
|
||||
* Abstraction over {@code HystrixCommand} that wraps command execution with Trace setting
|
||||
@@ -29,57 +28,38 @@ import com.netflix.hystrix.HystrixCommand;
|
||||
* @see Tracer
|
||||
*
|
||||
* @author Tomasz Nurkiewicz, 4financeIT
|
||||
* @author Marcin Grzejszczak, 4financeIT
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Spencer Gibb
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public abstract class TraceCommand<R> extends HystrixCommand<R> {
|
||||
|
||||
private static final String HYSTRIX_COMPONENT = "hystrix";
|
||||
|
||||
private final Tracer tracer;
|
||||
private final TraceKeys traceKeys;
|
||||
private final Span parentSpan;
|
||||
private final Span span;
|
||||
|
||||
protected TraceCommand(Tracer tracer, TraceKeys traceKeys, Setter setter) {
|
||||
super(setter);
|
||||
this.tracer = tracer;
|
||||
this.traceKeys = traceKeys;
|
||||
this.parentSpan = tracer.getCurrentSpan();
|
||||
this.span = this.tracer.nextSpan();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected R run() throws Exception {
|
||||
String commandKeyName = getCommandKey().name();
|
||||
Span span = startSpan(commandKeyName);
|
||||
this.tracer.addTag(Span.SPAN_LOCAL_COMPONENT_TAG_NAME, HYSTRIX_COMPONENT);
|
||||
this.tracer.addTag(this.traceKeys.getHystrix().getPrefix() +
|
||||
Span span = this.span.name(commandKeyName);
|
||||
span.tag(this.traceKeys.getHystrix().getPrefix() +
|
||||
this.traceKeys.getHystrix().getCommandKey(), commandKeyName);
|
||||
this.tracer.addTag(this.traceKeys.getHystrix().getPrefix() +
|
||||
span.tag(this.traceKeys.getHystrix().getPrefix() +
|
||||
this.traceKeys.getHystrix().getCommandGroup(), getCommandGroup().name());
|
||||
this.tracer.addTag(this.traceKeys.getHystrix().getPrefix() +
|
||||
span.tag(this.traceKeys.getHystrix().getPrefix() +
|
||||
this.traceKeys.getHystrix().getThreadPoolKey(), getThreadPoolKey().name());
|
||||
try {
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
||||
return doRun();
|
||||
}
|
||||
finally {
|
||||
close(span);
|
||||
}
|
||||
}
|
||||
|
||||
private Span startSpan(String commandKeyName) {
|
||||
Span span = this.parentSpan;
|
||||
if (span == null) {
|
||||
return this.tracer.createSpan(commandKeyName, this.parentSpan);
|
||||
}
|
||||
return this.tracer.continueSpan(span);
|
||||
}
|
||||
|
||||
private void close(Span span) {
|
||||
if (this.parentSpan == null) {
|
||||
this.tracer.close(span);
|
||||
} else {
|
||||
this.tracer.detach(span);
|
||||
span.finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.ErrorParser;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.util.SpanNameUtil;
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.context.IntegrationObjectSupport;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.support.ChannelInterceptorAdapter;
|
||||
import org.springframework.messaging.support.ExecutorChannelInterceptor;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
/**
|
||||
* Abstraction over classes related to channel intercepting
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter
|
||||
implements ExecutorChannelInterceptor {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
|
||||
/**
|
||||
* If a span comes from messaging components then it will have this value as a prefix
|
||||
* to its name.
|
||||
* <p>
|
||||
* Example of a Span name: {@code message:foo}
|
||||
* <p>
|
||||
* Where {@code message} is the prefix and {@code foo} is the channel name
|
||||
*/
|
||||
protected static final String MESSAGE_COMPONENT = "message";
|
||||
|
||||
private Tracer tracer;
|
||||
private TraceKeys traceKeys;
|
||||
private MessagingSpanTextMapExtractor spanExtractor;
|
||||
private MessagingSpanTextMapInjector spanInjector;
|
||||
private ErrorParser errorParser;
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
protected AbstractTraceChannelInterceptor(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
protected Tracer getTracer() {
|
||||
if (this.tracer == null) {
|
||||
this.tracer = this.beanFactory.getBean(Tracer.class);
|
||||
}
|
||||
return this.tracer;
|
||||
}
|
||||
|
||||
protected TraceKeys getTraceKeys() {
|
||||
if (this.traceKeys == null) {
|
||||
this.traceKeys = this.beanFactory.getBean(TraceKeys.class);
|
||||
}
|
||||
return this.traceKeys;
|
||||
}
|
||||
|
||||
protected MessagingSpanTextMapExtractor getSpanExtractor() {
|
||||
if (this.spanExtractor == null) {
|
||||
this.spanExtractor = this.beanFactory.getBean(MessagingSpanTextMapExtractor.class);
|
||||
}
|
||||
return this.spanExtractor;
|
||||
}
|
||||
|
||||
protected MessagingSpanTextMapInjector getSpanInjector() {
|
||||
if (this.spanInjector == null) {
|
||||
this.spanInjector = this.beanFactory.getBean(MessagingSpanTextMapInjector.class);
|
||||
}
|
||||
return this.spanInjector;
|
||||
}
|
||||
|
||||
protected ErrorParser getErrorParser() {
|
||||
if (this.errorParser == null) {
|
||||
this.errorParser = this.beanFactory.getBean(ErrorParser.class);
|
||||
}
|
||||
return this.errorParser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a span given the message and a channel. Returns {@code null} if ids are
|
||||
* missing.
|
||||
*/
|
||||
protected Span buildSpan(SpanTextMap carrier) {
|
||||
try {
|
||||
return getSpanExtractor().joinTrace(carrier);
|
||||
} catch (Exception e) {
|
||||
log.error("Exception occurred while trying to extract span from carrier", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String getChannelName(MessageChannel channel) {
|
||||
String name = null;
|
||||
if (ClassUtils.isPresent(
|
||||
"org.springframework.integration.context.IntegrationObjectSupport",
|
||||
null)) {
|
||||
if (channel instanceof IntegrationObjectSupport) {
|
||||
name = ((IntegrationObjectSupport) channel).getComponentName();
|
||||
}
|
||||
if (name == null && channel instanceof AbstractMessageChannel) {
|
||||
name = ((AbstractMessageChannel) channel).getFullChannelName();
|
||||
}
|
||||
}
|
||||
if (name == null) {
|
||||
name = channel.toString();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
String getMessageChannelName(MessageChannel channel) {
|
||||
return SpanNameUtil.shorten(MESSAGE_COMPONENT + ":" + getChannelName(channel));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.cloud.sleuth.util.TextMapUtil;
|
||||
|
||||
/**
|
||||
* Default implementation for messaging
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class HeaderBasedMessagingExtractor implements MessagingSpanTextMapExtractor {
|
||||
|
||||
private final Random random = new Random();
|
||||
|
||||
@Override
|
||||
public Span joinTrace(SpanTextMap textMap) {
|
||||
Map<String, String> carrier = TextMapUtil.asMap(textMap);
|
||||
boolean spanIdMissing = !hasHeader(carrier, TraceMessageHeaders.SPAN_ID_NAME);
|
||||
boolean traceIdMissing = !hasHeader(carrier, TraceMessageHeaders.TRACE_ID_NAME);
|
||||
if (Span.SPAN_SAMPLED.equals(carrier.get(TraceMessageHeaders.SPAN_FLAGS_NAME))) {
|
||||
String traceId = generateTraceIdIfMissing(carrier, traceIdMissing);
|
||||
if (spanIdMissing) {
|
||||
carrier.put(TraceMessageHeaders.SPAN_ID_NAME, traceId);
|
||||
}
|
||||
} else if (spanIdMissing) {
|
||||
return null;
|
||||
// TODO: Consider throwing IllegalArgumentException;
|
||||
}
|
||||
boolean idMissing = spanIdMissing || traceIdMissing;
|
||||
return extractSpanFromHeaders(carrier, Span.builder(), idMissing);
|
||||
}
|
||||
|
||||
private String generateTraceIdIfMissing(Map<String, String> carrier,
|
||||
boolean traceIdMissing) {
|
||||
if (traceIdMissing) {
|
||||
carrier.put(TraceMessageHeaders.TRACE_ID_NAME, Span.idToHex(this.random.nextLong()));
|
||||
}
|
||||
return carrier.get(TraceMessageHeaders.TRACE_ID_NAME);
|
||||
}
|
||||
|
||||
private Span extractSpanFromHeaders(Map<String, String> carrier,
|
||||
Span.SpanBuilder spanBuilder, boolean idMissing) {
|
||||
String traceId = carrier.get(TraceMessageHeaders.TRACE_ID_NAME);
|
||||
spanBuilder = spanBuilder
|
||||
.traceIdHigh(traceId.length() == 32 ? Span.hexToId(traceId, 0) : 0)
|
||||
.traceId(Span.hexToId(traceId))
|
||||
.spanId(Span.hexToId(carrier.get(TraceMessageHeaders.SPAN_ID_NAME)));
|
||||
String flags = carrier.get(TraceMessageHeaders.SPAN_FLAGS_NAME);
|
||||
boolean debug = Span.SPAN_SAMPLED.equals(flags);
|
||||
boolean spanSampled = Span.SPAN_SAMPLED.equals(carrier.get(TraceMessageHeaders.SAMPLED_NAME));
|
||||
if (debug) {
|
||||
spanBuilder.exportable(true);
|
||||
} else {
|
||||
spanBuilder.exportable(spanSampled);
|
||||
}
|
||||
String processId = carrier.get(TraceMessageHeaders.PROCESS_ID_NAME);
|
||||
String spanName = carrier.get(TraceMessageHeaders.SPAN_NAME_NAME);
|
||||
if (spanName != null) {
|
||||
spanBuilder.name(spanName);
|
||||
}
|
||||
if (processId != null) {
|
||||
spanBuilder.processId(processId);
|
||||
}
|
||||
setParentIdIfApplicable(carrier, spanBuilder, TraceMessageHeaders.PARENT_ID_NAME);
|
||||
spanBuilder.remote(true);
|
||||
spanBuilder.shared((debug || spanSampled) && !idMissing);
|
||||
for (Map.Entry<String, String> entry : carrier.entrySet()) {
|
||||
if (entry.getKey().toLowerCase().startsWith(Span.SPAN_BAGGAGE_HEADER_PREFIX + TraceMessageHeaders.HEADER_DELIMITER)) {
|
||||
spanBuilder.baggage(unprefixedKey(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
return spanBuilder.build();
|
||||
}
|
||||
|
||||
boolean hasHeader(Map<String, String> message, String name) {
|
||||
return message.containsKey(name);
|
||||
}
|
||||
|
||||
private void setParentIdIfApplicable(Map<String, String> carrier, Span.SpanBuilder spanBuilder,
|
||||
String spanParentIdHeader) {
|
||||
String parentId = carrier.get(spanParentIdHeader);
|
||||
if (parentId != null) {
|
||||
spanBuilder.parent(Span.hexToId(parentId));
|
||||
}
|
||||
}
|
||||
|
||||
private String unprefixedKey(String key) {
|
||||
return key.substring(key.indexOf(TraceMessageHeaders.HEADER_DELIMITER) + 1).toLowerCase();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.util.TextMapUtil;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Default implementation for messaging
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class HeaderBasedMessagingInjector implements MessagingSpanTextMapInjector {
|
||||
|
||||
private final TraceKeys traceKeys;
|
||||
|
||||
public HeaderBasedMessagingInjector(TraceKeys traceKeys) {
|
||||
this.traceKeys = traceKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inject(Span span, SpanTextMap carrier) {
|
||||
Map<String, String> map = TextMapUtil.asMap(carrier);
|
||||
if (span == null) {
|
||||
if (!isSampled(map, TraceMessageHeaders.SAMPLED_NAME)) {
|
||||
carrier.put(TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
addHeaders(map, span, carrier);
|
||||
}
|
||||
|
||||
private boolean isSampled(Map<String, String> initialMessage, String sampledHeaderName) {
|
||||
return Span.SPAN_SAMPLED.equals(initialMessage.get(sampledHeaderName));
|
||||
}
|
||||
|
||||
private void addHeaders(Map<String, String> map, Span span, SpanTextMap textMap) {
|
||||
addHeader(map, textMap, TraceMessageHeaders.TRACE_ID_NAME, span.traceIdString());
|
||||
addHeader(map, textMap, TraceMessageHeaders.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
|
||||
if (span.isExportable()) {
|
||||
addAnnotations(this.traceKeys, textMap, span);
|
||||
Long parentId = getFirst(span.getParents());
|
||||
if (parentId != null) {
|
||||
addHeader(map, textMap, TraceMessageHeaders.PARENT_ID_NAME, Span.idToHex(parentId));
|
||||
}
|
||||
addHeader(map, textMap, TraceMessageHeaders.SPAN_NAME_NAME, span.getName());
|
||||
addHeader(map, textMap, TraceMessageHeaders.PROCESS_ID_NAME, span.getProcessId());
|
||||
addHeader(map, textMap, TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_SAMPLED);
|
||||
}
|
||||
else {
|
||||
addHeader(map, textMap, TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
}
|
||||
for (Map.Entry<String, String> entry : span.baggageItems()) {
|
||||
textMap.put(prefixedKey(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void addAnnotations(TraceKeys traceKeys, SpanTextMap spanTextMap, Span span) {
|
||||
Map<String, String> map = TextMapUtil.asMap(spanTextMap);
|
||||
for (String name : traceKeys.getMessage().getHeaders()) {
|
||||
if (map.containsKey(name)) {
|
||||
String key = traceKeys.getMessage().getPrefix() + name.toLowerCase();
|
||||
Object value = map.get(name);
|
||||
if (value == null) {
|
||||
value = "null";
|
||||
}
|
||||
// TODO: better way to serialize?
|
||||
tagIfEntryMissing(span, key, value.toString());
|
||||
}
|
||||
}
|
||||
addPayloadAnnotations(traceKeys, map, span);
|
||||
}
|
||||
|
||||
private void addPayloadAnnotations(TraceKeys traceKeys, Map<String, String> map, Span span) {
|
||||
if (map.containsKey(traceKeys.getMessage().getPayload().getType())) {
|
||||
tagIfEntryMissing(span, traceKeys.getMessage().getPayload().getType(),
|
||||
map.get(traceKeys.getMessage().getPayload().getType()));
|
||||
tagIfEntryMissing(span, traceKeys.getMessage().getPayload().getSize(),
|
||||
map.get(traceKeys.getMessage().getPayload().getSize()));
|
||||
}
|
||||
}
|
||||
|
||||
private void tagIfEntryMissing(Span span, String key, String value) {
|
||||
if (!span.tags().containsKey(key)) {
|
||||
span.tag(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void addHeader(Map<String, String> map, SpanTextMap textMap, String name, String value) {
|
||||
if (StringUtils.hasText(value) && !map.containsKey(name)) {
|
||||
textMap.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private Long getFirst(List<Long> parents) {
|
||||
return parents.isEmpty() ? null : parents.get(0);
|
||||
}
|
||||
|
||||
private String prefixedKey(String key) {
|
||||
if (key.startsWith(Span.SPAN_BAGGAGE_HEADER_PREFIX + TraceMessageHeaders.HEADER_DELIMITER )) {
|
||||
return key;
|
||||
}
|
||||
return Span.SPAN_BAGGAGE_HEADER_PREFIX + TraceMessageHeaders.HEADER_DELIMITER + key;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-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.instrument.messaging;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.integration.channel.ChannelInterceptorAware;
|
||||
import org.springframework.integration.channel.interceptor.VetoCapableInterceptor;
|
||||
import org.springframework.messaging.support.ChannelInterceptor;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
class IntegrationTraceChannelInterceptor extends TraceChannelInterceptor implements VetoCapableInterceptor {
|
||||
|
||||
IntegrationTraceChannelInterceptor(BeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldIntercept(String beanName, ChannelInterceptorAware channel) {
|
||||
for (ChannelInterceptor interceptor : channel.getChannelInterceptors()) {
|
||||
if (interceptor instanceof AbstractTraceChannelInterceptor) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import brave.propagation.Propagation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.messaging.support.MessageHeaderAccessor;
|
||||
import org.springframework.messaging.support.NativeMessageHeaderAccessor;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.messaging.support.NativeMessageHeaderAccessor.NATIVE_HEADERS;
|
||||
|
||||
/**
|
||||
* This always sets native headers in defence of STOMP issues discussed <a href="https://github.com/spring-cloud/spring-cloud-sleuth/issues/716#issuecomment-337523705">here</a>
|
||||
*/
|
||||
enum MessageHeaderPropagation
|
||||
implements Propagation.Setter<MessageHeaderAccessor, String>,
|
||||
Propagation.Getter<MessageHeaderAccessor, String> {
|
||||
INSTANCE;
|
||||
|
||||
private static final Log log = LogFactory.getLog(MessageHeaderPropagation.class);
|
||||
|
||||
private static final Map<String, String> LEGACY_HEADER_MAPPING = new HashMap<>();
|
||||
|
||||
private static final String TRACE_ID_NAME = "X-B3-TraceId";
|
||||
private static final String SPAN_ID_NAME = "X-B3-SpanId";
|
||||
private static final String PARENT_SPAN_ID_NAME = "X-B3-ParentSpanId";
|
||||
private static final String SAMPLED_NAME = "X-B3-Sampled";
|
||||
private static final String FLAGS_NAME = "X-B3-Flags";
|
||||
|
||||
static {
|
||||
LEGACY_HEADER_MAPPING.put(TRACE_ID_NAME, TraceMessageHeaders.TRACE_ID_NAME);
|
||||
LEGACY_HEADER_MAPPING.put(SPAN_ID_NAME, TraceMessageHeaders.SPAN_ID_NAME);
|
||||
LEGACY_HEADER_MAPPING.put(PARENT_SPAN_ID_NAME, TraceMessageHeaders.PARENT_ID_NAME);
|
||||
LEGACY_HEADER_MAPPING.put(SAMPLED_NAME, TraceMessageHeaders.SAMPLED_NAME);
|
||||
LEGACY_HEADER_MAPPING.put(FLAGS_NAME, TraceMessageHeaders.SPAN_FLAGS_NAME);
|
||||
}
|
||||
|
||||
@Override public void put(MessageHeaderAccessor accessor, String key, String value) {
|
||||
try {
|
||||
doPut(accessor, key, value);
|
||||
} catch (Exception e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("An exception happened when we tried to retrieve the [" + key + "] from message", e);
|
||||
}
|
||||
}
|
||||
String legacyKey = LEGACY_HEADER_MAPPING.get(key);
|
||||
if (legacyKey != null) {
|
||||
doPut(accessor, legacyKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void doPut(MessageHeaderAccessor accessor, String key, String value) {
|
||||
accessor.setHeader(key, value);
|
||||
if (accessor instanceof NativeMessageHeaderAccessor) {
|
||||
NativeMessageHeaderAccessor nativeAccessor = (NativeMessageHeaderAccessor) accessor;
|
||||
nativeAccessor.setNativeHeader(key, value);
|
||||
}
|
||||
else {
|
||||
Map<String, List<String>> nativeHeaders = (Map) accessor
|
||||
.getHeader(NATIVE_HEADERS);
|
||||
if (nativeHeaders == null) {
|
||||
accessor.setHeader(NATIVE_HEADERS,
|
||||
nativeHeaders = new LinkedMultiValueMap<>());
|
||||
}
|
||||
nativeHeaders.put(key, Collections.singletonList(value));
|
||||
}
|
||||
}
|
||||
|
||||
@Override public String get(MessageHeaderAccessor accessor, String key) {
|
||||
try {
|
||||
String value = doGet(accessor, key);
|
||||
if (StringUtils.hasText(value)) {
|
||||
return value;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("An exception happened when we tried to retrieve the [" + key + "] from message", e);
|
||||
}
|
||||
}
|
||||
return legacyValue(accessor, key);
|
||||
}
|
||||
|
||||
private String legacyValue(MessageHeaderAccessor accessor, String key) {
|
||||
String legacyKey = LEGACY_HEADER_MAPPING.get(key);
|
||||
if (legacyKey != null) {
|
||||
return doGet(accessor, legacyKey);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String doGet(MessageHeaderAccessor accessor, String key) {
|
||||
if (accessor instanceof NativeMessageHeaderAccessor) {
|
||||
NativeMessageHeaderAccessor nativeAccessor = (NativeMessageHeaderAccessor) accessor;
|
||||
String result = nativeAccessor.getFirstNativeHeader(key);
|
||||
if (result != null)
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
Map<String, List<String>> nativeHeaders = (Map) accessor
|
||||
.getHeader(NATIVE_HEADERS);
|
||||
if (nativeHeaders != null) {
|
||||
List<String> result = nativeHeaders.get(key);
|
||||
if (result != null && !result.isEmpty())
|
||||
return result.get(0);
|
||||
}
|
||||
}
|
||||
Object result = accessor.getHeader(key);
|
||||
return result != null ? result.toString() : null;
|
||||
}
|
||||
|
||||
static void removeAnyTraceHeaders(MessageHeaderAccessor accessor,
|
||||
List<String> keysToRemove) {
|
||||
for (String keyToRemove : keysToRemove) {
|
||||
accessor.removeHeader(keyToRemove);
|
||||
if (accessor instanceof NativeMessageHeaderAccessor) {
|
||||
NativeMessageHeaderAccessor nativeAccessor = (NativeMessageHeaderAccessor) accessor;
|
||||
nativeAccessor.removeNativeHeader(keyToRemove);
|
||||
}
|
||||
else {
|
||||
Map<String, List<String>> nativeHeaders = (Map) accessor
|
||||
.getHeader(NATIVE_HEADERS);
|
||||
if (nativeHeaders == null)
|
||||
continue;
|
||||
nativeHeaders.remove(keyToRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "MessageHeaderPropagation{}";
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
|
||||
/**
|
||||
* Contract for extracting tracing headers from a {@link SpanTextMap}
|
||||
* via message headers
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public interface MessagingSpanTextMapExtractor extends SpanExtractor<SpanTextMap> {
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
|
||||
/**
|
||||
* Contract for injecting tracing headers from a {@link SpanTextMap}
|
||||
* via message headers
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public interface MessagingSpanTextMapInjector extends SpanInjector<SpanTextMap> {
|
||||
}
|
||||
@@ -1,88 +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.instrument.messaging;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.messaging.support.MessageHeaderAccessor;
|
||||
import org.springframework.messaging.support.NativeMessageHeaderAccessor;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link SpanTextMap} abstraction over {@link MessageBuilder}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
class MessagingTextMap implements SpanTextMap {
|
||||
|
||||
private final MessageBuilder<?> delegate;
|
||||
|
||||
public MessagingTextMap(MessageBuilder<?> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, String>> iterator() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (Map.Entry<String, Object> entry : this.delegate.build().getHeaders()
|
||||
.entrySet()) {
|
||||
if (!NativeMessageHeaderAccessor.NATIVE_HEADERS.equals(entry.getKey())) {
|
||||
map.put(entry.getKey(), String.valueOf(entry.getValue()));
|
||||
}
|
||||
}
|
||||
return map.entrySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void put(String key, String value) {
|
||||
if (!StringUtils.hasText(value)) {
|
||||
return;
|
||||
}
|
||||
Message<?> initialMessage = this.delegate.build();
|
||||
MessageHeaderAccessor accessor = MessageHeaderAccessor
|
||||
.getMutableAccessor(initialMessage);
|
||||
accessor.setHeader(key, value);
|
||||
if (accessor instanceof SimpMessageHeaderAccessor) {
|
||||
SimpMessageHeaderAccessor nativeAccessor = (SimpMessageHeaderAccessor) accessor;
|
||||
nativeAccessor.setNativeHeader(key, value);
|
||||
}
|
||||
else if (accessor.getHeader(NativeMessageHeaderAccessor.NATIVE_HEADERS) != null) {
|
||||
if (accessor.getHeader(
|
||||
NativeMessageHeaderAccessor.NATIVE_HEADERS) instanceof MultiValueMap) {
|
||||
MultiValueMap<String, String> map = (MultiValueMap<String, String>) accessor
|
||||
.getHeader(NativeMessageHeaderAccessor.NATIVE_HEADERS);
|
||||
map.add(key, value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
|
||||
accessor.setHeader(NativeMessageHeaderAccessor.NATIVE_HEADERS, map);
|
||||
map.add(key, value);
|
||||
}
|
||||
this.delegate.copyHeaders(accessor.toMessageHeaders());
|
||||
}
|
||||
}
|
||||
@@ -1,223 +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.instrument.messaging;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.Log;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.sampler.NeverSampler;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.support.ErrorMessage;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.messaging.support.MessageHeaderAccessor;
|
||||
|
||||
/**
|
||||
* A channel interceptor that automatically starts / continues / closes and detaches
|
||||
* spans.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
|
||||
|
||||
private static final org.apache.commons.logging.Log log = LogFactory
|
||||
.getLog(TraceChannelInterceptor.class);
|
||||
|
||||
public TraceChannelInterceptor(BeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSendCompletion(Message<?> message, MessageChannel channel,
|
||||
boolean sent, Exception ex) {
|
||||
Message<?> retrievedMessage = getMessage(message);
|
||||
MessageBuilder<?> messageBuilder = MessageBuilder.fromMessage(retrievedMessage);
|
||||
Span currentSpan = getTracer().isTracing() ? getTracer().getCurrentSpan()
|
||||
: buildSpan(new MessagingTextMap(messageBuilder));
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Completed sending and current span is " + currentSpan);
|
||||
}
|
||||
getTracer().continueSpan(currentSpan);
|
||||
if (containsServerReceived(currentSpan)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Marking span with server send");
|
||||
}
|
||||
currentSpan.logEvent(Span.SERVER_SEND);
|
||||
}
|
||||
else if (currentSpan != null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Marking span with client received");
|
||||
}
|
||||
currentSpan.logEvent(Span.CLIENT_RECV);
|
||||
}
|
||||
addErrorTag(ex);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Closing messaging span " + currentSpan);
|
||||
}
|
||||
getTracer().close(currentSpan);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Messaging span " + currentSpan + " successfully closed");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean containsServerReceived(Span span) {
|
||||
if (span == null) {
|
||||
return false;
|
||||
}
|
||||
for (Log log : span.logs()) {
|
||||
if (Span.SERVER_RECV.equals(log.getEvent())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Processing message before sending it to the channel");
|
||||
}
|
||||
Message<?> retrievedMessage = getMessage(message);
|
||||
MessageBuilder<?> messageBuilder = MessageBuilder.fromMessage(retrievedMessage);
|
||||
Span parentSpan = getTracer().isTracing() ? getTracer().getCurrentSpan()
|
||||
: buildSpan(new MessagingTextMap(messageBuilder));
|
||||
// Do not continue the parent (assume that this is handled by caller)
|
||||
// getTracer().continueSpan(parentSpan);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Parent span is " + parentSpan);
|
||||
}
|
||||
String name = getMessageChannelName(channel);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Name of the span will be [" + name + "]");
|
||||
}
|
||||
Span span = startSpan(parentSpan, name, message);
|
||||
if (message.getHeaders()
|
||||
.containsKey(TraceMessageHeaders.MESSAGE_SENT_FROM_CLIENT)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Marking span with server received");
|
||||
}
|
||||
span.logEvent(Span.SERVER_RECV);
|
||||
}
|
||||
else {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Marking span with client send");
|
||||
}
|
||||
span.logEvent(Span.CLIENT_SEND);
|
||||
messageBuilder.setHeader(TraceMessageHeaders.MESSAGE_SENT_FROM_CLIENT, true);
|
||||
}
|
||||
getSpanInjector().inject(span, new MessagingTextMap(messageBuilder));
|
||||
MessageHeaderAccessor headers = MessageHeaderAccessor.getMutableAccessor(message);
|
||||
if (message instanceof ErrorMessage) {
|
||||
headers.copyHeaders(sleuthHeaders(messageBuilder.build().getHeaders()));
|
||||
return new ErrorMessage((Throwable) message.getPayload(), headers.getMessageHeaders());
|
||||
}
|
||||
headers.copyHeaders(messageBuilder.build().getHeaders());
|
||||
return new GenericMessage<>(message.getPayload(), headers.getMessageHeaders());
|
||||
}
|
||||
|
||||
private Map<String, ?> sleuthHeaders(Map<String, ?> headers) {
|
||||
Map<String, Object> headersToCopy = new HashMap<>();
|
||||
for (Map.Entry<String, ?> entry : headers.entrySet()) {
|
||||
if (TraceMessageHeaders.ALL_HEADERS.contains(entry.getKey())) {
|
||||
headersToCopy.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return headersToCopy;
|
||||
}
|
||||
|
||||
private Message<?> getMessage(Message<?> message) {
|
||||
Object payload = message.getPayload();
|
||||
if (payload instanceof MessagingException) {
|
||||
MessagingException e = (MessagingException) payload;
|
||||
return e.getFailedMessage();
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private Span startSpan(Span span, String name, Message<?> message) {
|
||||
if (span != null) {
|
||||
return getTracer().createSpan(name, span);
|
||||
}
|
||||
if (Span.SPAN_NOT_SAMPLED
|
||||
.equals(message.getHeaders().get(TraceMessageHeaders.SAMPLED_NAME))) {
|
||||
return getTracer().createSpan(name, NeverSampler.INSTANCE);
|
||||
}
|
||||
return getTracer().createSpan(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> beforeHandle(Message<?> message, MessageChannel channel,
|
||||
MessageHandler handler) {
|
||||
Message<?> retrievedMessage = getMessage(message);
|
||||
MessageBuilder<?> messageBuilder = MessageBuilder.fromMessage(retrievedMessage);
|
||||
Span spanFromHeader = getTracer().isTracing() ? getTracer().getCurrentSpan()
|
||||
: buildSpan(new MessagingTextMap(messageBuilder));
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Continuing span " + spanFromHeader + " before handling message");
|
||||
}
|
||||
if (spanFromHeader != null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Marking span with server received");
|
||||
}
|
||||
spanFromHeader.logEvent(Span.SERVER_RECV);
|
||||
}
|
||||
getTracer().continueSpan(spanFromHeader);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Span " + spanFromHeader + " successfully continued");
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterMessageHandled(Message<?> message, MessageChannel channel,
|
||||
MessageHandler handler, Exception ex) {
|
||||
Span spanFromHeader = getTracer().getCurrentSpan();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Continuing span " + spanFromHeader + " after message handled");
|
||||
}
|
||||
if (spanFromHeader != null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Marking span with server send");
|
||||
}
|
||||
spanFromHeader.logEvent(Span.SERVER_SEND);
|
||||
addErrorTag(ex);
|
||||
}
|
||||
// related to #447
|
||||
if (getTracer().isTracing()) {
|
||||
getTracer().detach(spanFromHeader);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Detached " + spanFromHeader + " from current thread");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addErrorTag(Exception ex) {
|
||||
if (ex != null) {
|
||||
getErrorParser().parseErrorTags(getTracer().getCurrentSpan(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Contains trace related messaging headers. The deprecated headers contained `-` which
|
||||
* for example in the JMS specs is invalid. That's why the public constants in this class
|
||||
@@ -31,16 +28,10 @@ public class TraceMessageHeaders {
|
||||
|
||||
public static final String SPAN_ID_NAME = "spanId";
|
||||
public static final String SAMPLED_NAME = "spanSampled";
|
||||
public static final String PROCESS_ID_NAME = "spanProcessId";
|
||||
public static final String PARENT_ID_NAME = "spanParentSpanId";
|
||||
public static final String TRACE_ID_NAME = "spanTraceId";
|
||||
public static final String SPAN_NAME_NAME = "spanName";
|
||||
public static final String SPAN_FLAGS_NAME = "spanFlags";
|
||||
static final List<String> ALL_HEADERS = Arrays.asList(SPAN_ID_NAME, SAMPLED_NAME,
|
||||
PROCESS_ID_NAME, PARENT_ID_NAME, TRACE_ID_NAME, SPAN_NAME_NAME, SPAN_FLAGS_NAME);
|
||||
|
||||
static final String MESSAGE_SENT_FROM_CLIENT = "messageSent";
|
||||
static final String HEADER_DELIMITER = "_";
|
||||
|
||||
private TraceMessageHeaders() {}
|
||||
}
|
||||
|
||||
@@ -1,51 +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.instrument.messaging;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* AutoConfiguration containing Span extractor and injector for messaging. Will be reused
|
||||
* by Messaging and WebSockets
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(Message.class)
|
||||
@ConditionalOnBean(Tracer.class)
|
||||
public class TraceSpanMessagingAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public MessagingSpanTextMapExtractor messagingSpanExtractor() {
|
||||
return new HeaderBasedMessagingExtractor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public MessagingSpanTextMapInjector messagingSpanInjector(TraceKeys traceKeys) {
|
||||
return new HeaderBasedMessagingInjector(traceKeys);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -16,14 +16,13 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import brave.Tracing;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -37,21 +36,20 @@ import org.springframework.integration.config.GlobalChannelInterceptor;
|
||||
* @author Spencer Gibb
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @see TraceChannelInterceptor
|
||||
* @see TracingChannelInterceptor
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(GlobalChannelInterceptor.class)
|
||||
@ConditionalOnBean(Tracer.class)
|
||||
@AutoConfigureAfter({ TraceAutoConfiguration.class,
|
||||
TraceSpanMessagingAutoConfiguration.class })
|
||||
@ConditionalOnBean(Tracing.class)
|
||||
@AutoConfigureAfter({ TraceAutoConfiguration.class })
|
||||
@ConditionalOnProperty(value = "spring.sleuth.integration.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(TraceKeys.class)
|
||||
public class TraceSpringIntegrationAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@GlobalChannelInterceptor(patterns = "${spring.sleuth.integration.patterns:*}")
|
||||
public TraceChannelInterceptor traceChannelInterceptor(BeanFactory beanFactory) {
|
||||
return new IntegrationTraceChannelInterceptor(beanFactory);
|
||||
public TracingChannelInterceptor traceChannelInterceptor(Tracing tracing) {
|
||||
return new TracingChannelInterceptor(tracing);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import brave.Span;
|
||||
import brave.SpanCustomizer;
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import brave.propagation.ThreadLocalSpan;
|
||||
import brave.propagation.TraceContext;
|
||||
import brave.propagation.TraceContextOrSamplingFlags;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.sleuth.util.SpanNameUtil;
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.context.IntegrationObjectSupport;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.support.ChannelInterceptorAdapter;
|
||||
import org.springframework.messaging.support.ErrorMessage;
|
||||
import org.springframework.messaging.support.ExecutorChannelInterceptor;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.messaging.support.MessageHeaderAccessor;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* This starts and propagates {@link Span.Kind#PRODUCER} span for each message sent (via native
|
||||
* headers. It also extracts or creates a {@link Span.Kind#CONSUMER} span for each message
|
||||
* received. This span is injected onto each message so it becomes the parent when a handler later
|
||||
* calls {@link MessageHandler#handleMessage(Message)}, or a another processing library calls {@link #nextSpan(Message)}.
|
||||
* <p>
|
||||
* <p>This implementation uses {@link ThreadLocalSpan} to propagate context between callbacks. This
|
||||
* is an alternative to {@code ThreadStatePropagationChannelInterceptor} which is less sensitive
|
||||
* to message manipulation by other interceptors.
|
||||
*/
|
||||
public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
|
||||
implements ExecutorChannelInterceptor {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TracingChannelInterceptor.class);
|
||||
|
||||
public static TracingChannelInterceptor create(Tracing tracing) {
|
||||
return new TracingChannelInterceptor(tracing);
|
||||
}
|
||||
|
||||
final Tracing tracing;
|
||||
final Tracer tracer;
|
||||
final ThreadLocalSpan threadLocalSpan;
|
||||
final TraceContext.Injector<MessageHeaderAccessor> injector;
|
||||
final TraceContext.Extractor<MessageHeaderAccessor> extractor;
|
||||
|
||||
TracingChannelInterceptor(Tracing tracing) {
|
||||
this.tracing = tracing;
|
||||
this.tracer = tracing.tracer();
|
||||
this.threadLocalSpan = ThreadLocalSpan.create(this.tracer);
|
||||
this.injector = tracing.propagation().injector(MessageHeaderPropagation.INSTANCE);
|
||||
this.extractor = tracing.propagation()
|
||||
.extractor(MessageHeaderPropagation.INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to create a span for processing the given message. Note: the result has no name and is
|
||||
* not started.
|
||||
* <p>
|
||||
* <p>This creates a child from identifiers extracted from the message headers, or a new span if
|
||||
* one couldn't be extracted.
|
||||
*/
|
||||
public Span nextSpan(Message<?> message) {
|
||||
MessageHeaderAccessor headers = mutableHeaderAccessor(message);
|
||||
TraceContextOrSamplingFlags extracted = this.extractor.extract(headers);
|
||||
headers.setImmutable();
|
||||
Span result = this.tracer.nextSpan(extracted);
|
||||
if (extracted.context() == null && !result.isNoop()) {
|
||||
addTags(message, result, null);
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Created a new span " + result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts and propagates {@link Span.Kind#PRODUCER} span for each message sent.
|
||||
*/
|
||||
@Override public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
MessageHeaderAccessor headers = mutableHeaderAccessor(message);
|
||||
TraceContextOrSamplingFlags extracted = this.extractor.extract(headers);
|
||||
Span span = this.threadLocalSpan.next(extracted);
|
||||
|
||||
MessageHeaderPropagation
|
||||
.removeAnyTraceHeaders(headers, this.tracing.propagation().keys());
|
||||
this.injector.inject(span.context(), headers);
|
||||
if (!span.isNoop()) {
|
||||
span.kind(Span.Kind.PRODUCER).name("send").start();
|
||||
addTags(message, span, channel);
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Created a new span in pre send" + span);
|
||||
}
|
||||
headers.setImmutable();
|
||||
return new GenericMessage<>(message.getPayload(), headers.getMessageHeaders());
|
||||
}
|
||||
|
||||
@Override public void afterSendCompletion(Message<?> message, MessageChannel channel,
|
||||
boolean sent, Exception ex) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Will finish the current span after completion " + this.tracer.currentSpan());
|
||||
}
|
||||
finishSpan(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* This starts a consumer span as a child of the incoming message or the current trace context,
|
||||
* placing it in scope until the receive completes.
|
||||
*/
|
||||
@Override public Message<?> postReceive(Message<?> message, MessageChannel channel) {
|
||||
MessageHeaderAccessor headers = mutableHeaderAccessor(message);
|
||||
TraceContextOrSamplingFlags extracted = this.extractor.extract(headers);
|
||||
Span span = this.threadLocalSpan.next(extracted);
|
||||
|
||||
MessageHeaderPropagation
|
||||
.removeAnyTraceHeaders(headers, this.tracing.propagation().keys());
|
||||
this.injector.inject(span.context(), headers);
|
||||
if (!span.isNoop()) {
|
||||
span.kind(Span.Kind.CONSUMER).name("receive").start();
|
||||
addTags(message, span, channel);
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Created a new span in post receive " + span);
|
||||
}
|
||||
headers.setImmutable();
|
||||
return new GenericMessage<>(message.getPayload(), headers.getMessageHeaders());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterReceiveCompletion(Message<?> message, MessageChannel channel,
|
||||
Exception ex) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Will finish the current span after receive completion " + this.tracer.currentSpan());
|
||||
}
|
||||
finishSpan(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* This starts a consumer span as a child of the incoming message or the current trace context.
|
||||
* It then creates a span for the handler, placing it in scope.
|
||||
*/
|
||||
@Override public Message<?> beforeHandle(Message<?> message, MessageChannel channel,
|
||||
MessageHandler handler) {
|
||||
MessageHeaderAccessor headers = mutableHeaderAccessor(message);
|
||||
TraceContextOrSamplingFlags extracted = this.extractor.extract(headers);
|
||||
|
||||
// Start and finish a consumer span as we will immediately process it.
|
||||
Span consumerSpan = this.tracer.nextSpan(extracted);
|
||||
if (!consumerSpan.isNoop()) {
|
||||
consumerSpan.kind(Span.Kind.CONSUMER).start();
|
||||
addTags(message, consumerSpan, channel);
|
||||
consumerSpan.finish();
|
||||
}
|
||||
|
||||
// create and scope a span for the message processor
|
||||
this.threadLocalSpan.next(TraceContextOrSamplingFlags.create(consumerSpan.context()))
|
||||
.name("handle").start();
|
||||
|
||||
// remove any trace headers, but don't re-inject as we are synchronously processing the
|
||||
// message and can rely on scoping to access this span later.
|
||||
MessageHeaderPropagation
|
||||
.removeAnyTraceHeaders(headers, this.tracing.propagation().keys());
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Created a new span in before handle" + consumerSpan);
|
||||
}
|
||||
if (message instanceof ErrorMessage) {
|
||||
return new ErrorMessage((Throwable) message.getPayload(), headers.getMessageHeaders());
|
||||
}
|
||||
headers.setImmutable();
|
||||
return new GenericMessage<>(message.getPayload(), headers.getMessageHeaders());
|
||||
}
|
||||
|
||||
@Override public void afterMessageHandled(Message<?> message, MessageChannel channel,
|
||||
MessageHandler handler, Exception ex) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Will finish the current span after message handled " + this.tracer.currentSpan());
|
||||
}
|
||||
finishSpan(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* When an upstream context was not present, lookup keys are unlikely added
|
||||
*/
|
||||
static void addTags(Message<?> message, SpanCustomizer result, MessageChannel channel) {
|
||||
// TODO topic etc
|
||||
if (channel != null) {
|
||||
result.tag("channel", messageChannelName(channel));
|
||||
}
|
||||
}
|
||||
|
||||
private static String channelName(MessageChannel channel) {
|
||||
String name = null;
|
||||
if (ClassUtils.isPresent(
|
||||
"org.springframework.integration.context.IntegrationObjectSupport",
|
||||
null)) {
|
||||
if (channel instanceof IntegrationObjectSupport) {
|
||||
name = ((IntegrationObjectSupport) channel).getComponentName();
|
||||
}
|
||||
if (name == null && channel instanceof AbstractMessageChannel) {
|
||||
name = ((AbstractMessageChannel) channel).getFullChannelName();
|
||||
}
|
||||
}
|
||||
if (name == null) {
|
||||
name = channel.toString();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
private static String messageChannelName(MessageChannel channel) {
|
||||
return SpanNameUtil.shorten("send:" + channelName(channel));
|
||||
}
|
||||
|
||||
void finishSpan(Exception error) {
|
||||
Span span = this.threadLocalSpan.remove();
|
||||
if (span == null || span.isNoop())
|
||||
return;
|
||||
if (error != null) { // an error occurred, adding error to span
|
||||
String message = error.getMessage();
|
||||
if (message == null)
|
||||
message = error.getClass().getSimpleName();
|
||||
span.tag("error", message);
|
||||
}
|
||||
span.finish();
|
||||
}
|
||||
|
||||
private MessageHeaderAccessor mutableHeaderAccessor(Message<?> message) {
|
||||
MessageHeaderAccessor headers = MessageHeaderAccessor.getMutableAccessor(getMessage(message));
|
||||
headers.setLeaveMutable(true);
|
||||
return headers;
|
||||
}
|
||||
|
||||
private Message<?> getMessage(Message<?> message) {
|
||||
Object payload = message.getPayload();
|
||||
if (payload instanceof MessagingException) {
|
||||
MessagingException e = (MessagingException) payload;
|
||||
return e.getFailedMessage();
|
||||
}
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,14 @@
|
||||
package org.springframework.cloud.sleuth.instrument.messaging.websocket;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import brave.Tracing;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.MessagingSpanTextMapExtractor;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.MessagingSpanTextMapInjector;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.TraceChannelInterceptor;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.TraceSpanMessagingAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.TracingChannelInterceptor;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.simp.config.ChannelRegistration;
|
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
|
||||
import org.springframework.web.socket.config.annotation.DelegatingWebSocketMessageBrokerConfiguration;
|
||||
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
||||
@@ -29,25 +22,15 @@ import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
||||
*
|
||||
* @see AbstractWebSocketMessageBrokerConfigurer
|
||||
*/
|
||||
@Component
|
||||
@Configuration
|
||||
@AutoConfigureAfter(TraceSpanMessagingAutoConfiguration.class)
|
||||
@ConditionalOnClass(DelegatingWebSocketMessageBrokerConfiguration.class)
|
||||
@ConditionalOnBean(Tracer.class)
|
||||
@ConditionalOnBean(Tracing.class)
|
||||
@ConditionalOnProperty(value = "spring.sleuth.integration.websockets.enabled", matchIfMissing = true)
|
||||
public class TraceWebSocketAutoConfiguration
|
||||
extends AbstractWebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Autowired
|
||||
BeanFactory beanFactory;
|
||||
@Autowired
|
||||
Tracer tracer;
|
||||
@Autowired
|
||||
TraceKeys traceKeys;
|
||||
@Autowired
|
||||
MessagingSpanTextMapExtractor spanExtractor;
|
||||
@Autowired
|
||||
MessagingSpanTextMapInjector spanInjector;
|
||||
Tracing tracing;
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
@@ -56,16 +39,16 @@ public class TraceWebSocketAutoConfiguration
|
||||
|
||||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry registry) {
|
||||
registry.configureBrokerChannel().setInterceptors(new TraceChannelInterceptor(this.beanFactory));
|
||||
registry.configureBrokerChannel().setInterceptors(TracingChannelInterceptor.create(this.tracing));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureClientOutboundChannel(ChannelRegistration registration) {
|
||||
registration.setInterceptors(new TraceChannelInterceptor(this.beanFactory));
|
||||
registration.setInterceptors(TracingChannelInterceptor.create(this.tracing));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureClientInboundChannel(ChannelRegistration registration) {
|
||||
registration.setInterceptors(new TraceChannelInterceptor(this.beanFactory));
|
||||
registration.setInterceptors(TracingChannelInterceptor.create(this.tracing));
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,11 @@ package org.springframework.cloud.sleuth.instrument.reactor;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
import brave.Tracing;
|
||||
import reactor.core.Fuseable;
|
||||
import reactor.core.Scannable;
|
||||
import reactor.core.publisher.Operators;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
/**
|
||||
* Reactive Span pointcuts factories
|
||||
@@ -19,18 +18,19 @@ import reactor.core.publisher.Operators;
|
||||
public abstract class ReactorSleuth {
|
||||
|
||||
/**
|
||||
* Return a span operator pointcut given a {@link Tracer}. This can be used in reactor
|
||||
* Return a span operator pointcut given a {@link Tracing}. This can be used in reactor
|
||||
* via {@link reactor.core.publisher.Flux#transform(Function)}, {@link
|
||||
* reactor.core.publisher.Mono#transform(Function)}, {@link
|
||||
* reactor.core.publisher.Hooks#onEachOperator(Function)} or {@link
|
||||
* reactor.core.publisher.Hooks#onLastOperator(Function)}.
|
||||
*
|
||||
* @param tracer the {@link Tracer} instance to use in this span operator
|
||||
* @param tracing the {@link Tracing} instance to use in this span operator
|
||||
* @param <T> an arbitrary type that is left unchanged by the span operator
|
||||
*
|
||||
* @return a new Span operator pointcut
|
||||
*/
|
||||
public static <T> Function<? super Publisher<T>, ? extends Publisher<T>> spanOperator(Tracer tracer) {
|
||||
public static <T> Function<? super Publisher<T>, ? extends Publisher<T>> spanOperator(
|
||||
Tracing tracing) {
|
||||
return Operators.lift(POINTCUT_FILTER, ((scannable, sub) -> {
|
||||
//do not trace fused flows
|
||||
if(scannable instanceof Fuseable && sub instanceof Fuseable.QueueSubscription){
|
||||
@@ -39,7 +39,7 @@ public abstract class ReactorSleuth {
|
||||
return new SpanSubscriber<>(
|
||||
sub,
|
||||
sub.currentContext(),
|
||||
tracer,
|
||||
tracing,
|
||||
scannable.name());
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -2,14 +2,16 @@ package org.springframework.cloud.sleuth.instrument.reactor;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import brave.propagation.TraceContextOrSamplingFlags;
|
||||
import reactor.core.CoreSubscriber;
|
||||
import reactor.util.Logger;
|
||||
import reactor.util.Loggers;
|
||||
import reactor.util.context.Context;
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
|
||||
/**
|
||||
* A trace representation of the {@link Subscriber}
|
||||
@@ -21,7 +23,8 @@ import reactor.util.context.Context;
|
||||
final class SpanSubscriber<T> extends AtomicBoolean implements Subscription,
|
||||
CoreSubscriber<T> {
|
||||
|
||||
private static final Logger log = Loggers.getLogger(SpanSubscriber.class);
|
||||
private static final Logger log = Loggers.getLogger(
|
||||
SpanSubscriber.class);
|
||||
|
||||
private final Span span;
|
||||
private final Span rootSpan;
|
||||
@@ -30,11 +33,11 @@ final class SpanSubscriber<T> extends AtomicBoolean implements Subscription,
|
||||
private final Tracer tracer;
|
||||
private Subscription s;
|
||||
|
||||
SpanSubscriber(Subscriber<? super T> subscriber, Context ctx, Tracer tracer,
|
||||
SpanSubscriber(Subscriber<? super T> subscriber, Context ctx, Tracing tracing,
|
||||
String name) {
|
||||
this.subscriber = subscriber;
|
||||
this.tracer = tracer;
|
||||
Span root = ctx.getOrDefault(Span.class, tracer.getCurrentSpan());
|
||||
this.tracer = tracing.tracer();
|
||||
Span root = ctx.getOrDefault(Span.class, this.tracer.currentSpan());
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Span from context [{}]", root);
|
||||
}
|
||||
@@ -42,7 +45,9 @@ final class SpanSubscriber<T> extends AtomicBoolean implements Subscription,
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Stored context root span [{}]", this.rootSpan);
|
||||
}
|
||||
this.span = tracer.createSpan(name, root);
|
||||
this.span = root != null ?
|
||||
this.tracer.nextSpan(TraceContextOrSamplingFlags.create(root.context()))
|
||||
.name(name) : this.tracer.nextSpan().name(name);
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Created span [{}], with name [{}]", this.span, name);
|
||||
}
|
||||
@@ -54,55 +59,29 @@ final class SpanSubscriber<T> extends AtomicBoolean implements Subscription,
|
||||
log.trace("On subscribe");
|
||||
}
|
||||
this.s = subscription;
|
||||
this.tracer.continueSpan(this.span);
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("On subscribe - span continued");
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(this.span)) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("On subscribe - span continued");
|
||||
}
|
||||
this.subscriber.onSubscribe(this);
|
||||
}
|
||||
this.subscriber.onSubscribe(this);
|
||||
}
|
||||
|
||||
@Override public void request(long n) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Request");
|
||||
}
|
||||
this.tracer.continueSpan(this.span);
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Request - continued");
|
||||
}
|
||||
this.s.request(n);
|
||||
// We're in the main thread so we don't want to pollute it with wrong spans
|
||||
// that's why we need to detach the current one and continue with its parent
|
||||
Span localRootSpan = this.span;
|
||||
while (localRootSpan != null) {
|
||||
if (this.rootSpan != null) {
|
||||
if (localRootSpan.getSpanId() != this.rootSpan.getSpanId() &&
|
||||
!isRootParentSpan(localRootSpan)) {
|
||||
localRootSpan = continueDetachedSpan(localRootSpan);
|
||||
} else {
|
||||
localRootSpan = null;
|
||||
}
|
||||
} else if (!isRootParentSpan(localRootSpan)) {
|
||||
localRootSpan = continueDetachedSpan(localRootSpan);
|
||||
} else {
|
||||
localRootSpan = null;
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(this.span)) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Request - continued");
|
||||
}
|
||||
this.s.request(n);
|
||||
// no additional cleaning is required cause we operate on scopes
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Request after cleaning. Current span [{}]",
|
||||
this.tracer.currentSpan());
|
||||
}
|
||||
}
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Request after cleaning. Current span [{}]",
|
||||
this.tracer.getCurrentSpan());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRootParentSpan(Span localRootSpan) {
|
||||
return localRootSpan.getSpanId() == localRootSpan.getTraceId();
|
||||
}
|
||||
|
||||
private Span continueDetachedSpan(Span localRootSpan) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Will detach span {}", localRootSpan);
|
||||
}
|
||||
Span detachedSpan = this.tracer.detach(localRootSpan);
|
||||
return this.tracer.continueSpan(detachedSpan);
|
||||
}
|
||||
|
||||
@Override public void cancel() {
|
||||
@@ -144,12 +123,12 @@ final class SpanSubscriber<T> extends AtomicBoolean implements Subscription,
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Cleaning up");
|
||||
}
|
||||
if (this.tracer.getCurrentSpan() != this.span) {
|
||||
Tracer.SpanInScope ws = null;
|
||||
if (this.tracer.currentSpan() != this.span) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Detaching span");
|
||||
}
|
||||
this.tracer.detach(this.tracer.getCurrentSpan());
|
||||
this.tracer.continueSpan(this.span);
|
||||
ws = this.tracer.withSpanInScope(this.span);
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Continuing span");
|
||||
}
|
||||
@@ -157,13 +136,15 @@ final class SpanSubscriber<T> extends AtomicBoolean implements Subscription,
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Closing span");
|
||||
}
|
||||
this.tracer.close(this.span);
|
||||
this.span.finish();
|
||||
if (ws != null) {
|
||||
ws.close();
|
||||
}
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Span closed");
|
||||
}
|
||||
if (this.rootSpan != null) {
|
||||
this.tracer.continueSpan(this.rootSpan);
|
||||
this.tracer.close(this.rootSpan);
|
||||
this.rootSpan.finish();
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Closed root span");
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package org.springframework.cloud.sleuth.instrument.reactor;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.function.Supplier;
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import brave.Tracing;
|
||||
import reactor.core.publisher.Hooks;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
@@ -12,16 +17,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.async.TraceableScheduledExecutorService;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceWebFluxAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import reactor.core.publisher.Hooks;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
|
||||
@@ -38,42 +37,31 @@ import reactor.core.scheduler.Schedulers;
|
||||
public class TraceReactorAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnBean(Tracer.class)
|
||||
@ConditionalOnBean(Tracing.class)
|
||||
static class TraceReactorConfiguration {
|
||||
@Autowired Tracer tracer;
|
||||
@Autowired TraceKeys traceKeys;
|
||||
@Autowired SpanNamer spanNamer;
|
||||
@Autowired Tracing tracing;
|
||||
@Autowired BeanFactory beanFactory;
|
||||
@Autowired LastOperatorWrapper lastOperatorWrapper;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnNotWebApplication
|
||||
LastOperatorWrapper spanOperator() {
|
||||
return new LastOperatorWrapper() {
|
||||
@Override public void wrapLastOperator(Tracer tracer) {
|
||||
Hooks.onLastOperator(ReactorSleuth.spanOperator(tracer));
|
||||
}
|
||||
};
|
||||
@ConditionalOnNotWebApplication LastOperatorWrapper spanOperator() {
|
||||
return tracer -> Hooks.onLastOperator(ReactorSleuth.spanOperator(tracer));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnWebApplication
|
||||
LastOperatorWrapper noOpLastOperatorWrapper() {
|
||||
return new LastOperatorWrapper() {
|
||||
@Override public void wrapLastOperator(Tracer tracer) {
|
||||
}
|
||||
};
|
||||
@ConditionalOnWebApplication LastOperatorWrapper noOpLastOperatorWrapper() {
|
||||
return tracer -> { };
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void setupHooks() {
|
||||
this.lastOperatorWrapper.wrapLastOperator(this.tracer);
|
||||
this.lastOperatorWrapper.wrapLastOperator(this.tracing);
|
||||
Schedulers.setFactory(new Schedulers.Factory() {
|
||||
@Override public ScheduledExecutorService decorateExecutorService(String schedulerType,
|
||||
Supplier<? extends ScheduledExecutorService> actual) {
|
||||
return new TraceableScheduledExecutorService(actual.get(),
|
||||
TraceReactorConfiguration.this.tracer,
|
||||
TraceReactorConfiguration.this.traceKeys,
|
||||
TraceReactorConfiguration.this.spanNamer);
|
||||
return new TraceableScheduledExecutorService(
|
||||
TraceReactorConfiguration.this.beanFactory,
|
||||
actual.get());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -87,5 +75,5 @@ public class TraceReactorAutoConfiguration {
|
||||
}
|
||||
|
||||
interface LastOperatorWrapper {
|
||||
void wrapLastOperator(Tracer tracer);
|
||||
void wrapLastOperator(Tracing tracer);
|
||||
}
|
||||
@@ -2,19 +2,19 @@ package org.springframework.cloud.sleuth.instrument.rxjava;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import rx.plugins.RxJavaSchedulersHook;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import rx.plugins.RxJavaSchedulersHook;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration} that
|
||||
* enables support for RxJava via {@link RxJavaSchedulersHook}.
|
||||
@@ -24,7 +24,7 @@ import rx.plugins.RxJavaSchedulersHook;
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureAfter(TraceAutoConfiguration.class)
|
||||
@ConditionalOnBean(Tracer.class)
|
||||
@ConditionalOnBean(Tracing.class)
|
||||
@ConditionalOnClass(RxJavaSchedulersHook.class)
|
||||
@ConditionalOnProperty(value = "spring.sleuth.rxjava.schedulers.hook.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(SleuthRxJavaSchedulersProperties.class)
|
||||
|
||||
@@ -2,12 +2,11 @@ package org.springframework.cloud.sleuth.instrument.rxjava;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
import rx.functions.Action0;
|
||||
import rx.plugins.RxJavaErrorHandler;
|
||||
import rx.plugins.RxJavaObservableExecutionHook;
|
||||
@@ -23,7 +22,8 @@ import rx.plugins.RxJavaSchedulersHook;
|
||||
*/
|
||||
class SleuthRxJavaSchedulersHook extends RxJavaSchedulersHook {
|
||||
|
||||
private static final Log log = LogFactory.getLog(SleuthRxJavaSchedulersHook.class);
|
||||
private static final Log log = LogFactory.getLog(
|
||||
SleuthRxJavaSchedulersHook.class);
|
||||
|
||||
private static final String RXJAVA_COMPONENT = "rxjava";
|
||||
private final Tracer tracer;
|
||||
@@ -93,7 +93,7 @@ class SleuthRxJavaSchedulersHook extends RxJavaSchedulersHook {
|
||||
this.tracer = tracer;
|
||||
this.traceKeys = traceKeys;
|
||||
this.threadsToIgnore = threadsToIgnore;
|
||||
this.parent = tracer.getCurrentSpan();
|
||||
this.parent = this.tracer.currentSpan();
|
||||
this.actual = actual;
|
||||
}
|
||||
|
||||
@@ -116,21 +116,19 @@ class SleuthRxJavaSchedulersHook extends RxJavaSchedulersHook {
|
||||
Span span = this.parent;
|
||||
boolean created = false;
|
||||
if (span != null) {
|
||||
span = this.tracer.continueSpan(span);
|
||||
span = this.tracer.joinSpan(this.parent.context());
|
||||
} else {
|
||||
span = this.tracer.createSpan(RXJAVA_COMPONENT);
|
||||
this.tracer.addTag(Span.SPAN_LOCAL_COMPONENT_TAG_NAME, RXJAVA_COMPONENT);
|
||||
this.tracer.addTag(this.traceKeys.getAsync().getPrefix()
|
||||
+ this.traceKeys.getAsync().getThreadNameKey(), Thread.currentThread().getName());
|
||||
span = this.tracer.nextSpan().name(RXJAVA_COMPONENT).start();
|
||||
span.tag(this.traceKeys.getAsync().getPrefix()
|
||||
+ this.traceKeys.getAsync().getThreadNameKey(),
|
||||
Thread.currentThread().getName());
|
||||
created = true;
|
||||
}
|
||||
try {
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
||||
this.actual.call();
|
||||
} finally {
|
||||
if (created) {
|
||||
this.tracer.close(span);
|
||||
} else if (this.tracer.isTracing()) {
|
||||
this.tracer.detach(span);
|
||||
span.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -18,12 +18,13 @@ package org.springframework.cloud.sleuth.instrument.scheduling;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.util.SpanNameUtil;
|
||||
|
||||
/**
|
||||
@@ -39,21 +40,20 @@ import org.springframework.cloud.sleuth.util.SpanNameUtil;
|
||||
* @author Spencer Gibb
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @see Tracer
|
||||
* @see Tracing
|
||||
*/
|
||||
@Aspect
|
||||
public class TraceSchedulingAspect {
|
||||
|
||||
private static final String SCHEDULED_COMPONENT = "scheduled";
|
||||
|
||||
private final Tracer tracer;
|
||||
private final TraceKeys traceKeys;
|
||||
private final Pattern skipPattern;
|
||||
private final TraceKeys traceKeys;
|
||||
|
||||
public TraceSchedulingAspect(Tracer tracer, TraceKeys traceKeys, Pattern skipPattern) {
|
||||
public TraceSchedulingAspect(Tracer tracer, Pattern skipPattern,
|
||||
TraceKeys traceKeys) {
|
||||
this.tracer = tracer;
|
||||
this.traceKeys = traceKeys;
|
||||
this.skipPattern = skipPattern;
|
||||
this.traceKeys = traceKeys;
|
||||
}
|
||||
|
||||
@Around("execution (@org.springframework.scheduling.annotation.Scheduled * *.*(..))")
|
||||
@@ -62,18 +62,24 @@ public class TraceSchedulingAspect {
|
||||
return pjp.proceed();
|
||||
}
|
||||
String spanName = SpanNameUtil.toLowerHyphen(pjp.getSignature().getName());
|
||||
Span span = this.tracer.createSpan(spanName);
|
||||
this.tracer.addTag(Span.SPAN_LOCAL_COMPONENT_TAG_NAME, SCHEDULED_COMPONENT);
|
||||
this.tracer.addTag(this.traceKeys.getAsync().getPrefix() +
|
||||
this.traceKeys.getAsync().getClassNameKey(), pjp.getTarget().getClass().getSimpleName());
|
||||
this.tracer.addTag(this.traceKeys.getAsync().getPrefix() +
|
||||
this.traceKeys.getAsync().getMethodNameKey(), pjp.getSignature().getName());
|
||||
try {
|
||||
Span span = startOrContinueRenamedSpan(spanName);
|
||||
try(Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
||||
span.tag(this.traceKeys.getAsync().getPrefix() +
|
||||
this.traceKeys.getAsync().getClassNameKey(), pjp.getTarget().getClass().getSimpleName());
|
||||
span.tag(this.traceKeys.getAsync().getPrefix() +
|
||||
this.traceKeys.getAsync().getMethodNameKey(), pjp.getSignature().getName());
|
||||
return pjp.proceed();
|
||||
}
|
||||
finally {
|
||||
this.tracer.close(span);
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
}
|
||||
|
||||
private Span startOrContinueRenamedSpan(String spanName) {
|
||||
Span currentSpan = this.tracer.currentSpan();
|
||||
if (currentSpan != null) {
|
||||
return currentSpan.name(spanName);
|
||||
}
|
||||
return this.tracer.nextSpan().name(spanName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -16,20 +16,21 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.scheduling;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Registers beans related to task scheduling.
|
||||
*
|
||||
@@ -42,15 +43,16 @@ import java.util.regex.Pattern;
|
||||
@Configuration
|
||||
@EnableAspectJAutoProxy
|
||||
@ConditionalOnProperty(value = "spring.sleuth.scheduled.enabled", matchIfMissing = true)
|
||||
@ConditionalOnBean(Tracer.class)
|
||||
@ConditionalOnBean(Tracing.class)
|
||||
@AutoConfigureAfter(TraceAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(SleuthSchedulingProperties.class)
|
||||
public class TraceSchedulingAutoConfiguration {
|
||||
|
||||
@ConditionalOnClass(name = "org.aspectj.lang.ProceedingJoinPoint")
|
||||
@Bean
|
||||
public TraceSchedulingAspect traceSchedulingAspect(Tracer tracer, TraceKeys traceKeys,
|
||||
SleuthSchedulingProperties sleuthSchedulingProperties) {
|
||||
return new TraceSchedulingAspect(tracer, traceKeys, Pattern.compile(sleuthSchedulingProperties.getSkipPattern()));
|
||||
@ConditionalOnClass(name = "org.aspectj.lang.ProceedingJoinPoint")
|
||||
public TraceSchedulingAspect traceSchedulingAspect(Tracer tracer,
|
||||
SleuthSchedulingProperties sleuthSchedulingProperties, TraceKeys traceKeys) {
|
||||
return new TraceSchedulingAspect(tracer,
|
||||
Pattern.compile(sleuthSchedulingProperties.getSkipPattern()), traceKeys);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,81 +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.instrument.web;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* A {@link SpanTextMap} abstraction over {@link HttpServletRequest}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
class HttpServletRequestTextMap implements SpanTextMap {
|
||||
|
||||
private final HttpServletRequest delegate;
|
||||
private final UrlPathHelper urlPathHelper;
|
||||
|
||||
HttpServletRequestTextMap(HttpServletRequest delegate) {
|
||||
this.delegate = delegate;
|
||||
this.urlPathHelper = new UrlPathHelper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, String>> iterator() {
|
||||
final Enumeration<String> headerNames = this.delegate.getHeaderNames();
|
||||
|
||||
return new Iterator<Map.Entry<String, String>>() {
|
||||
|
||||
private boolean useAdditionalHeader = true;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return useAdditionalHeader
|
||||
|| (headerNames != null && headerNames.hasMoreElements());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<String, String> next() {
|
||||
if (useAdditionalHeader) {
|
||||
useAdditionalHeader = false;
|
||||
return new AbstractMap.SimpleImmutableEntry<>(
|
||||
ZipkinHttpSpanMapper.URI_HEADER,
|
||||
HttpServletRequestTextMap.this.urlPathHelper
|
||||
.getPathWithinApplication(
|
||||
HttpServletRequestTextMap.this.delegate));
|
||||
}
|
||||
|
||||
String name = headerNames.nextElement();
|
||||
String value = HttpServletRequestTextMap.this.delegate.getHeader(name);
|
||||
return new AbstractMap.SimpleEntry<>(name, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, String value) {
|
||||
throw new UnsupportedOperationException("change servlet request isn't supported");
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
|
||||
/**
|
||||
* Contract for extracting tracing headers from a {@link SpanTextMap}
|
||||
* via HTTP headers
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public interface HttpSpanExtractor extends SpanExtractor<SpanTextMap> {
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
|
||||
/**
|
||||
* Contract for injecting tracing headers from a {@link SpanTextMap}
|
||||
* via HTTP headers
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public interface HttpSpanInjector extends SpanInjector<SpanTextMap> {
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Injects HTTP related keys to the current span.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.1
|
||||
*/
|
||||
public class HttpTraceKeysInjector {
|
||||
|
||||
private final Tracer tracer;
|
||||
private final TraceKeys traceKeys;
|
||||
|
||||
public HttpTraceKeysInjector(Tracer tracer, TraceKeys traceKeys) {
|
||||
this.tracer = tracer;
|
||||
this.traceKeys = traceKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds tags from the HTTP request to the current Span
|
||||
*/
|
||||
public void addRequestTags(String url, String host, String path, String method) {
|
||||
this.tracer.addTag(this.traceKeys.getHttp().getUrl(), url);
|
||||
this.tracer.addTag(this.traceKeys.getHttp().getHost(), host);
|
||||
this.tracer.addTag(this.traceKeys.getHttp().getPath(), path);
|
||||
this.tracer.addTag(this.traceKeys.getHttp().getMethod(), method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds tags from the HTTP request to the given Span
|
||||
*/
|
||||
public void addRequestTags(Span span, String url, String host, String path, String method) {
|
||||
tagSpan(span, this.traceKeys.getHttp().getUrl(), url);
|
||||
tagSpan(span, this.traceKeys.getHttp().getHost(), host);
|
||||
tagSpan(span, this.traceKeys.getHttp().getPath(), path);
|
||||
tagSpan(span, this.traceKeys.getHttp().getMethod(), method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds tags from the HTTP request to the given Span
|
||||
*/
|
||||
public void addRequestTags(Span span, URI uri, String method) {
|
||||
addRequestTags(span, uri.toString(), uri.getHost(), uri.getPath(), method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds tags from the HTTP request together with headers to the current Span
|
||||
*/
|
||||
public void addRequestTags(String url, String host, String path, String method,
|
||||
Map<String, ? extends Collection<String>> headers) {
|
||||
addRequestTags(url, host, path, method);
|
||||
addRequestTagsFromHeaders(headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a tag to the given, exportable Span
|
||||
*/
|
||||
public void tagSpan(Span span, String key, String value) {
|
||||
if (span != null && span.isExportable()) {
|
||||
span.tag(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void addRequestTagsFromHeaders(Map<String, ? extends Collection<String>> headers) {
|
||||
for (String name : this.traceKeys.getHttp().getHeaders()) {
|
||||
Collection<String> values = headers.get(name);
|
||||
if (values != null) {
|
||||
addTagForEntry(name, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addTagForEntry(String name, Collection<String> list) {
|
||||
String key = this.traceKeys.getHttp().getPrefix() + name.toLowerCase();
|
||||
String value = list.size() == 1 ? list.iterator().next()
|
||||
: StringUtils.collectionToDelimitedString(list, ",", "'", "'");
|
||||
this.tracer.addTag(key, value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
|
||||
/**
|
||||
* Created by mgrzejszczak.
|
||||
*/
|
||||
class ServerHttpRequestTextMap implements SpanTextMap {
|
||||
|
||||
private final ServerHttpRequest delegate;
|
||||
private final Map<String, String> additionalHeaders = new HashMap<>();
|
||||
|
||||
ServerHttpRequestTextMap(ServerHttpRequest delegate) {
|
||||
this.delegate = delegate;
|
||||
this.additionalHeaders.put(ZipkinHttpSpanMapper.URI_HEADER,
|
||||
delegate.getPath().pathWithinApplication().value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, String>> iterator() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (Map.Entry<String, List<String>> entry : this.delegate.getHeaders()
|
||||
.entrySet()) {
|
||||
map.put(entry.getKey(), entry.getValue() != null ?
|
||||
entry.getValue().isEmpty() ? "" : entry.getValue().get(0) : "");
|
||||
}
|
||||
map.putAll(this.additionalHeaders);
|
||||
return map.entrySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, String value) {
|
||||
this.additionalHeaders.put(key, value);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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.instrument.web;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import brave.SpanCustomizer;
|
||||
import brave.http.HttpAdapter;
|
||||
import brave.http.HttpClientParser;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.util.SpanNameUtil;
|
||||
|
||||
/**
|
||||
* An {@link HttpClientParser} that behaves like Sleuth in versions 1.x
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 2.0.0
|
||||
*/
|
||||
class SleuthHttpClientParser extends HttpClientParser {
|
||||
|
||||
private final TraceKeys traceKeys;
|
||||
|
||||
public SleuthHttpClientParser(TraceKeys traceKeys) {
|
||||
this.traceKeys = traceKeys;
|
||||
}
|
||||
|
||||
@Override protected <Req> String spanName(HttpAdapter<Req, ?> adapter,
|
||||
Req req) {
|
||||
return getName(URI.create(adapter.url(req)));
|
||||
}
|
||||
|
||||
@Override public <Req> void request(HttpAdapter<Req, ?> adapter, Req req,
|
||||
SpanCustomizer customizer) {
|
||||
super.request(adapter, req, customizer);
|
||||
String url = adapter.url(req);
|
||||
URI uri = URI.create(url);
|
||||
addRequestTags(customizer, url, uri.getHost(), uri.getPath(), adapter.method(req));
|
||||
this.traceKeys.getHttp().getHeaders()
|
||||
.forEach(s -> {
|
||||
String headerValue = adapter.requestHeader(req, s);
|
||||
if (headerValue != null) {
|
||||
customizer.tag(key(s), headerValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String key(String key) {
|
||||
return this.traceKeys.getHttp().getPrefix() + key.toLowerCase();
|
||||
}
|
||||
|
||||
private String getName(URI uri) {
|
||||
// The returned name should comply with RFC 882 - Section 3.1.2.
|
||||
// i.e Header values must composed of printable ASCII values.
|
||||
return SpanNameUtil.shorten(uriScheme(uri) + ":" + uri.getRawPath());
|
||||
}
|
||||
|
||||
private String uriScheme(URI uri) {
|
||||
return uri.getScheme() == null ? "http" : uri.getScheme();
|
||||
}
|
||||
|
||||
private void addRequestTags(SpanCustomizer customizer, String url, String host,
|
||||
String path, String method) {
|
||||
customizer.tag(this.traceKeys.getHttp().getUrl(), url);
|
||||
if (host != null) {
|
||||
customizer.tag(this.traceKeys.getHttp().getHost(), host);
|
||||
}
|
||||
customizer.tag(this.traceKeys.getHttp().getPath(), path);
|
||||
customizer.tag(this.traceKeys.getHttp().getMethod(), method);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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.instrument.web;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Sleuth HTTP settings
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@ConfigurationProperties("spring.sleuth.http")
|
||||
public class SleuthHttpProperties {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
private Legacy legacy = new Legacy();
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public Legacy getLegacy() {
|
||||
return this.legacy;
|
||||
}
|
||||
|
||||
public void setLegacy(Legacy legacy) {
|
||||
this.legacy = legacy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy Sleuth support. Related to the way headers are parsed and tags are set
|
||||
*/
|
||||
public static class Legacy {
|
||||
|
||||
private boolean enabled = false;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user