Added a ZipkinRestTemplateProvider to allow customization of the rest template; fixes gh-1932

This commit is contained in:
Marcin Grzejszczak
2021-05-06 09:54:25 +02:00
parent efe08e68b4
commit 4fef057e96
5 changed files with 659 additions and 554 deletions

View File

@@ -1,494 +1,492 @@
[[features]]
[[project-features]]
= Spring Cloud Sleuth Features
include::_attributes.adoc[]
This section dives into the details of {project-full-name}.
Here you can learn about the key features that you may want to use and customize.
If you have not already done so, you might want to read the "<<getting-started.adoc#getting-started>>" and "<<using.adoc#using>>" sections, so that you have a good grounding in the basics.
[[features-context-propagation]]
== Context Propagation
Traces connect from service to service using header propagation.
The default format is https://github.com/openzipkin/b3-propagation[B3].
Similar to data formats, you can configure alternate header formats also, provided trace and span IDs are compatible with B3. Most notably, this means the trace ID and span IDs are lower-case hex, not UUIDs.
Besides trace identifiers, other properties (Baggage) can also be passed along with the request.
Remote Baggage must be predefined, but is flexible otherwise.
To use the provided defaults you can set the `spring.sleuth.propagation.type` property.
The value can be a list in which case you will propagate more tracing headers.
For Brave we support `AWS`, `B3`, `W3C` propagation types.
You can read more about how to provide custom context propagation in this "<<howto.adoc#how-to-change-context-propagation,how to section>>".
[[features-sampling]]
== Sampling
Spring Cloud Sleuth pushes the sampling decision down to the tracer implementation.
However, there are cases where you can change the sampling decision at runtime.
One of such cases is skip reporting of certain client spans.
To achieve that you can set the `spring.sleuth.web.client.skip-pattern` with the path patterns to be skipped.
Another option is to provide your own custom `org.springframework.cloud.sleuth.SamplerFunction<`org.springframework.cloud.sleuth.http.HttpRequest>` implementation and define when a given `HttpRequest` should not be sampled.
[[features-baggage]]
== Baggage
Distributed tracing works by propagating fields inside and across services that connect the trace together: traceId and spanId notably.
The context that holds these fields can optionally push other fields that need to be consistent regardless of many services are touched.
The simple name for these extra fields is "Baggage".
Sleuth allows you to define which baggage are permitted to exist in the trace context, including what header names are used.
The following example shows setting baggage values using Spring Cloud Sleuth's API:
[source,java,indent=0]
----
include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/baggage/multiple/MultipleHopsIntegrationTests.java[tags=baggage,indent=0]
----
IMPORTANT: There is currently no limitation of the count or size of baggage items.
Keep in mind that too many can decrease system throughput or increase RPC latency.
In extreme cases, too much baggage can crash the application, due to exceeding transport-level message or header capacity.
You can use properties to define fields that have no special configuration such as name mapping:
* `spring.sleuth.baggage.remote-fields` is a list of header names to accept and propagate to remote services.
* `spring.sleuth.baggage.local-fields` is a list of names to propagate locally
No prefixing applies with these keys.
What you set is literally what is used.
A name set in either of these properties will result in a `Baggage` of the same name.
In order to automatically set the baggage values to Slf4j's MDC, you have to set the `spring.sleuth.baggage.correlation-fields` property with a list of allowed local or remote keys. E.g. `spring.sleuth.baggage.correlation-fields=country-code` will set the value of the `country-code` baggage into MDC.
Note that the extra field is propagated and added to MDC starting with the next downstream trace context.
To immediately add the extra field to MDC in the current trace context, configure the field to flush on update:
```
// configuration
@Bean
BaggageField countryCodeField() {
return BaggageField.create("country-code");
}
@Bean
ScopeDecorator mdcScopeDecorator() {
return MDCScopeDecorator.newBuilder()
.clear()
.add(SingleCorrelationField.newBuilder(countryCodeField())
.flushOnUpdate()
.build())
.build();
}
// service
@Autowired
BaggageField countryCodeField;
countryCodeField.updateValue("new-value");
```
IMPORTANT: Remember that adding entries to MDC can drastically decrease the performance of your application!
If you want to add the baggage entries as tags, to make it possible to search for spans via the baggage entries, you can set the value of
`spring.sleuth.baggage.tag-fields` with a list of allowed baggage keys.
To disable the feature you have to pass the `spring.sleuth.propagation.tag.enabled=false` property.
[[features-baggage-vs-tags]]
=== Baggage versus Tags
Like trace IDs, Baggage is attached to messages or requests, usually as headers.
Tags are key value pairs sent in a Span to Zipkin.
Baggage values are not added spans by default, which means you can't search based on Baggage unless you opt-in.
To make baggage also tags, use the property `spring.sleuth.baggage.tag-fields`
like so:
[source,yml]
----
include::{brave_path}/src/test/resources/application-baggage.yml[indent=0]
----
[[features-brave]]
== OpenZipkin Brave Tracer Integration
Spring Cloud Sleuth integrates with the OpenZipkin Brave tracer via the bridge that is available in the `spring-cloud-sleuth-brave` module.
In this section you can read about specific Brave integrations.
You can choose to use either Sleuth's API or the Brave API directly in your code (e.g. either Sleuth's `Tracer` or Brave's `Tracer`).
If you want to use this tracer implementation's API directly please read https://github.com/openzipkin/brave[their documentation to learn more about it].
[[features-brave-basics]]
=== Brave Basics
Here are the most core types you might use:
* `brave.SpanCustomizer` - to change the span currently in progress
* `brave.Tracer` - to get a start new spans ad-hoc
Here are the most relevant links from the OpenZipkin Brave project:
* https://github.com/openzipkin/brave/tree/master/brave[Brave's core library]
* https://github.com/openzipkin/brave/tree/master/brave#baggage[Baggage (propagated fields)]
* https://github.com/openzipkin/brave/tree/master/instrumentation/http[HTTP tracing]
[[features-brave-sampling]]
=== Brave Sampling
Sampling only applies to tracing backends, such as Zipkin.
Trace IDs appear in logs regardless of sample rate.
Sampling is a way to prevent overloading the system, by consistently tracing some, but not all requests.
The default rate of 10 traces per second is controlled by the `spring.sleuth.sampler.rate`
property and applies when we know Sleuth is used for reasons besides logging.
Use a rate above 100 traces per second with extreme caution as it can overload your tracing system.
The sampler can be set by Java Config also, as shown in the following example:
[source,java,indent=0]
----
include::{brave_path}/src/test/java/org/springframework/cloud/sleuth/brave/SpringCloudSleuthDocTests.java[tags=always_sampler,indent=0]
----
TIP: You can set the HTTP header `b3` to `1`, or, when doing messaging, you can set the `spanFlags` header to `1`.
Doing so forces the current request to be sampled regardless of configuration.
By default samplers will work with the refresh scope mechanism.
That means that you can change the sampling properties at runtime, refresh the application and the changes will be reflected.
However, sometimes the fact of creating a proxy around samplers and calling it from too early (from `@PostConstruct` annotated method) may lead to dead locks.
In such a case either create a sampler bean explicitly, or set the property `spring.sleuth.sampler.refresh.enabled` to `false` to disable the refresh scope support.
[[features-brave-baggage]]
=== Brave Baggage Java configuration
If you need to do anything more advanced than above, do not define properties and instead use a
`@Bean` config for the baggage fields you use.
* `BaggagePropagationCustomizer` sets up baggage fields
* Add a `SingleBaggageField` to control header names for a `Baggage`.
* `CorrelationScopeCustomizer` sets up MDC fields
* Add a `SingleCorrelationField` to change the MDC name of a `Baggage` or if updates flush.
[[features-brave-customizations]]
=== Brave Customizations
The `brave.Tracer` object is fully managed by sleuth, so you rarely need to affect it.
That said, Sleuth supports a number of `Customizer` types, that allow you to configure anything not already done by Sleuth with auto-configuration or properties.
If you define one of the following as a `Bean`, Sleuth will invoke it to customize behaviour:
* `RpcTracingCustomizer` - for RPC tagging and sampling policy
* `HttpTracingCustomizer` - for HTTP tagging and sampling policy
* `MessagingTracingCustomizer` - for messaging tagging and sampling policy
* `CurrentTraceContextCustomizer` - to integrate decorators such as correlation.
* `BaggagePropagationCustomizer` - for propagating baggage fields in process and over headers
* `CorrelationScopeDecoratorCustomizer` - for scope decorations such as MDC (logging) field correlation
[[features-brave-sampling-customizations]]
==== Brave Sampling Customizations
If client /server sampling is required, just register a bean of type
`brave.sampler.SamplerFunction<HttpRequest>` and name the bean
`sleuthHttpClientSampler` for client sampler and `sleuthHttpServerSampler`
for server sampler.
For your convenience the `@HttpClientSampler` and `@HttpServerSampler`
annotations can be used to inject the proper beans or to reference the bean names via their static String `NAME` fields.
Check out Brave's code to see an example of how to make a path-based sampler
https://github.com/openzipkin/brave/tree/master/instrumentation/http#sampling-policy
If you want to completely rewrite the `HttpTracing` bean you can use the `SkipPatternProvider`
interface to retrieve the URL `Pattern` for spans that should be not sampled.
Below you can see an example of usage of `SkipPatternProvider` inside a server side, `Sampler<HttpRequest>`.
[source,java,indent=0]
----
@Configuration(proxyBeanMethods = false)
class Config {
include::{tests_path}/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceFilterWebIntegrationTests.java[tags=custom_server_sampler,indent=2]
}
----
[[features-brave-messaging]]
=== Brave Messaging
Sleuth automatically configures the `MessagingTracing` bean which serves as a foundation for Messaging instrumentation such as Kafka or JMS.
If a customization of producer / consumer sampling of messaging traces is required, just register a bean of type `brave.sampler.SamplerFunction<MessagingRequest>` and name the bean `sleuthProducerSampler` for producer sampler and `sleuthConsumerSampler`
for consumer sampler.
For your convenience the `@ProducerSampler` and `@ConsumerSampler`
annotations can be used to inject the proper beans or to reference the bean names via their static String `NAME` fields.
Ex.
Here's a sampler that traces 100 consumer requests per second, except for the "alerts" channel.
Other requests will use a global rate provided by the
`Tracing` component.
[source,java,indent=0]
----
@Configuration(proxyBeanMethods = false)
class Config {
include::{autoconfig_path}/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/messaging/BraveMessagingAutoConfigurationIntegrationTests.java[tags=custom_messaging_consumer_sampler,indent=2]
}
----
For more, see https://github.com/openzipkin/brave/tree/master/instrumentation/messaging#sampling-policy
[[features-brave-opentracing]]
=== Brave Opentracing
You can integrate with Brave and https://opentracing.io/[OpenTracing] via the
`io.opentracing.brave:brave-opentracing` bridge.
Just add it to the classpath and the OpenTracing `Tracer` will be set up automatically.
[[features-zipkin]]
== Sending Spans to Zipkin
Spring Cloud Sleuth provides various integrations with the https://zipkin.io[OpenZipkin] distributed tracing system.
Regardless of the chosen tracer implementation it's enough to add `spring-cloud-sleuth-zipkin` to the classpath to start sending spans to Zipkin.
You can choose whether to do that via HTTP or messaging.
You can read more about how to do that in "<<howto.adoc#how-to-set-up-sleuth-with-brave-zipkin-messaging,how to section>>".
When the span is closed, it is sent to Zipkin over HTTP. The communication is asynchronous.
You can configure the URL by setting the `spring.zipkin.baseUrl` property, as follows:
[source,yaml]
----
spring.zipkin.baseUrl: https://192.168.99.100:9411/
----
If you want to find Zipkin through service discovery, you can pass the Zipkin's service ID inside the URL, as shown in the following example for `zipkinserver` service ID:
[source,yaml]
----
spring.zipkin.baseUrl: https://zipkinserver/
----
To disable this feature just set `spring.zipkin.discovery-client-enabled` to `false`.
When the Discovery Client feature is enabled, Sleuth uses
`LoadBalancerClient` to find the URL of the Zipkin Server.
It means that you can set up the load balancing configuration.
If you have `web`, `rabbit`, `activemq` or `kafka` together on the classpath, you might need to pick the means by which you would like to send spans to zipkin.
To do so, set `web`, `rabbit`, `activemq` or `kafka` to the `spring.zipkin.sender.type` property.
The following example shows setting the sender type for `web`:
[source,yaml]
----
spring.zipkin.sender.type: web
----
To customize the `RestTemplate` that sends spans to Zipkin via HTTP, you can register the `ZipkinRestTemplateCustomizer` bean.
[source,java,indent=0]
----
@Configuration(proxyBeanMethods = false)
class MyConfig {
@Bean ZipkinRestTemplateCustomizer myCustomizer() {
return new ZipkinRestTemplateCustomizer() {
@Override
void customize(RestTemplate restTemplate) {
// customize the RestTemplate
}
};
}
}
----
If, however, you would like to control the full process of creating the `RestTemplate`
object, you will have to create a bean of `zipkin2.reporter.Sender` type.
[source,java,indent=0]
----
@Bean Sender myRestTemplateSender(ZipkinProperties zipkin,
ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer) {
RestTemplate restTemplate = mySuperCustomRestTemplate();
zipkinRestTemplateCustomizer.customize(restTemplate);
return myCustomSender(zipkin, restTemplate);
}
----
By default, api path will be set to `api/v2/spans` or `api/v1/spans` depending on the encoder version. If you want to use a custom api path, you can configure it using the following property (empty case, set ""):
[source,yaml]
----
spring.zipkin.api-path: v2/path2
----
[[features-zipkin-custom-service-name]]
=== Custom service name
By default, Sleuth assumes that, when you send a span to Zipkin, you want the span's service name to be equal to the value of the `spring.application.name` property.
That is not always the case, though.
There are situations in which you want to explicitly provide a different service name for all spans coming from your application.
To achieve that, you can pass the following property to your application to override that value (the example is for a service named `myService`):
[source,yaml]
----
spring.zipkin.service.name: myService
----
[[features-zipkin-host-locator]]
=== Host Locator
IMPORTANT: This section is about defining *host* from service discovery.
It is *NOT* about finding Zipkin through service discovery.
To define the host that corresponds to a particular span, we need to resolve the host name and port.
The default approach is to take these values from server properties.
If those are not set, we try to retrieve the host name from the network interfaces.
If you have the discovery client enabled and prefer to retrieve the host address from the registered instance in a service registry, you have to set the `spring.zipkin.locator.discovery.enabled` property (it is applicable for both HTTP-based and Stream-based span reporting), as follows:
[source,yaml]
----
spring.zipkin.locator.discovery.enabled: true
----
[[features-zipkin-custom-reported-spans]]
=== Customization of Reported Spans
In Sleuth, we generate spans with a fixed name.
Some users want to modify the name depending on values of tags.
Sleuth registers a `SpanFilter` bean that can automatically skip reporting spans of given name patterns.
The property `spring.sleuth.span-filter.span-name-patterns-to-skip` contains the default skip patterns for span names.
The property `spring.sleuth.span-filter.additional-span-name-patterns-to-skip` will append the provided span name patterns to the existing ones.
In order to disable this functionality just set `spring.sleuth.span-filter.enabled` to `false`.
[[features-zipkin-custom-reported-spans-brave]]
==== Brave Customization of Reported Spans
IMPORTANT: This section is applicable for Brave tracer only.
Before reporting spans (for example, to Zipkin) you may want to modify that span in some way.
You can do so by implementing a `SpanHandler`.
The following example shows how to register two beans that implement `SpanHandler`:
[source,java,indent=0]
----
include::{autoconfig_path}/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/SpanHandlerTests.java[tags=spanHandler,indent=0]
----
The preceding example results in changing the name of the reported span to `foo bar`, just before it gets reported (for example, to Zipkin).
=== Overriding the auto-configuration of Zipkin
Spring Cloud Sleuth supports sending traces to multiple tracing systems as of version 2.1.0. In order to get this to work, every tracing system needs to have a `Reporter<Span>` and `Sender`.
If you want to override the provided beans you need to give them a specific name.
To do this you can use respectively `ZipkinAutoConfiguration.REPORTER_BEAN_NAME` and `ZipkinAutoConfiguration.SENDER_BEAN_NAME`.
[source,java,indent=0]
----
include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/autoconfig/zipkin2/ZipkinAutoConfigurationTests.java[tags=override_default_beans,indent=0]
----
[[features-log-integration]]
== Log integration
Sleuth configures the logging context with variables including the service name (`%{spring.zipkin.service.name}` or `%{spring.application.name}` if the previous one was not set), span ID (`%{spanId}`) and the trace ID (`%{traceId}`).
These help you connect logs with distributed traces and allow you choice in what tools you use to troubleshoot your services.
Once you find any log with an error, you can look for the trace ID in the message.
Paste that into your distributed tracing system to visualize the entire trace, regardless of how many services the first request ended up hitting.
[source]
----
backend.log: 2020-04-09 17:45:40.516 ERROR [backend,5e8eeec48b08e26882aba313eb08f0a4,dcc1df555b5777b3] 97203 --- [nio-9000-exec-1] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown
frontend.log:2020-04-09 17:45:40.574 ERROR [frontend,5e8eeec48b08e26882aba313eb08f0a4,82aba313eb08f0a4] 97192 --- [nio-8081-exec-2] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown
----
Above, you'll notice the trace ID is `5e8eeec48b08e26882aba313eb08f0a4`, for example.
This log configuration was automatically setup by Sleuth.
You can disable it by disabling Sleuth via `spring.sleuth.enabled=false` property or putting your own `logging.pattern.level` property.
If you use a log aggregating tool (such as https://www.elastic.co/products/kibana[Kibana], https://www.splunk.com/[Splunk], and others), you can order the events that took place.
An example from Kibana would resemble the following image:
image::{github-raw}/docs/src/main/asciidoc/images/kibana.png[Log correlation with Kibana]
If you want to use https://www.elastic.co/guide/en/logstash/current/index.html[Logstash], the following listing shows the Grok pattern for Logstash:
[source]
----
filter {
# pattern matching logback pattern
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
}
date {
match => ["timestamp", "ISO8601"]
}
mutate {
remove_field => ["timestamp"]
}
}
----
NOTE: If you want to use Grok together with the logs from Cloud Foundry, you have to use the following pattern:
[source]
----
filter {
# pattern matching logback pattern
grok {
match => { "message" => "(?m)OUT\s+%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
}
date {
match => ["timestamp", "ISO8601"]
}
mutate {
remove_field => ["timestamp"]
}
}
----
[[features-log-integration-json-logback]]
=== JSON Logback with Logstash
Often, you do not want to store your logs in a text file but in a JSON file that Logstash can immediately pick.
To do so, you have to do the following (for readability, we pass the dependencies in the `groupId:artifactId:version` notation).
*Dependencies Setup*
. Ensure that Logback is on the classpath (`ch.qos.logback:logback-core`).
. Add Logstash Logback encode.
For example, to use version `4.6`, add `net.logstash.logback:logstash-logback-encoder:4.6`.
*Logback Setup*
Consider the following example of a Logback configuration file (logback-spring.xml).
[source,xml]
-----
include::{project-root}/docs/src/main/asciidoc/logback-spring.xml[]
-----
That Logback configuration file:
* Logs information from the application in a JSON format to a `build/${spring.application.name}.json` file.
* Has commented out two additional appenders: console and standard log file.
* Has the same logging pattern as the one presented in the previous section.
NOTE: If you use a custom `logback-spring.xml`, you must pass the `spring.application.name` in the `bootstrap` rather than the `application` property file.
Otherwise, your custom logback file does not properly read the property.
[[features-whats-next]]
== What to Read Next
If you want to learn more about any of the classes discussed in this section, you can browse the
{github-code}[source code directly].
If you have specific questions, see the
<<howto.adoc#howto, how-to>> section.
If you are comfortable with {project-full-name}'s core features, you can continue on and read about
<<integrations.adoc, {project-full-name}'s integrations>>.
[[features]]
[[project-features]]
= Spring Cloud Sleuth Features
include::_attributes.adoc[]
This section dives into the details of {project-full-name}.
Here you can learn about the key features that you may want to use and customize.
If you have not already done so, you might want to read the "<<getting-started.adoc#getting-started>>" and "<<using.adoc#using>>" sections, so that you have a good grounding in the basics.
[[features-context-propagation]]
== Context Propagation
Traces connect from service to service using header propagation.
The default format is https://github.com/openzipkin/b3-propagation[B3].
Similar to data formats, you can configure alternate header formats also, provided trace and span IDs are compatible with B3. Most notably, this means the trace ID and span IDs are lower-case hex, not UUIDs.
Besides trace identifiers, other properties (Baggage) can also be passed along with the request.
Remote Baggage must be predefined, but is flexible otherwise.
To use the provided defaults you can set the `spring.sleuth.propagation.type` property.
The value can be a list in which case you will propagate more tracing headers.
For Brave we support `AWS`, `B3`, `W3C` propagation types.
You can read more about how to provide custom context propagation in this "<<howto.adoc#how-to-change-context-propagation,how to section>>".
[[features-sampling]]
== Sampling
Spring Cloud Sleuth pushes the sampling decision down to the tracer implementation.
However, there are cases where you can change the sampling decision at runtime.
One of such cases is skip reporting of certain client spans.
To achieve that you can set the `spring.sleuth.web.client.skip-pattern` with the path patterns to be skipped.
Another option is to provide your own custom `org.springframework.cloud.sleuth.SamplerFunction<`org.springframework.cloud.sleuth.http.HttpRequest>` implementation and define when a given `HttpRequest` should not be sampled.
[[features-baggage]]
== Baggage
Distributed tracing works by propagating fields inside and across services that connect the trace together: traceId and spanId notably.
The context that holds these fields can optionally push other fields that need to be consistent regardless of many services are touched.
The simple name for these extra fields is "Baggage".
Sleuth allows you to define which baggage are permitted to exist in the trace context, including what header names are used.
The following example shows setting baggage values using Spring Cloud Sleuth's API:
[source,java,indent=0]
----
include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/baggage/multiple/MultipleHopsIntegrationTests.java[tags=baggage,indent=0]
----
IMPORTANT: There is currently no limitation of the count or size of baggage items.
Keep in mind that too many can decrease system throughput or increase RPC latency.
In extreme cases, too much baggage can crash the application, due to exceeding transport-level message or header capacity.
You can use properties to define fields that have no special configuration such as name mapping:
* `spring.sleuth.baggage.remote-fields` is a list of header names to accept and propagate to remote services.
* `spring.sleuth.baggage.local-fields` is a list of names to propagate locally
No prefixing applies with these keys.
What you set is literally what is used.
A name set in either of these properties will result in a `Baggage` of the same name.
In order to automatically set the baggage values to Slf4j's MDC, you have to set the `spring.sleuth.baggage.correlation-fields` property with a list of allowed local or remote keys. E.g. `spring.sleuth.baggage.correlation-fields=country-code` will set the value of the `country-code` baggage into MDC.
Note that the extra field is propagated and added to MDC starting with the next downstream trace context.
To immediately add the extra field to MDC in the current trace context, configure the field to flush on update:
```
// configuration
@Bean
BaggageField countryCodeField() {
return BaggageField.create("country-code");
}
@Bean
ScopeDecorator mdcScopeDecorator() {
return MDCScopeDecorator.newBuilder()
.clear()
.add(SingleCorrelationField.newBuilder(countryCodeField())
.flushOnUpdate()
.build())
.build();
}
// service
@Autowired
BaggageField countryCodeField;
countryCodeField.updateValue("new-value");
```
IMPORTANT: Remember that adding entries to MDC can drastically decrease the performance of your application!
If you want to add the baggage entries as tags, to make it possible to search for spans via the baggage entries, you can set the value of
`spring.sleuth.baggage.tag-fields` with a list of allowed baggage keys.
To disable the feature you have to pass the `spring.sleuth.propagation.tag.enabled=false` property.
[[features-baggage-vs-tags]]
=== Baggage versus Tags
Like trace IDs, Baggage is attached to messages or requests, usually as headers.
Tags are key value pairs sent in a Span to Zipkin.
Baggage values are not added spans by default, which means you can't search based on Baggage unless you opt-in.
To make baggage also tags, use the property `spring.sleuth.baggage.tag-fields`
like so:
[source,yml]
----
include::{brave_path}/src/test/resources/application-baggage.yml[indent=0]
----
[[features-brave]]
== OpenZipkin Brave Tracer Integration
Spring Cloud Sleuth integrates with the OpenZipkin Brave tracer via the bridge that is available in the `spring-cloud-sleuth-brave` module.
In this section you can read about specific Brave integrations.
You can choose to use either Sleuth's API or the Brave API directly in your code (e.g. either Sleuth's `Tracer` or Brave's `Tracer`).
If you want to use this tracer implementation's API directly please read https://github.com/openzipkin/brave[their documentation to learn more about it].
[[features-brave-basics]]
=== Brave Basics
Here are the most core types you might use:
* `brave.SpanCustomizer` - to change the span currently in progress
* `brave.Tracer` - to get a start new spans ad-hoc
Here are the most relevant links from the OpenZipkin Brave project:
* https://github.com/openzipkin/brave/tree/master/brave[Brave's core library]
* https://github.com/openzipkin/brave/tree/master/brave#baggage[Baggage (propagated fields)]
* https://github.com/openzipkin/brave/tree/master/instrumentation/http[HTTP tracing]
[[features-brave-sampling]]
=== Brave Sampling
Sampling only applies to tracing backends, such as Zipkin.
Trace IDs appear in logs regardless of sample rate.
Sampling is a way to prevent overloading the system, by consistently tracing some, but not all requests.
The default rate of 10 traces per second is controlled by the `spring.sleuth.sampler.rate`
property and applies when we know Sleuth is used for reasons besides logging.
Use a rate above 100 traces per second with extreme caution as it can overload your tracing system.
The sampler can be set by Java Config also, as shown in the following example:
[source,java,indent=0]
----
include::{brave_path}/src/test/java/org/springframework/cloud/sleuth/brave/SpringCloudSleuthDocTests.java[tags=always_sampler,indent=0]
----
TIP: You can set the HTTP header `b3` to `1`, or, when doing messaging, you can set the `spanFlags` header to `1`.
Doing so forces the current request to be sampled regardless of configuration.
By default samplers will work with the refresh scope mechanism.
That means that you can change the sampling properties at runtime, refresh the application and the changes will be reflected.
However, sometimes the fact of creating a proxy around samplers and calling it from too early (from `@PostConstruct` annotated method) may lead to dead locks.
In such a case either create a sampler bean explicitly, or set the property `spring.sleuth.sampler.refresh.enabled` to `false` to disable the refresh scope support.
[[features-brave-baggage]]
=== Brave Baggage Java configuration
If you need to do anything more advanced than above, do not define properties and instead use a
`@Bean` config for the baggage fields you use.
* `BaggagePropagationCustomizer` sets up baggage fields
* Add a `SingleBaggageField` to control header names for a `Baggage`.
* `CorrelationScopeCustomizer` sets up MDC fields
* Add a `SingleCorrelationField` to change the MDC name of a `Baggage` or if updates flush.
[[features-brave-customizations]]
=== Brave Customizations
The `brave.Tracer` object is fully managed by sleuth, so you rarely need to affect it.
That said, Sleuth supports a number of `Customizer` types, that allow you to configure anything not already done by Sleuth with auto-configuration or properties.
If you define one of the following as a `Bean`, Sleuth will invoke it to customize behaviour:
* `RpcTracingCustomizer` - for RPC tagging and sampling policy
* `HttpTracingCustomizer` - for HTTP tagging and sampling policy
* `MessagingTracingCustomizer` - for messaging tagging and sampling policy
* `CurrentTraceContextCustomizer` - to integrate decorators such as correlation.
* `BaggagePropagationCustomizer` - for propagating baggage fields in process and over headers
* `CorrelationScopeDecoratorCustomizer` - for scope decorations such as MDC (logging) field correlation
[[features-brave-sampling-customizations]]
==== Brave Sampling Customizations
If client /server sampling is required, just register a bean of type
`brave.sampler.SamplerFunction<HttpRequest>` and name the bean
`sleuthHttpClientSampler` for client sampler and `sleuthHttpServerSampler`
for server sampler.
For your convenience the `@HttpClientSampler` and `@HttpServerSampler`
annotations can be used to inject the proper beans or to reference the bean names via their static String `NAME` fields.
Check out Brave's code to see an example of how to make a path-based sampler
https://github.com/openzipkin/brave/tree/master/instrumentation/http#sampling-policy
If you want to completely rewrite the `HttpTracing` bean you can use the `SkipPatternProvider`
interface to retrieve the URL `Pattern` for spans that should be not sampled.
Below you can see an example of usage of `SkipPatternProvider` inside a server side, `Sampler<HttpRequest>`.
[source,java,indent=0]
----
@Configuration(proxyBeanMethods = false)
class Config {
include::{tests_path}/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceFilterWebIntegrationTests.java[tags=custom_server_sampler,indent=2]
}
----
[[features-brave-messaging]]
=== Brave Messaging
Sleuth automatically configures the `MessagingTracing` bean which serves as a foundation for Messaging instrumentation such as Kafka or JMS.
If a customization of producer / consumer sampling of messaging traces is required, just register a bean of type `brave.sampler.SamplerFunction<MessagingRequest>` and name the bean `sleuthProducerSampler` for producer sampler and `sleuthConsumerSampler`
for consumer sampler.
For your convenience the `@ProducerSampler` and `@ConsumerSampler`
annotations can be used to inject the proper beans or to reference the bean names via their static String `NAME` fields.
Ex.
Here's a sampler that traces 100 consumer requests per second, except for the "alerts" channel.
Other requests will use a global rate provided by the
`Tracing` component.
[source,java,indent=0]
----
@Configuration(proxyBeanMethods = false)
class Config {
include::{autoconfig_path}/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/messaging/BraveMessagingAutoConfigurationIntegrationTests.java[tags=custom_messaging_consumer_sampler,indent=2]
}
----
For more, see https://github.com/openzipkin/brave/tree/master/instrumentation/messaging#sampling-policy
[[features-brave-opentracing]]
=== Brave Opentracing
You can integrate with Brave and https://opentracing.io/[OpenTracing] via the
`io.opentracing.brave:brave-opentracing` bridge.
Just add it to the classpath and the OpenTracing `Tracer` will be set up automatically.
[[features-zipkin]]
== Sending Spans to Zipkin
Spring Cloud Sleuth provides various integrations with the https://zipkin.io[OpenZipkin] distributed tracing system.
Regardless of the chosen tracer implementation it's enough to add `spring-cloud-sleuth-zipkin` to the classpath to start sending spans to Zipkin.
You can choose whether to do that via HTTP or messaging.
You can read more about how to do that in "<<howto.adoc#how-to-set-up-sleuth-with-brave-zipkin-messaging,how to section>>".
When the span is closed, it is sent to Zipkin over HTTP. The communication is asynchronous.
You can configure the URL by setting the `spring.zipkin.baseUrl` property, as follows:
[source,yaml]
----
spring.zipkin.baseUrl: https://192.168.99.100:9411/
----
If you want to find Zipkin through service discovery, you can pass the Zipkin's service ID inside the URL, as shown in the following example for `zipkinserver` service ID:
[source,yaml]
----
spring.zipkin.baseUrl: https://zipkinserver/
----
To disable this feature just set `spring.zipkin.discovery-client-enabled` to `false`.
When the Discovery Client feature is enabled, Sleuth uses
`LoadBalancerClient` to find the URL of the Zipkin Server.
It means that you can set up the load balancing configuration.
If you have `web`, `rabbit`, `activemq` or `kafka` together on the classpath, you might need to pick the means by which you would like to send spans to zipkin.
To do so, set `web`, `rabbit`, `activemq` or `kafka` to the `spring.zipkin.sender.type` property.
The following example shows setting the sender type for `web`:
[source,yaml]
----
spring.zipkin.sender.type: web
----
To customize the `RestTemplate` that sends spans to Zipkin via HTTP, you can register the `ZipkinRestTemplateCustomizer` bean.
[source,java,indent=0]
----
@Configuration(proxyBeanMethods = false)
class MyConfig {
@Bean ZipkinRestTemplateCustomizer myCustomizer() {
return new ZipkinRestTemplateCustomizer() {
@Override
void customize(RestTemplate restTemplate) {
// customize the RestTemplate
}
};
}
}
----
If, however, you would like to control the full process of creating the `RestTemplate`
object, you will have to create a bean of `ZipkinRestTemplateProvider` type.
[source,java,indent=0]
----
@Bean
ZipkinRestTemplateProvider myZipkinRestTemplateProvider() {
return MyRestTemplate::new;
}
----
By default, api path will be set to `api/v2/spans` or `api/v1/spans` depending on the encoder version. If you want to use a custom api path, you can configure it using the following property (empty case, set ""):
[source,yaml]
----
spring.zipkin.api-path: v2/path2
----
[[features-zipkin-custom-service-name]]
=== Custom service name
By default, Sleuth assumes that, when you send a span to Zipkin, you want the span's service name to be equal to the value of the `spring.application.name` property.
That is not always the case, though.
There are situations in which you want to explicitly provide a different service name for all spans coming from your application.
To achieve that, you can pass the following property to your application to override that value (the example is for a service named `myService`):
[source,yaml]
----
spring.zipkin.service.name: myService
----
[[features-zipkin-host-locator]]
=== Host Locator
IMPORTANT: This section is about defining *host* from service discovery.
It is *NOT* about finding Zipkin through service discovery.
To define the host that corresponds to a particular span, we need to resolve the host name and port.
The default approach is to take these values from server properties.
If those are not set, we try to retrieve the host name from the network interfaces.
If you have the discovery client enabled and prefer to retrieve the host address from the registered instance in a service registry, you have to set the `spring.zipkin.locator.discovery.enabled` property (it is applicable for both HTTP-based and Stream-based span reporting), as follows:
[source,yaml]
----
spring.zipkin.locator.discovery.enabled: true
----
[[features-zipkin-custom-reported-spans]]
=== Customization of Reported Spans
In Sleuth, we generate spans with a fixed name.
Some users want to modify the name depending on values of tags.
Sleuth registers a `SpanFilter` bean that can automatically skip reporting spans of given name patterns.
The property `spring.sleuth.span-filter.span-name-patterns-to-skip` contains the default skip patterns for span names.
The property `spring.sleuth.span-filter.additional-span-name-patterns-to-skip` will append the provided span name patterns to the existing ones.
In order to disable this functionality just set `spring.sleuth.span-filter.enabled` to `false`.
[[features-zipkin-custom-reported-spans-brave]]
==== Brave Customization of Reported Spans
IMPORTANT: This section is applicable for Brave tracer only.
Before reporting spans (for example, to Zipkin) you may want to modify that span in some way.
You can do so by implementing a `SpanHandler`.
The following example shows how to register two beans that implement `SpanHandler`:
[source,java,indent=0]
----
include::{autoconfig_path}/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/SpanHandlerTests.java[tags=spanHandler,indent=0]
----
The preceding example results in changing the name of the reported span to `foo bar`, just before it gets reported (for example, to Zipkin).
=== Overriding the auto-configuration of Zipkin
Spring Cloud Sleuth supports sending traces to multiple tracing systems as of version 2.1.0. In order to get this to work, every tracing system needs to have a `Reporter<Span>` and `Sender`.
If you want to override the provided beans you need to give them a specific name.
To do this you can use respectively `ZipkinAutoConfiguration.REPORTER_BEAN_NAME` and `ZipkinAutoConfiguration.SENDER_BEAN_NAME`.
[source,java,indent=0]
----
include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/autoconfig/zipkin2/ZipkinAutoConfigurationTests.java[tags=override_default_beans,indent=0]
----
[[features-log-integration]]
== Log integration
Sleuth configures the logging context with variables including the service name (`%{spring.zipkin.service.name}` or `%{spring.application.name}` if the previous one was not set), span ID (`%{spanId}`) and the trace ID (`%{traceId}`).
These help you connect logs with distributed traces and allow you choice in what tools you use to troubleshoot your services.
Once you find any log with an error, you can look for the trace ID in the message.
Paste that into your distributed tracing system to visualize the entire trace, regardless of how many services the first request ended up hitting.
[source]
----
backend.log: 2020-04-09 17:45:40.516 ERROR [backend,5e8eeec48b08e26882aba313eb08f0a4,dcc1df555b5777b3] 97203 --- [nio-9000-exec-1] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown
frontend.log:2020-04-09 17:45:40.574 ERROR [frontend,5e8eeec48b08e26882aba313eb08f0a4,82aba313eb08f0a4] 97192 --- [nio-8081-exec-2] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown
----
Above, you'll notice the trace ID is `5e8eeec48b08e26882aba313eb08f0a4`, for example.
This log configuration was automatically setup by Sleuth.
You can disable it by disabling Sleuth via `spring.sleuth.enabled=false` property or putting your own `logging.pattern.level` property.
If you use a log aggregating tool (such as https://www.elastic.co/products/kibana[Kibana], https://www.splunk.com/[Splunk], and others), you can order the events that took place.
An example from Kibana would resemble the following image:
image::{github-raw}/docs/src/main/asciidoc/images/kibana.png[Log correlation with Kibana]
If you want to use https://www.elastic.co/guide/en/logstash/current/index.html[Logstash], the following listing shows the Grok pattern for Logstash:
[source]
----
filter {
# pattern matching logback pattern
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
}
date {
match => ["timestamp", "ISO8601"]
}
mutate {
remove_field => ["timestamp"]
}
}
----
NOTE: If you want to use Grok together with the logs from Cloud Foundry, you have to use the following pattern:
[source]
----
filter {
# pattern matching logback pattern
grok {
match => { "message" => "(?m)OUT\s+%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
}
date {
match => ["timestamp", "ISO8601"]
}
mutate {
remove_field => ["timestamp"]
}
}
----
[[features-log-integration-json-logback]]
=== JSON Logback with Logstash
Often, you do not want to store your logs in a text file but in a JSON file that Logstash can immediately pick.
To do so, you have to do the following (for readability, we pass the dependencies in the `groupId:artifactId:version` notation).
*Dependencies Setup*
. Ensure that Logback is on the classpath (`ch.qos.logback:logback-core`).
. Add Logstash Logback encode.
For example, to use version `4.6`, add `net.logstash.logback:logstash-logback-encoder:4.6`.
*Logback Setup*
Consider the following example of a Logback configuration file (logback-spring.xml).
[source,xml]
-----
include::{project-root}/docs/src/main/asciidoc/logback-spring.xml[]
-----
That Logback configuration file:
* Logs information from the application in a JSON format to a `build/${spring.application.name}.json` file.
* Has commented out two additional appenders: console and standard log file.
* Has the same logging pattern as the one presented in the previous section.
NOTE: If you use a custom `logback-spring.xml`, you must pass the `spring.application.name` in the `bootstrap` rather than the `application` property file.
Otherwise, your custom logback file does not properly read the property.
[[features-whats-next]]
== What to Read Next
If you want to learn more about any of the classes discussed in this section, you can browse the
{github-code}[source code directly].
If you have specific questions, see the
<<howto.adoc#howto, how-to>> section.
If you are comfortable with {project-full-name}'s core features, you can continue on and read about
<<integrations.adoc, {project-full-name}'s integrations>>.

View File

@@ -1,54 +1,54 @@
/*
* Copyright 2013-2021 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
*
* https://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.instrument.batch;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.beans.factory.BeanFactory;
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.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that registers instrumentation for Spring Batch.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(JobBuilderFactory.class)
@ConditionalOnBean(Tracer.class)
@ConditionalOnProperty(value = "spring.sleuth.batch.enabled", matchIfMissing = true)
@AutoConfigureAfter(BraveAutoConfiguration.class)
public class TraceBatchAutoConfiguration {
@Bean
static TraceJobBuilderFactoryBeanPostProcessor traceJobBuilderFactoryBeanPostProcessor(BeanFactory beanFactory) {
return new TraceJobBuilderFactoryBeanPostProcessor(beanFactory);
}
@Bean
static TraceStepBuilderFactoryBeanPostProcessor traceStepBuilderFactoryBeanPostProcessor(BeanFactory beanFactory) {
return new TraceStepBuilderFactoryBeanPostProcessor(beanFactory);
}
}
/*
* Copyright 2013-2021 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
*
* https://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.instrument.batch;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.beans.factory.BeanFactory;
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.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that registers instrumentation for Spring Batch.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(JobBuilderFactory.class)
@ConditionalOnBean(Tracer.class)
@ConditionalOnProperty(value = "spring.sleuth.batch.enabled", matchIfMissing = true)
@AutoConfigureAfter(BraveAutoConfiguration.class)
public class TraceBatchAutoConfiguration {
@Bean
static TraceJobBuilderFactoryBeanPostProcessor traceJobBuilderFactoryBeanPostProcessor(BeanFactory beanFactory) {
return new TraceJobBuilderFactoryBeanPostProcessor(beanFactory);
}
@Bean
static TraceStepBuilderFactoryBeanPostProcessor traceStepBuilderFactoryBeanPostProcessor(BeanFactory beanFactory) {
return new TraceStepBuilderFactoryBeanPostProcessor(beanFactory);
}
}

View File

@@ -16,8 +16,6 @@
package org.springframework.cloud.sleuth.autoconfig.zipkin2;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import zipkin2.reporter.Sender;
import org.springframework.beans.factory.annotation.Autowired;
@@ -34,6 +32,7 @@ import org.springframework.cloud.sleuth.zipkin2.StaticInstanceZipkinLoadBalancer
import org.springframework.cloud.sleuth.zipkin2.ZipkinLoadBalancer;
import org.springframework.cloud.sleuth.zipkin2.ZipkinProperties;
import org.springframework.cloud.sleuth.zipkin2.ZipkinRestTemplateCustomizer;
import org.springframework.cloud.sleuth.zipkin2.ZipkinRestTemplateProvider;
import org.springframework.cloud.sleuth.zipkin2.ZipkinRestTemplateWrapper;
import org.springframework.cloud.sleuth.zipkin2.ZipkinUrlExtractor;
import org.springframework.context.annotation.Bean;
@@ -47,16 +46,20 @@ import org.springframework.web.client.RestTemplate;
@EnableConfigurationProperties(ZipkinSenderProperties.class)
class ZipkinRestTemplateSenderConfiguration {
private static final Log log = LogFactory.getLog(ZipkinRestTemplateSenderConfiguration.class);
@Bean(ZipkinAutoConfiguration.SENDER_BEAN_NAME)
Sender restTemplateSender(ZipkinProperties zipkin, ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer,
ZipkinUrlExtractor extractor) {
RestTemplate restTemplate = new ZipkinRestTemplateWrapper(zipkin, extractor);
ZipkinRestTemplateProvider zipkinRestTemplateProvider) {
RestTemplate restTemplate = zipkinRestTemplateProvider.zipkinRestTemplate();
restTemplate = zipkinRestTemplateCustomizer.customizeTemplate(restTemplate);
return new RestTemplateSender(restTemplate, zipkin.getBaseUrl(), zipkin.getApiPath(), zipkin.getEncoder());
}
@Bean
@ConditionalOnMissingBean
ZipkinRestTemplateProvider zipkinRestTemplateProvider(ZipkinProperties zipkin, ZipkinUrlExtractor extractor) {
return () -> new ZipkinRestTemplateWrapper(zipkin, extractor);
}
@Bean
ZipkinUrlExtractor defaultZipkinUrlExtractor(final ZipkinLoadBalancer zipkinLoadBalancer) {
return new CachingZipkinUrlExtractor(zipkinLoadBalancer);

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2013-2021 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
*
* https://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.zipkin2;
import org.assertj.core.api.BDDAssertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.zipkin2.ZipkinProperties;
import org.springframework.cloud.sleuth.zipkin2.ZipkinRestTemplateCustomizer;
import org.springframework.cloud.sleuth.zipkin2.ZipkinRestTemplateProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
class ZipkinRestTemplateSenderConfigurationTests {
@Test
void should_override_the_default_rest_template() {
ApplicationContextRunner runner = new ApplicationContextRunner().withUserConfiguration(Config.class,
ZipkinRestTemplateSenderConfiguration.class, ZipkinProperties.class);
runner.run(context -> {
Config config = context.getBean(Config.class);
assertThat(config.customizerCalled).isTrue();
});
}
@Configuration(proxyBeanMethods = false)
static class Config {
boolean customizerCalled;
@Bean
ZipkinRestTemplateProvider myZipkinRestTemplateProvider() {
return MyRestTemplate::new;
}
@Bean
ZipkinRestTemplateCustomizer myZipkinRestTemplateCustomizer() {
return new ZipkinRestTemplateCustomizer() {
@Override
public RestTemplate customizeTemplate(RestTemplate restTemplate) {
customizerCalled = true;
BDDAssertions.then(restTemplate).isInstanceOf(MyRestTemplate.class);
return restTemplate;
}
};
}
}
static class MyRestTemplate extends RestTemplate {
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2013-2021 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
*
* https://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.zipkin2;
import org.springframework.web.client.RestTemplate;
/**
* A provider for a {@link RestTemplate} used to send spans to Zipkin.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public interface ZipkinRestTemplateProvider {
RestTemplate zipkinRestTemplate();
}