This commit is contained in:
Marcin Grzejszczak
2021-05-26 18:36:50 +02:00
parent 12737adb67
commit 682398daec
21 changed files with 2592 additions and 2636 deletions

View File

@@ -1,112 +1,112 @@
|===
|Name | Default | Description
|spring.sleuth.async.configurer.enabled | `true` | Enable default AsyncConfigurer.
|spring.sleuth.async.enabled | `true` | Enable instrumenting async related components so that the tracing information is passed between threads.
|spring.sleuth.async.ignored-beans | | List of {@link java.util.concurrent.Executor} bean names that should be ignored and not wrapped in a trace representation.
|spring.sleuth.baggage.correlation-enabled | `true` | Enables correlating the baggage context with logging contexts.
|spring.sleuth.baggage.correlation-fields | | List of fields that should be propagated over the wire.
|spring.sleuth.baggage.local-fields | | List of fields that should be accessible within the JVM process but not propagated over the wire.
|spring.sleuth.baggage.remote-fields | | List of fields that are referenced the same in-process as it is on the wire. For example, the field "x-vcap-request-id" would be set as-is including the prefix.
|spring.sleuth.baggage.tag-fields | | List of fields that should automatically become tags.
|spring.sleuth.batch.enabled | `true` | Enable Spring Batch instrumentation.
|spring.sleuth.circuitbreaker.enabled | `true` | Enable Spring Cloud CircuitBreaker instrumentation.
|spring.sleuth.config.server.enabled | `true` | Enable Spring Cloud Config Server instrumentation.
|spring.sleuth.deployer.enabled | `true` | Enable Spring Cloud Deployer instrumentation.
|spring.sleuth.deployer.status-poll-delay | `500` | Default poll delay to retrieve the deployed application status.
|spring.sleuth.enabled | `true` |
|spring.sleuth.feign.enabled | `true` | Enable span information propagation when using Feign.
|spring.sleuth.feign.processor.enabled | `true` | Enable post processor that wraps Feign Context in its tracing representations.
|spring.sleuth.function.enabled | `true` | Enable instrumenting of Spring Cloud Function and Spring Cloud Function based projects (e.g. Spring Cloud Stream).
|spring.sleuth.grpc.enabled | `true` | Enable span information propagation when using GRPC.
|spring.sleuth.http.enabled | `true` | Enables HTTP support.
|spring.sleuth.integration.enabled | `true` | Enable Spring Integration instrumentation.
|spring.sleuth.integration.patterns | `[!hystrixStreamOutput*, *, !channel*]` | An array of patterns against which channel names will be matched. @see org.springframework.integration.config.GlobalChannelInterceptor#patterns() Defaults to any channel name not matching the Hystrix Stream and functional Stream channel names.
|spring.sleuth.integration.websockets.enabled | `true` | Enable tracing for WebSockets.
|spring.sleuth.jdbc.decorator.datasource.datasource-proxy.count-query | `false` | Creates listener to count queries. @see ProxyDataSourceBuilder#countQuery() @see QueryCountHolder
|spring.sleuth.jdbc.decorator.datasource.datasource-proxy.json-format | `false` | Use json output for logging query. @see ProxyDataSourceBuilder#asJson()
|spring.sleuth.jdbc.decorator.datasource.datasource-proxy.logging | | Logging to use for logging queries.
|spring.sleuth.jdbc.decorator.datasource.datasource-proxy.multiline | `true` | Use multiline output for logging query. @see ProxyDataSourceBuilder#multiline()
|spring.sleuth.jdbc.decorator.datasource.datasource-proxy.query.enable-logging | `true` | Enable logging all queries to the log.
|spring.sleuth.jdbc.decorator.datasource.datasource-proxy.query.log-level | `DEBUG` | Severity of query logger.
|spring.sleuth.jdbc.decorator.datasource.datasource-proxy.query.logger-name | | Name of query logger.
|spring.sleuth.jdbc.decorator.datasource.datasource-proxy.slow-query.log-level | `WARN` | Severity of slow query logger.
|spring.sleuth.jdbc.decorator.datasource.datasource-proxy.slow-query.logger-name | | Name of slow query logger.
|spring.sleuth.jdbc.decorator.datasource.datasource-proxy.slow-query.threshold | `300` | Number of seconds to consider query as slow.
|spring.sleuth.jdbc.decorator.datasource.enabled | `true` | Enables data source decorating.
|spring.sleuth.jdbc.decorator.datasource.excluded-beans | | Beans that won't be decorated.
|spring.sleuth.jdbc.decorator.datasource.includes | | Which types of tracing we would like to include.
|spring.sleuth.jdbc.decorator.datasource.p6spy.custom-appender-class | | Class file to use (only with logging=custom). The class must implement {@link com.p6spy.engine.spy.appender.FormattedLogger}.
|spring.sleuth.jdbc.decorator.datasource.p6spy.enable-logging | `true` | Enables logging JDBC events.
|spring.sleuth.jdbc.decorator.datasource.p6spy.log-file | `spy.log` | Name of log file to use (only with logging=file).
|spring.sleuth.jdbc.decorator.datasource.p6spy.log-filter.pattern | | Use regex pattern to filter log messages. Only matched messages will be logged.
|spring.sleuth.jdbc.decorator.datasource.p6spy.log-format | | Custom log format.
|spring.sleuth.jdbc.decorator.datasource.p6spy.logging | | Logging to use for logging queries.
|spring.sleuth.jdbc.decorator.datasource.p6spy.multiline | `true` | Enables multiline output.
|spring.sleuth.jdbc.decorator.datasource.p6spy.tracing.include-parameter-values | `true` | Report the effective sql string (with '?' replaced with real values) to tracing systems. <p> NOTE this setting does not affect the logging message.
|spring.sleuth.kafka.enabled | `true` | Enable instrumenting of Apache Kafka clients.
|spring.sleuth.messaging.aspect.enabled | `false` | Should {@link MessageMapping} wrapping be enabled.
|spring.sleuth.messaging.enabled | `false` | Should messaging be turned on.
|spring.sleuth.messaging.jms.enabled | `true` | Enable tracing of JMS.
|spring.sleuth.messaging.jms.remote-service-name | `jms` | JMS remote service name.
|spring.sleuth.messaging.kafka.enabled | `true` | Enable tracing of Kafka.
|spring.sleuth.messaging.kafka.mapper.enabled | `true` | Enable DefaultKafkaHeaderMapper tracing for Kafka.
|spring.sleuth.messaging.kafka.remote-service-name | `kafka` | Kafka remote service name.
|spring.sleuth.messaging.kafka.streams.enabled | `false` | Should Kafka Streams be turned on.
|spring.sleuth.messaging.rabbit.enabled | `true` | Enable tracing of RabbitMQ.
|spring.sleuth.messaging.rabbit.remote-service-name | `rabbitmq` | Rabbit remote service name.
|spring.sleuth.mongodb.enabled | `true` | Enable tracing for MongoDb.
|spring.sleuth.opentracing.enabled | `true` | Enables OpenTracing support.
|spring.sleuth.propagation.type | | Tracing context propagation types.
|spring.sleuth.quartz.enabled | `true` | Enable tracing for Quartz.
|spring.sleuth.r2dbc.enabled | `true` | Enable R2dbc instrumentation.
|spring.sleuth.reactor.decorate-on-each | `true` | When true decorates on each operator, will be less performing, but logging will always contain the tracing entries in each operator. When false decorates on last operator, will be more performing, but logging might not always contain the tracing entries. @deprecated use explicit value via {@link SleuthReactorProperties#instrumentationType}
|spring.sleuth.reactor.enabled | `true` | When true enables instrumentation for reactor.
|spring.sleuth.reactor.instrumentation-type | |
|spring.sleuth.redis.enabled | `true` | Enable span information propagation when using Redis.
|spring.sleuth.redis.remote-service-name | `redis` | Service name for the remote Redis endpoint.
|spring.sleuth.rpc.enabled | `true` | Enable tracing of RPC.
|spring.sleuth.rsocket.enabled | `true` | When true enables instrumentation for rsocket.
|spring.sleuth.rxjava.schedulers.hook.enabled | `true` | Enable support for RxJava via RxJavaSchedulersHook.
|spring.sleuth.rxjava.schedulers.ignoredthreads | `[HystrixMetricPoller, ^RxComputation.*$]` | Thread names for which spans will not be sampled.
|spring.sleuth.sampler.probability | | Probability of requests that should be sampled. E.g. 1.0 - 100% requests should be sampled. The precision is whole-numbers only (i.e. there's no support for 0.1% of the traces).
|spring.sleuth.sampler.rate | `10` | A rate per second can be a nice choice for low-traffic endpoints as it allows you surge protection. For example, you may never expect the endpoint to get more than 50 requests per second. If there was a sudden surge of traffic, to 5000 requests per second, you would still end up with 50 traces per second. Conversely, if you had a percentage, like 10%, the same surge would end up with 500 traces per second, possibly overloading your storage. Amazon X-Ray includes a rate-limited sampler (named Reservoir) for this purpose. Brave has taken the same approach via the {@link brave.sampler.RateLimitingSampler}.
|spring.sleuth.sampler.refresh.enabled | `true` | Enable refresh scope for sampler.
|spring.sleuth.scheduled.enabled | `true` | Enable tracing for {@link org.springframework.scheduling.annotation.Scheduled}.
|spring.sleuth.scheduled.skip-pattern | | Pattern for the fully qualified name of a class that should be skipped.
|spring.sleuth.span-filter.additional-span-name-patterns-to-ignore | | Additional list of span names to ignore. Will be appended to {@link #spanNamePatternsToSkip}.
|spring.sleuth.span-filter.enabled | `false` | Will turn on the default Sleuth handler mechanism. Might ignore exporting of certain spans;
|spring.sleuth.span-filter.span-name-patterns-to-skip | `^catalogWatchTaskScheduler$` | List of span names to ignore. They will not be sent to external systems.
|spring.sleuth.supports-join | `true` | True means the tracing system supports sharing a span ID between a client and server.
|spring.sleuth.task.enabled | `true` | Enable Spring Cloud Task instrumentation.
|spring.sleuth.trace-id128 | `false` | When true, generate 128-bit trace IDs instead of 64-bit ones.
|spring.sleuth.tracer.mode | | Set which tracer implementation should be picked.
|spring.sleuth.tx.enabled | `true` | Enable Spring TX instrumentation.
|spring.sleuth.vault.enabled | `true` | Enable Spring Vault instrumentation.
|spring.sleuth.web.additional-skip-pattern | | Additional pattern for URLs that should be skipped in tracing. This will be appended to the {@link SleuthWebProperties#skipPattern}.
|spring.sleuth.web.client.enabled | `true` | Enable interceptor injecting into {@link org.springframework.web.client.RestTemplate}.
|spring.sleuth.web.client.skip-pattern | | Pattern for URLs that should be skipped in client side tracing.
|spring.sleuth.web.enabled | `true` | When true enables instrumentation for web applications.
|spring.sleuth.web.filter-order | `0` | Order in which the tracing filters should be registered.
|spring.sleuth.web.ignore-auto-configured-skip-patterns | `false` | If set to true, auto-configured skip patterns will be ignored.
|spring.sleuth.web.servlet.enabled | `true` | Enable servlet instrumentation.
|spring.sleuth.web.skip-pattern | `/api-docs.*\|/swagger.*\|.*\.png\|.*\.css\|.*\.js\|.*\.html\|/favicon.ico\|/hystrix.stream` | Pattern for URLs that should be skipped in tracing.
|spring.sleuth.web.tomcat.enabled | `true` | Enable tracing instrumentation for Tomcat.
|spring.sleuth.web.webclient.enabled | `true` | Enable tracing instrumentation for WebClient.
|spring.zipkin.activemq.message-max-bytes | `100000` | Maximum number of bytes for a given message with spans sent to Zipkin over ActiveMQ.
|spring.zipkin.activemq.queue | `zipkin` | Name of the ActiveMQ queue where spans should be sent to Zipkin.
|spring.zipkin.api-path | | The API path to append to baseUrl (above) as suffix. This applies if you use other monitoring tools, such as New Relic. The trace API doesn't need the API path, so you can set it to blank ("") in the configuration.
|spring.zipkin.base-url | `http://localhost:9411/` | URL of the zipkin query server instance. You can also provide the service id of the Zipkin server if Zipkin's registered in service discovery (e.g. https://zipkinserver/).
|spring.zipkin.compression.enabled | `false` |
|spring.zipkin.discovery-client-enabled | | If set to {@code false}, will treat the {@link ZipkinProperties#baseUrl} as a URL always.
|spring.zipkin.enabled | `true` | Enables sending spans to Zipkin.
|spring.zipkin.encoder | | Encoding type of spans sent to Zipkin. Set to {@link SpanBytesEncoder#JSON_V1} if your server is not recent.
|spring.zipkin.kafka.topic | `zipkin` | Name of the Kafka topic where spans should be sent to Zipkin.
|spring.zipkin.locator.discovery.enabled | `false` | Enabling of locating the host name via service discovery.
|spring.zipkin.message-timeout | `1` | Timeout in seconds before pending spans will be sent in batches to Zipkin.
|spring.zipkin.rabbitmq.addresses | | Addresses of the RabbitMQ brokers used to send spans to Zipkin
|spring.zipkin.rabbitmq.queue | `zipkin` | Name of the RabbitMQ queue where spans should be sent to Zipkin.
|spring.zipkin.sender.type | | Means of sending spans to Zipkin.
|spring.zipkin.service.name | | The name of the service, from which the Span was sent via HTTP, that should appear in Zipkin.
|===
|===
|Name | Default | Description
|spring.sleuth.async.configurer.enabled | `true` | Enable default AsyncConfigurer.
|spring.sleuth.async.enabled | `true` | Enable instrumenting async related components so that the tracing information is passed between threads.
|spring.sleuth.async.ignored-beans | | List of {@link java.util.concurrent.Executor} bean names that should be ignored and not wrapped in a trace representation.
|spring.sleuth.baggage.correlation-enabled | `true` | Enables correlating the baggage context with logging contexts.
|spring.sleuth.baggage.correlation-fields | | List of fields that should be propagated over the wire.
|spring.sleuth.baggage.local-fields | | List of fields that should be accessible within the JVM process but not propagated over the wire.
|spring.sleuth.baggage.remote-fields | | List of fields that are referenced the same in-process as it is on the wire. For example, the field "x-vcap-request-id" would be set as-is including the prefix.
|spring.sleuth.baggage.tag-fields | | List of fields that should automatically become tags.
|spring.sleuth.batch.enabled | `true` | Enable Spring Batch instrumentation.
|spring.sleuth.circuitbreaker.enabled | `true` | Enable Spring Cloud CircuitBreaker instrumentation.
|spring.sleuth.config.server.enabled | `true` | Enable Spring Cloud Config Server instrumentation.
|spring.sleuth.deployer.enabled | `true` | Enable Spring Cloud Deployer instrumentation.
|spring.sleuth.deployer.status-poll-delay | `500` | Default poll delay to retrieve the deployed application status.
|spring.sleuth.enabled | `true` |
|spring.sleuth.feign.enabled | `true` | Enable span information propagation when using Feign.
|spring.sleuth.feign.processor.enabled | `true` | Enable post processor that wraps Feign Context in its tracing representations.
|spring.sleuth.function.enabled | `true` | Enable instrumenting of Spring Cloud Function and Spring Cloud Function based projects (e.g. Spring Cloud Stream).
|spring.sleuth.grpc.enabled | `true` | Enable span information propagation when using GRPC.
|spring.sleuth.http.enabled | `true` | Enables HTTP support.
|spring.sleuth.integration.enabled | `true` | Enable Spring Integration instrumentation.
|spring.sleuth.integration.patterns | `[!hystrixStreamOutput*, *, !channel*]` | An array of patterns against which channel names will be matched. @see org.springframework.integration.config.GlobalChannelInterceptor#patterns() Defaults to any channel name not matching the Hystrix Stream and functional Stream channel names.
|spring.sleuth.integration.websockets.enabled | `true` | Enable tracing for WebSockets.
|spring.sleuth.jdbc.datasource-proxy.count-query | `false` | Creates listener to count queries. @see ProxyDataSourceBuilder#countQuery() @see QueryCountHolder
|spring.sleuth.jdbc.datasource-proxy.json-format | `false` | Use json output for logging query. @see ProxyDataSourceBuilder#asJson()
|spring.sleuth.jdbc.datasource-proxy.logging | | Logging to use for logging queries.
|spring.sleuth.jdbc.datasource-proxy.multiline | `true` | Use multiline output for logging query. @see ProxyDataSourceBuilder#multiline()
|spring.sleuth.jdbc.datasource-proxy.query.enable-logging | `true` | Enable logging all queries to the log.
|spring.sleuth.jdbc.datasource-proxy.query.log-level | `DEBUG` | Severity of query logger.
|spring.sleuth.jdbc.datasource-proxy.query.logger-name | | Name of query logger.
|spring.sleuth.jdbc.datasource-proxy.slow-query.log-level | `WARN` | Severity of slow query logger.
|spring.sleuth.jdbc.datasource-proxy.slow-query.logger-name | | Name of slow query logger.
|spring.sleuth.jdbc.datasource-proxy.slow-query.threshold | `300` | Number of seconds to consider query as slow.
|spring.sleuth.jdbc.enabled | `true` | Enables data source decorating.
|spring.sleuth.jdbc.excluded-beans | | Beans that won't be decorated.
|spring.sleuth.jdbc.includes | | Which types of tracing we would like to include.
|spring.sleuth.jdbc.p6spy.custom-appender-class | | Class file to use (only with logging=custom). The class must implement {@link com.p6spy.engine.spy.appender.FormattedLogger}.
|spring.sleuth.jdbc.p6spy.enable-logging | `true` | Enables logging JDBC events.
|spring.sleuth.jdbc.p6spy.log-file | `spy.log` | Name of log file to use (only with logging=file).
|spring.sleuth.jdbc.p6spy.log-filter.pattern | | Use regex pattern to filter log messages. Only matched messages will be logged.
|spring.sleuth.jdbc.p6spy.log-format | | Custom log format.
|spring.sleuth.jdbc.p6spy.logging | | Logging to use for logging queries.
|spring.sleuth.jdbc.p6spy.multiline | `true` | Enables multiline output.
|spring.sleuth.jdbc.p6spy.tracing.include-parameter-values | `true` | Report the effective sql string (with '?' replaced with real values) to tracing systems. <p> NOTE this setting does not affect the logging message.
|spring.sleuth.kafka.enabled | `true` | Enable instrumenting of Apache Kafka clients.
|spring.sleuth.messaging.aspect.enabled | `false` | Should {@link MessageMapping} wrapping be enabled.
|spring.sleuth.messaging.enabled | `false` | Should messaging be turned on.
|spring.sleuth.messaging.jms.enabled | `true` | Enable tracing of JMS.
|spring.sleuth.messaging.jms.remote-service-name | `jms` | JMS remote service name.
|spring.sleuth.messaging.kafka.enabled | `true` | Enable tracing of Kafka.
|spring.sleuth.messaging.kafka.mapper.enabled | `true` | Enable DefaultKafkaHeaderMapper tracing for Kafka.
|spring.sleuth.messaging.kafka.remote-service-name | `kafka` | Kafka remote service name.
|spring.sleuth.messaging.kafka.streams.enabled | `false` | Should Kafka Streams be turned on.
|spring.sleuth.messaging.rabbit.enabled | `true` | Enable tracing of RabbitMQ.
|spring.sleuth.messaging.rabbit.remote-service-name | `rabbitmq` | Rabbit remote service name.
|spring.sleuth.mongodb.enabled | `true` | Enable tracing for MongoDb.
|spring.sleuth.opentracing.enabled | `true` | Enables OpenTracing support.
|spring.sleuth.propagation.type | | Tracing context propagation types.
|spring.sleuth.quartz.enabled | `true` | Enable tracing for Quartz.
|spring.sleuth.r2dbc.enabled | `true` | Enable R2dbc instrumentation.
|spring.sleuth.reactor.decorate-on-each | `true` | When true decorates on each operator, will be less performing, but logging will always contain the tracing entries in each operator. When false decorates on last operator, will be more performing, but logging might not always contain the tracing entries. @deprecated use explicit value via {@link SleuthReactorProperties#instrumentationType}
|spring.sleuth.reactor.enabled | `true` | When true enables instrumentation for reactor.
|spring.sleuth.reactor.instrumentation-type | |
|spring.sleuth.redis.enabled | `true` | Enable span information propagation when using Redis.
|spring.sleuth.redis.remote-service-name | `redis` | Service name for the remote Redis endpoint.
|spring.sleuth.rpc.enabled | `true` | Enable tracing of RPC.
|spring.sleuth.rsocket.enabled | `true` | When true enables instrumentation for rsocket.
|spring.sleuth.rxjava.schedulers.hook.enabled | `true` | Enable support for RxJava via RxJavaSchedulersHook.
|spring.sleuth.rxjava.schedulers.ignoredthreads | `[HystrixMetricPoller, ^RxComputation.*$]` | Thread names for which spans will not be sampled.
|spring.sleuth.sampler.probability | | Probability of requests that should be sampled. E.g. 1.0 - 100% requests should be sampled. The precision is whole-numbers only (i.e. there's no support for 0.1% of the traces).
|spring.sleuth.sampler.rate | `10` | A rate per second can be a nice choice for low-traffic endpoints as it allows you surge protection. For example, you may never expect the endpoint to get more than 50 requests per second. If there was a sudden surge of traffic, to 5000 requests per second, you would still end up with 50 traces per second. Conversely, if you had a percentage, like 10%, the same surge would end up with 500 traces per second, possibly overloading your storage. Amazon X-Ray includes a rate-limited sampler (named Reservoir) for this purpose. Brave has taken the same approach via the {@link brave.sampler.RateLimitingSampler}.
|spring.sleuth.sampler.refresh.enabled | `true` | Enable refresh scope for sampler.
|spring.sleuth.scheduled.enabled | `true` | Enable tracing for {@link org.springframework.scheduling.annotation.Scheduled}.
|spring.sleuth.scheduled.skip-pattern | | Pattern for the fully qualified name of a class that should be skipped.
|spring.sleuth.span-filter.additional-span-name-patterns-to-ignore | | Additional list of span names to ignore. Will be appended to {@link #spanNamePatternsToSkip}.
|spring.sleuth.span-filter.enabled | `false` | Will turn on the default Sleuth handler mechanism. Might ignore exporting of certain spans;
|spring.sleuth.span-filter.span-name-patterns-to-skip | `^catalogWatchTaskScheduler$` | List of span names to ignore. They will not be sent to external systems.
|spring.sleuth.supports-join | `true` | True means the tracing system supports sharing a span ID between a client and server.
|spring.sleuth.task.enabled | `true` | Enable Spring Cloud Task instrumentation.
|spring.sleuth.trace-id128 | `false` | When true, generate 128-bit trace IDs instead of 64-bit ones.
|spring.sleuth.tracer.mode | | Set which tracer implementation should be picked.
|spring.sleuth.tx.enabled | `true` | Enable Spring TX instrumentation.
|spring.sleuth.vault.enabled | `true` | Enable Spring Vault instrumentation.
|spring.sleuth.web.additional-skip-pattern | | Additional pattern for URLs that should be skipped in tracing. This will be appended to the {@link SleuthWebProperties#skipPattern}.
|spring.sleuth.web.client.enabled | `true` | Enable interceptor injecting into {@link org.springframework.web.client.RestTemplate}.
|spring.sleuth.web.client.skip-pattern | | Pattern for URLs that should be skipped in client side tracing.
|spring.sleuth.web.enabled | `true` | When true enables instrumentation for web applications.
|spring.sleuth.web.filter-order | `0` | Order in which the tracing filters should be registered.
|spring.sleuth.web.ignore-auto-configured-skip-patterns | `false` | If set to true, auto-configured skip patterns will be ignored.
|spring.sleuth.web.servlet.enabled | `true` | Enable servlet instrumentation.
|spring.sleuth.web.skip-pattern | `/api-docs.*\|/swagger.*\|.*\.png\|.*\.css\|.*\.js\|.*\.html\|/favicon.ico\|/hystrix.stream` | Pattern for URLs that should be skipped in tracing.
|spring.sleuth.web.tomcat.enabled | `true` | Enable tracing instrumentation for Tomcat.
|spring.sleuth.web.webclient.enabled | `true` | Enable tracing instrumentation for WebClient.
|spring.zipkin.activemq.message-max-bytes | `100000` | Maximum number of bytes for a given message with spans sent to Zipkin over ActiveMQ.
|spring.zipkin.activemq.queue | `zipkin` | Name of the ActiveMQ queue where spans should be sent to Zipkin.
|spring.zipkin.api-path | | The API path to append to baseUrl (above) as suffix. This applies if you use other monitoring tools, such as New Relic. The trace API doesn't need the API path, so you can set it to blank ("") in the configuration.
|spring.zipkin.base-url | `http://localhost:9411/` | URL of the zipkin query server instance. You can also provide the service id of the Zipkin server if Zipkin's registered in service discovery (e.g. https://zipkinserver/).
|spring.zipkin.compression.enabled | `false` |
|spring.zipkin.discovery-client-enabled | | If set to {@code false}, will treat the {@link ZipkinProperties#baseUrl} as a URL always.
|spring.zipkin.enabled | `true` | Enables sending spans to Zipkin.
|spring.zipkin.encoder | | Encoding type of spans sent to Zipkin. Set to {@link SpanBytesEncoder#JSON_V1} if your server is not recent.
|spring.zipkin.kafka.topic | `zipkin` | Name of the Kafka topic where spans should be sent to Zipkin.
|spring.zipkin.locator.discovery.enabled | `false` | Enabling of locating the host name via service discovery.
|spring.zipkin.message-timeout | `1` | Timeout in seconds before pending spans will be sent in batches to Zipkin.
|spring.zipkin.rabbitmq.addresses | | Addresses of the RabbitMQ brokers used to send spans to Zipkin
|spring.zipkin.rabbitmq.queue | `zipkin` | Name of the RabbitMQ queue where spans should be sent to Zipkin.
|spring.zipkin.sender.type | | Means of sending spans to Zipkin.
|spring.zipkin.service.name | | The name of the service, from which the Span was sent via HTTP, that should appear in Zipkin.
|===

View File

@@ -695,8 +695,8 @@ runtimeOnly "net.ttddyy:datasource-proxy:${datasourceProxyVersion}"
----
====
Please check the <<appendix.adoc#appendix,appendix>> page under `spring.sleuth.jdbc.decorator.datasource.p6spy` for all p6spy configuration options and `spring.sleuth.jdbc.decorator.datasource.datasource-proxy` for all datasource proxy configuration options.
Please check the <<appendix.adoc#appendix,appendix>> page under `spring.sleuth.jdbc.p6spy` for all p6spy configuration options and `spring.sleuth.jdbc.datasource-proxy` for all datasource proxy configuration options.
You can configure P6Spy manually using one of available configuration methods. For more information please refer to the http://p6spy.readthedocs.io/en/latest/configandusage.html[P6Spy Configuration Guide].
In order to disable this instrumentation set `spring.sleuth.jdbc.decorator.datasource.enabled` to `false`.
In order to disable this instrumentation set `spring.sleuth.jdbc.enabled` to `false`.

View File

@@ -1,151 +1,153 @@
/*
* 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.async;
import java.util.concurrent.Executor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.interceptor.AsyncExecutionAspectSupport;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
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.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.sleuth.SpanNamer;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.async.LazyTraceExecutor;
import org.springframework.cloud.sleuth.instrument.async.TraceAsyncAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} enabling async related processing.
*
* @author Dave Syer
* @author Marcin Grzejszczak
* @since 1.0.0
* @see LazyTraceExecutor
* @see TraceAsyncAspect
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SleuthAsyncProperties.class)
@ConditionalOnProperty(value = "spring.sleuth.async.enabled", matchIfMissing = true)
@ConditionalOnBean(Tracer.class)
@AutoConfigureAfter(BraveAutoConfiguration.class)
public class TraceAsyncDefaultAutoConfiguration {
@Bean
@ConditionalOnProperty(value = "spring.sleuth.scheduled.enabled", matchIfMissing = true)
static ExecutorBeanPostProcessor executorBeanPostProcessor(BeanFactory beanFactory) {
return new ExecutorBeanPostProcessor(beanFactory);
}
@Bean
TraceAsyncAspect traceAsyncAspect(Tracer tracer, SpanNamer spanNamer) {
return new TraceAsyncAspect(tracer, spanNamer);
}
/**
* Wrapper for the async executor.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(AsyncConfigurer.class)
@ConditionalOnMissingBean(AsyncConfigurer.class)
@ConditionalOnProperty(value = "spring.sleuth.async.configurer.enabled", matchIfMissing = true)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static class DefaultAsyncConfigurerSupport extends AsyncConfigurerSupport {
private static final Log log = LogFactory.getLog(DefaultAsyncConfigurerSupport.class);
@Autowired
private BeanFactory beanFactory;
@Override
public Executor getAsyncExecutor() {
Executor delegate = getDefaultExecutor();
return new LazyTraceExecutor(this.beanFactory, delegate);
}
/**
* Retrieve or build a default executor for this advice instance. An executor
* returned from here will be cached for further use.
* <p>
* The default implementation searches for a unique {@link TaskExecutor} bean in
* the context, or for an {@link Executor} bean named "taskExecutor" otherwise. If
* neither of the two is resolvable, this implementation will return {@code null}.
* @return the default executor, or {@code null} if none available
* @see AsyncExecutionAspectSupport#getDefaultExecutor(org.springframework.beans.factory.BeanFactory)
*/
private Executor getDefaultExecutor() {
try {
// Search for TaskExecutor bean... not plain Executor since that would
// match with ScheduledExecutorService as well, which is unusable for
// our purposes here. TaskExecutor is more clearly designed for it.
return this.beanFactory.getBean(TaskExecutor.class);
}
catch (NoUniqueBeanDefinitionException ex) {
log.debug("Could not find unique TaskExecutor bean", ex);
try {
return this.beanFactory.getBean(AsyncExecutionAspectSupport.DEFAULT_TASK_EXECUTOR_BEAN_NAME,
Executor.class);
}
catch (NoSuchBeanDefinitionException ex2) {
if (log.isInfoEnabled()) {
log.info("More than one TaskExecutor bean found within the context, and none is named "
+ "'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly "
+ "as an alias) in order to use it for async processing: " + ex.getBeanNamesFound());
}
}
}
catch (NoSuchBeanDefinitionException ex) {
log.debug("Could not find default TaskExecutor bean", ex);
try {
return this.beanFactory.getBean(AsyncExecutionAspectSupport.DEFAULT_TASK_EXECUTOR_BEAN_NAME,
Executor.class);
}
catch (NoSuchBeanDefinitionException ex2) {
log.info("No task executor bean found for async processing: "
+ "no bean of type TaskExecutor and no bean named 'taskExecutor' either");
}
// Giving up -> either using local default executor or none at all...
}
// backward compatibility
if (log.isInfoEnabled()) {
log.info(
"For backward compatibility, will fallback to the default, SimpleAsyncTaskExecutor implementation");
}
return new SimpleAsyncTaskExecutor();
}
}
}
/*
* 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.async;
import java.util.concurrent.Executor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.interceptor.AsyncExecutionAspectSupport;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
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.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.sleuth.SpanNamer;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.async.LazyTraceExecutor;
import org.springframework.cloud.sleuth.instrument.async.TraceAsyncAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} enabling async related processing.
*
* @author Dave Syer
* @author Marcin Grzejszczak
* @since 1.0.0
* @see LazyTraceExecutor
* @see TraceAsyncAspect
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SleuthAsyncProperties.class)
@ConditionalOnProperty(value = "spring.sleuth.async.enabled", matchIfMissing = true)
@ConditionalOnBean(Tracer.class)
@AutoConfigureAfter(BraveAutoConfiguration.class)
public class TraceAsyncDefaultAutoConfiguration {
@Bean
@ConditionalOnProperty(value = "spring.sleuth.scheduled.enabled", matchIfMissing = true)
static ExecutorBeanPostProcessor executorBeanPostProcessor(BeanFactory beanFactory) {
return new ExecutorBeanPostProcessor(beanFactory);
}
@Bean
TraceAsyncAspect traceAsyncAspect(Tracer tracer, SpanNamer spanNamer) {
return new TraceAsyncAspect(tracer, spanNamer);
}
/**
* Wrapper for the async executor.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(AsyncConfigurer.class)
@ConditionalOnMissingBean(AsyncConfigurer.class)
@ConditionalOnProperty(value = "spring.sleuth.async.configurer.enabled", matchIfMissing = true)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static class DefaultAsyncConfigurerSupport extends AsyncConfigurerSupport {
private static final Log log = LogFactory.getLog(DefaultAsyncConfigurerSupport.class);
@Autowired
private BeanFactory beanFactory;
@Override
public Executor getAsyncExecutor() {
Executor delegate = getDefaultExecutor();
return new LazyTraceExecutor(this.beanFactory, delegate);
}
/**
* Retrieve or build a default executor for this advice instance. An executor
* returned from here will be cached for further use.
* <p>
* The default implementation searches for a unique {@link TaskExecutor} bean in
* the context, or for an {@link Executor} bean named "taskExecutor" otherwise. If
* neither of the two is resolvable, this implementation will return {@code null}.
* @return the default executor, or {@code null} if none available
* @see AsyncExecutionAspectSupport#getDefaultExecutor(org.springframework.beans.factory.BeanFactory)
*/
private Executor getDefaultExecutor() {
try {
// Search for TaskExecutor bean... not plain Executor since that would
// match with ScheduledExecutorService as well, which is unusable for
// our purposes here. TaskExecutor is more clearly designed for it.
return this.beanFactory.getBean(TaskExecutor.class);
}
catch (NoUniqueBeanDefinitionException ex) {
if (log.isDebugEnabled()) {
log.debug("Could not find unique TaskExecutor bean", ex);
}
try {
return this.beanFactory.getBean(AsyncExecutionAspectSupport.DEFAULT_TASK_EXECUTOR_BEAN_NAME,
Executor.class);
}
catch (NoSuchBeanDefinitionException ex2) {
if (log.isInfoEnabled()) {
log.info("More than one TaskExecutor bean found within the context, and none is named "
+ "'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly "
+ "as an alias) in order to use it for async processing: " + ex.getBeanNamesFound());
}
}
}
catch (NoSuchBeanDefinitionException ex) {
log.debug("Could not find default TaskExecutor bean", ex);
try {
return this.beanFactory.getBean(AsyncExecutionAspectSupport.DEFAULT_TASK_EXECUTOR_BEAN_NAME,
Executor.class);
}
catch (NoSuchBeanDefinitionException ex2) {
log.info("No task executor bean found for async processing: "
+ "no bean of type TaskExecutor and no bean named 'taskExecutor' either");
}
// Giving up -> either using local default executor or none at all...
}
// backward compatibility
if (log.isInfoEnabled()) {
log.info(
"For backward compatibility, will fallback to the default, SimpleAsyncTaskExecutor implementation");
}
return new SimpleAsyncTaskExecutor();
}
}
}

View File

@@ -97,8 +97,7 @@ class DataSourceProxyConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.sleuth.jdbc.decorator.datasource.datasource-proxy.count-query",
havingValue = "true")
@ConditionalOnProperty(value = "spring.sleuth.jdbc.datasource-proxy.count-query", havingValue = "true")
QueryCountStrategy queryCountStrategy() {
return new SingleQueryCountHolder();
}

View File

@@ -66,8 +66,8 @@ class P6SpyPropertiesSetter implements BeanDefinitionRegistryPostProcessor, Clos
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
ConfigurableEnvironment environment = this.context.getEnvironment();
String customModuleList = initialP6SpyOptions.get("modulelist");
boolean isEnableLogging = environment
.getProperty("spring.sleuth.jdbc.decorator.datasource.p6spy.enable-logging", Boolean.class, true);
boolean isEnableLogging = environment.getProperty("spring.sleuth.jdbc.p6spy.enable-logging", Boolean.class,
true);
if (customModuleList != null) {
log.info("P6Spy modulelist is overridden, some p6spy configuration features will not be applied");
}
@@ -94,7 +94,7 @@ class P6SpyPropertiesSetter implements BeanDefinitionRegistryPostProcessor, Clos
if (isEnableLogging && !initialP6SpyOptions.containsKey("appender")) {
TraceDataSourceDecoratorProperties.P6SpyProperties.P6SpyLogging logging = TraceDataSourceDecoratorProperties.P6SpyProperties.P6SpyLogging
.valueOf(environment
.getProperty("spring.sleuth.jdbc.decorator.datasource.p6spy.logging", String.class,
.getProperty("spring.sleuth.jdbc.p6spy.logging", String.class,
TraceDataSourceDecoratorProperties.P6SpyProperties.P6SpyLogging.SLF4J.toString())
.toUpperCase());
switch (logging) {
@@ -108,19 +108,17 @@ class P6SpyPropertiesSetter implements BeanDefinitionRegistryPostProcessor, Clos
System.setProperty("p6spy.config.appender", "com.p6spy.engine.spy.appender.FileLogger");
break;
case CUSTOM:
String customAppender = environment.getProperty(
"spring.sleuth.jdbc.decorator.datasource.p6spy.custom-appender-class", String.class, "");
String customAppender = environment.getProperty("spring.sleuth.jdbc.p6spy.custom-appender-class",
String.class, "");
System.setProperty("p6spy.config.appender", customAppender);
break;
}
}
if (!initialP6SpyOptions.containsKey("logfile")) {
String logFile = environment.getProperty("spring.sleuth.jdbc.decorator.datasource.p6spy.log-file",
String.class, "spy.log");
String logFile = environment.getProperty("spring.sleuth.jdbc.p6spy.log-file", String.class, "spy.log");
System.setProperty("p6spy.config.logfile", logFile);
}
String pattern = environment.getProperty("spring.sleuth.jdbc.decorator.datasource.p6spy.log-filter.pattern",
String.class);
String pattern = environment.getProperty("spring.sleuth.jdbc.p6spy.log-filter.pattern", String.class);
if (pattern != null) {
System.setProperty("p6spy.config.filter", "true");
System.setProperty("p6spy.config.sqlexpression", pattern);
@@ -133,11 +131,11 @@ class P6SpyPropertiesSetter implements BeanDefinitionRegistryPostProcessor, Clos
}
private Boolean multiLine(ConfigurableEnvironment environment) {
return environment.getProperty("spring.sleuth.jdbc.decorator.datasource.p6spy.multiline", Boolean.class, true);
return environment.getProperty("spring.sleuth.jdbc.p6spy.multiline", Boolean.class, true);
}
private String logFormat(ConfigurableEnvironment environment) {
return environment.getProperty("spring.sleuth.jdbc.decorator.datasource.p6spy.log-format", String.class);
return environment.getProperty("spring.sleuth.jdbc.p6spy.log-format", String.class);
}
@Override

View File

@@ -42,8 +42,7 @@ import org.springframework.context.annotation.Import;
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(TraceDataSourceDecoratorProperties.class)
@ConditionalOnProperty(name = "spring.sleuth.jdbc.decorator.datasource.enabled", havingValue = "true",
matchIfMissing = true)
@ConditionalOnProperty(name = "spring.sleuth.jdbc.enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnBean({ DataSource.class, Tracer.class })
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@Import({ P6SpyConfiguration.class, DataSourceProxyConfiguration.class })

View File

@@ -22,7 +22,6 @@ import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import net.ttddyy.dsproxy.QueryCountHolder;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -34,7 +33,7 @@ import org.springframework.cloud.sleuth.instrument.jdbc.TraceType;
* @author Arthur Gavlyukovskiy
* @since 3.1.0
*/
@ConfigurationProperties(prefix = "spring.sleuth.jdbc.decorator.datasource")
@ConfigurationProperties(prefix = "spring.sleuth.jdbc")
public class TraceDataSourceDecoratorProperties {
/**
@@ -130,14 +129,6 @@ public class TraceDataSourceDecoratorProperties {
*/
private boolean jsonFormat = false;
/**
* Creates listener to count queries.
*
* @see ProxyDataSourceBuilder#countQuery()
* @see QueryCountHolder
*/
private boolean countQuery = false;
public DataSourceProxyLogging getLogging() {
return logging;
}
@@ -178,14 +169,6 @@ public class TraceDataSourceDecoratorProperties {
this.jsonFormat = jsonFormat;
}
public boolean isCountQuery() {
return countQuery;
}
public void setCountQuery(boolean countQuery) {
this.countQuery = countQuery;
}
/**
* Properties to configure query logging listener.
*/

View File

@@ -1,205 +1,206 @@
/*
* 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.brave.instrument.web.client;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import brave.Span;
import brave.Tracer;
import brave.baggage.BaggagePropagation;
import brave.handler.SpanHandler;
import brave.propagation.B3Propagation;
import brave.sampler.Sampler;
import brave.test.TestSpanHandler;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration;
import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration;
import org.springframework.cloud.sleuth.DisableSecurity;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static brave.Span.Kind.CLIENT;
import static brave.propagation.B3Propagation.Format.SINGLE_NO_PARENT;
import static org.assertj.core.api.BDDAssertions.then;
@SpringBootTest(classes = WebClientTests.TestConfiguration.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {"spring.sleuth.web.servlet.enabled=false", "spring.application.name=fooservice",
"spring.sleuth.web.client.skip-pattern=/skip.*"})
@DirtiesContext
public class WebClientTests {
@Autowired
HttpClientBuilder httpClientBuilder; // #845
@Autowired
HttpAsyncClientBuilder httpAsyncClientBuilder; // #845
@Autowired
TestSpanHandler spans;
@Autowired
Tracer tracer;
@LocalServerPort
int port;
@Autowired
FooController fooController;
@AfterEach
@BeforeEach
public void close() {
this.spans.clear();
this.fooController.clear();
}
@Test
@SuppressWarnings("unchecked")
public void shouldAttachTraceIdWhenCallingAnotherServiceForHttpClient() throws Exception {
then(this.spans).isEmpty();
Span span = this.tracer.nextSpan().name("foo").start();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
String response = this.httpClientBuilder.build().execute(new HttpGet("http://localhost:" + this.port),
new BasicResponseHandler());
then(response).isNotEmpty();
}
then(this.tracer.currentSpan()).isNull();
then(this.spans).isNotEmpty().extracting("traceId", String.class).containsOnly(span.context().traceIdString());
then(this.spans.spans().stream().map(s -> s.kind().name()).collect(Collectors.toList())).contains("CLIENT");
}
@Test
@SuppressWarnings("unchecked")
public void shouldAttachTraceIdWhenCallingAnotherServiceForAsyncHttpClient() throws Exception {
Span span = this.tracer.nextSpan().name("foo").start();
CloseableHttpAsyncClient client = this.httpAsyncClientBuilder.build();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
client.start();
Future<HttpResponse> future = client.execute(new HttpGet("http://localhost:" + this.port),
new FutureCallback<HttpResponse>() {
@Override
public void completed(HttpResponse result) {
}
@Override
public void failed(Exception ex) {
}
@Override
public void cancelled() {
}
});
then(future.get()).isNotNull();
}
finally {
client.close();
}
then(this.tracer.currentSpan()).isNull();
then(this.spans).isNotEmpty().extracting("traceId", String.class).containsOnly(span.context().traceIdString());
then(this.spans.spans().stream().map(s -> s.kind().name()).collect(Collectors.toList())).contains("CLIENT");
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration(exclude = {GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class,
R2dbcAutoConfiguration.class, R2dbcDataAutoConfiguration.class, MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
@DisableSecurity
public static class TestConfiguration {
@Bean
BaggagePropagation.FactoryBuilder baggagePropagationFactoryBuilder() {
// Use b3 single format as it is less verbose
return BaggagePropagation.newFactoryBuilder(
B3Propagation.newFactoryBuilder().injectFormat(CLIENT, SINGLE_NO_PARENT).build());
}
@Bean
FooController fooController() {
return new FooController();
}
@Bean
Sampler testSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
SpanHandler testSpanHandler() {
return new TestSpanHandler();
}
}
@RestController
public static class FooController {
Span span;
@RequestMapping("/")
public Map<String, String> home(@RequestHeader HttpHeaders headers) {
Map<String, String> map = new HashMap<>();
for (String key : headers.keySet()) {
map.put(key, headers.getFirst(key));
}
return map;
}
public Span getSpan() {
return this.span;
}
public void clear() {
this.span = null;
}
}
}
/*
* 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.brave.instrument.web.client;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import brave.Span;
import brave.Tracer;
import brave.baggage.BaggagePropagation;
import brave.handler.SpanHandler;
import brave.propagation.B3Propagation;
import brave.sampler.Sampler;
import brave.test.TestSpanHandler;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration;
import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration;
import org.springframework.cloud.sleuth.DisableSecurity;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static brave.Span.Kind.CLIENT;
import static brave.propagation.B3Propagation.Format.SINGLE_NO_PARENT;
import static org.assertj.core.api.BDDAssertions.then;
@SpringBootTest(classes = WebClientTests.TestConfiguration.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = { "spring.sleuth.web.servlet.enabled=false", "spring.application.name=fooservice",
"spring.sleuth.web.client.skip-pattern=/skip.*" })
@DirtiesContext
public class WebClientTests {
@Autowired
HttpClientBuilder httpClientBuilder; // #845
@Autowired
HttpAsyncClientBuilder httpAsyncClientBuilder; // #845
@Autowired
TestSpanHandler spans;
@Autowired
Tracer tracer;
@LocalServerPort
int port;
@Autowired
FooController fooController;
@AfterEach
@BeforeEach
public void close() {
this.spans.clear();
this.fooController.clear();
}
@Test
@SuppressWarnings("unchecked")
public void shouldAttachTraceIdWhenCallingAnotherServiceForHttpClient() throws Exception {
then(this.spans).isEmpty();
Span span = this.tracer.nextSpan().name("foo").start();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
String response = this.httpClientBuilder.build().execute(new HttpGet("http://localhost:" + this.port),
new BasicResponseHandler());
then(response).isNotEmpty();
}
then(this.tracer.currentSpan()).isNull();
then(this.spans).isNotEmpty().extracting("traceId", String.class).containsOnly(span.context().traceIdString());
then(this.spans.spans().stream().map(s -> s.kind().name()).collect(Collectors.toList())).contains("CLIENT");
}
@Test
@SuppressWarnings("unchecked")
public void shouldAttachTraceIdWhenCallingAnotherServiceForAsyncHttpClient() throws Exception {
Span span = this.tracer.nextSpan().name("foo").start();
CloseableHttpAsyncClient client = this.httpAsyncClientBuilder.build();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
client.start();
Future<HttpResponse> future = client.execute(new HttpGet("http://localhost:" + this.port),
new FutureCallback<HttpResponse>() {
@Override
public void completed(HttpResponse result) {
}
@Override
public void failed(Exception ex) {
}
@Override
public void cancelled() {
}
});
then(future.get()).isNotNull();
}
finally {
client.close();
}
then(this.tracer.currentSpan()).isNull();
then(this.spans).isNotEmpty().extracting("traceId", String.class).containsOnly(span.context().traceIdString());
then(this.spans.spans().stream().map(s -> s.kind().name()).collect(Collectors.toList())).contains("CLIENT");
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class,
R2dbcAutoConfiguration.class, R2dbcDataAutoConfiguration.class, MongoAutoConfiguration.class,
MongoDataAutoConfiguration.class })
@DisableSecurity
public static class TestConfiguration {
@Bean
BaggagePropagation.FactoryBuilder baggagePropagationFactoryBuilder() {
// Use b3 single format as it is less verbose
return BaggagePropagation.newFactoryBuilder(
B3Propagation.newFactoryBuilder().injectFormat(CLIENT, SINGLE_NO_PARENT).build());
}
@Bean
FooController fooController() {
return new FooController();
}
@Bean
Sampler testSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
SpanHandler testSpanHandler() {
return new TestSpanHandler();
}
}
@RestController
public static class FooController {
Span span;
@RequestMapping("/")
public Map<String, String> home(@RequestHeader HttpHeaders headers) {
Map<String, String> map = new HashMap<>();
for (String key : headers.keySet()) {
map.put(key, headers.getFirst(key));
}
return map;
}
public Span getSpan() {
return this.span;
}
public void clear() {
this.span = null;
}
}
}

View File

@@ -75,7 +75,7 @@ class DataSourceDecoratorAutoConfigurationTests {
@Test
void testNoDecoratingForExcludeBeans() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.decorator.datasource.excluded-beans:dataSource");
.withPropertyValues("spring.sleuth.jdbc.excluded-beans:dataSource");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
@@ -183,7 +183,7 @@ class DataSourceDecoratorAutoConfigurationTests {
@Test
void testDecoratingCanBeDisabled() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.decorator.datasource.enabled:false");
.withPropertyValues("spring.sleuth.jdbc.enabled:false");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
@@ -194,7 +194,7 @@ class DataSourceDecoratorAutoConfigurationTests {
@Test
void testDecoratingCanBeDisabledForSpecificBeans() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.decorator.datasource.excluded-beans:secondDataSource")
.withPropertyValues("spring.sleuth.jdbc.excluded-beans:secondDataSource")
.withUserConfiguration(TestMultiDataSourceConfiguration.class);
contextRunner.run(context -> {

View File

@@ -1,281 +1,280 @@
/*
* 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.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import javax.sql.DataSource;
import com.p6spy.engine.common.ConnectionInformation;
import com.p6spy.engine.common.P6LogQuery;
import com.p6spy.engine.event.CompoundJdbcEventListener;
import com.p6spy.engine.event.JdbcEventListener;
import com.p6spy.engine.logging.Category;
import com.p6spy.engine.logging.LoggingEventListener;
import com.p6spy.engine.spy.JdbcEventListenerFactory;
import com.p6spy.engine.spy.P6DataSource;
import com.p6spy.engine.spy.appender.CustomLineFormat;
import com.p6spy.engine.spy.appender.FormattedLogger;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
class P6SpyConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceDataSourceDecoratorAutoConfiguration.class, BraveAutoConfiguration.class,
TestSpanHandlerConfiguration.class, PropertyPlaceholderAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.datasource.url:jdbc:h2:mem:testdb-" + ThreadLocalRandom.current().nextInt())
.withClassLoader(new FilteredClassLoader("net.ttddyy.dsproxy"));
@BeforeEach
@AfterEach
void resetLogAccumulator() {
LogAccumulator.reset();
}
@Test
void testCustomListeners() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(CustomListenerConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
GetCountingListener getCountingListener = context.getBean(GetCountingListener.class);
ClosingCountingListener closingCountingListener = context.getBean(ClosingCountingListener.class);
P6DataSource p6DataSource = (P6DataSource) ((DataSourceWrapper) dataSource).getDecoratedDataSource();
assertThat(p6DataSource).extracting("jdbcEventListenerFactory").isEqualTo(jdbcEventListenerFactory);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).contains(getCountingListener, closingCountingListener);
assertThat(getCountingListener.connectionCount).isEqualTo(0);
Connection connection1 = p6DataSource.getConnection();
assertThat(getCountingListener.connectionCount).isEqualTo(1);
assertThat(closingCountingListener.connectionCount).isEqualTo(0);
Connection connection2 = p6DataSource.getConnection();
assertThat(getCountingListener.connectionCount).isEqualTo(2);
// order matters!
connection2.close();
assertThat(closingCountingListener.connectionCount).isEqualTo(1);
// order matters!
connection1.close();
assertThat(closingCountingListener.connectionCount).isEqualTo(2);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testDoesNotRegisterLoggingListenerIfDisabled() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.decorator.datasource.p6spy.enable-logging:false");
contextRunner.run(context -> {
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).extracting("class")
.doesNotContain(LoggingEventListener.class);
});
}
@Test
void testCanSetCustomLoggingFormat() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.decorator.datasource.p6spy.log-format:test %{connectionId}");
contextRunner.run(context -> {
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).extracting("class").contains(LoggingEventListener.class);
assertThat(P6LogQuery.getLogger()).extracting("strategy").extracting("class")
.isEqualTo(CustomLineFormat.class);
});
}
@Test
void testMultilineShouldNotOverrideCustomProperties() {
System.setProperty("p6spy.config.logMessageFormat", "com.p6spy.engine.spy.appender.CustomLineFormat");
System.setProperty("p6spy.config.excludecategories", "debug");
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.decorator.datasource.p6spy.multiline:true");
contextRunner.run(context -> {
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).extracting("class").contains(LoggingEventListener.class);
assertThat(P6LogQuery.getLogger()).extracting("strategy").extracting("class")
.isEqualTo(CustomLineFormat.class);
});
}
@Test
void testUseCustomLogger() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues(
"spring.sleuth.jdbc.decorator.datasource.p6spy.logging:custom",
"spring.sleuth.jdbc.decorator.datasource.p6spy.custom-appender-class:"
+ LogAccumulator.class.getName());
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
dataSource.getConnection().close();
assertThat(P6LogQuery.getLogger()).isInstanceOf(LogAccumulator.class);
});
}
@Test
void testLogFilterPattern() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues(
"spring.sleuth.jdbc.decorator.datasource.p6spy.logging:custom",
"spring.sleuth.jdbc.decorator.datasource.p6spy.custom-appender-class:" + LogAccumulator.class.getName(),
"spring.sleuth.jdbc.decorator.datasource.p6spy.log-filter.pattern:.*table1.*");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
try (Connection connection = dataSource.getConnection();
PreparedStatement ps1 = connection.prepareStatement("select 1 /* from table1 */");
PreparedStatement ps2 = connection.prepareStatement("select 1 /* from table2 */")) {
ps1.execute();
ps2.execute();
}
assertThat(LogAccumulator.MESSAGES).hasSize(1);
assertThat(LogAccumulator.MESSAGES).allMatch(message -> message.contains("table1"));
});
}
@Test
void testLogFilterPatternMatchAll() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues(
"spring.sleuth.jdbc.decorator.datasource.p6spy.logging:custom",
"spring.sleuth.jdbc.decorator.datasource.p6spy.custom-appender-class:" + LogAccumulator.class.getName(),
"spring.sleuth.jdbc.decorator.datasource.p6spy.log-filter.pattern:.*");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
try (Connection connection = dataSource.getConnection();
PreparedStatement ps1 = connection.prepareStatement("select 1 /* from table1 */");
PreparedStatement ps2 = connection.prepareStatement("select 1 /* from table2 */")) {
ps1.execute();
ps2.execute();
}
assertThat(LogAccumulator.MESSAGES).hasSize(2);
});
}
@Configuration
static class CustomListenerConfiguration {
@Bean
public GetCountingListener wrappingCountingListener() {
return new GetCountingListener();
}
@Bean
public ClosingCountingListener closingCountingListener() {
return new ClosingCountingListener();
}
}
static class GetCountingListener extends JdbcEventListener {
int connectionCount = 0;
@Override
public void onAfterGetConnection(ConnectionInformation connectionInformation, SQLException e) {
connectionCount++;
}
}
static class ClosingCountingListener extends JdbcEventListener {
int connectionCount = 0;
@Override
public void onAfterConnectionClose(ConnectionInformation connectionInformation, SQLException e) {
connectionCount++;
}
}
public static class LogAccumulator extends FormattedLogger {
static final List<String> MESSAGES = new ArrayList<>();
static final List<Exception> EXCEPTIONS = new ArrayList<>();
public static void reset() {
MESSAGES.clear();
EXCEPTIONS.clear();
}
@Override
public void logException(Exception e) {
EXCEPTIONS.add(e);
}
@Override
public void logText(String text) {
MESSAGES.add(text);
}
@Override
public boolean isCategoryEnabled(Category category) {
return true;
}
}
}
/*
* 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.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import javax.sql.DataSource;
import com.p6spy.engine.common.ConnectionInformation;
import com.p6spy.engine.common.P6LogQuery;
import com.p6spy.engine.event.CompoundJdbcEventListener;
import com.p6spy.engine.event.JdbcEventListener;
import com.p6spy.engine.logging.Category;
import com.p6spy.engine.logging.LoggingEventListener;
import com.p6spy.engine.spy.JdbcEventListenerFactory;
import com.p6spy.engine.spy.P6DataSource;
import com.p6spy.engine.spy.appender.CustomLineFormat;
import com.p6spy.engine.spy.appender.FormattedLogger;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
class P6SpyConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceDataSourceDecoratorAutoConfiguration.class, BraveAutoConfiguration.class,
TestSpanHandlerConfiguration.class, PropertyPlaceholderAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.datasource.url:jdbc:h2:mem:testdb-" + ThreadLocalRandom.current().nextInt())
.withClassLoader(new FilteredClassLoader("net.ttddyy.dsproxy"));
@BeforeEach
@AfterEach
void resetLogAccumulator() {
LogAccumulator.reset();
}
@Test
void testCustomListeners() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(CustomListenerConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
GetCountingListener getCountingListener = context.getBean(GetCountingListener.class);
ClosingCountingListener closingCountingListener = context.getBean(ClosingCountingListener.class);
P6DataSource p6DataSource = (P6DataSource) ((DataSourceWrapper) dataSource).getDecoratedDataSource();
assertThat(p6DataSource).extracting("jdbcEventListenerFactory").isEqualTo(jdbcEventListenerFactory);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).contains(getCountingListener, closingCountingListener);
assertThat(getCountingListener.connectionCount).isEqualTo(0);
Connection connection1 = p6DataSource.getConnection();
assertThat(getCountingListener.connectionCount).isEqualTo(1);
assertThat(closingCountingListener.connectionCount).isEqualTo(0);
Connection connection2 = p6DataSource.getConnection();
assertThat(getCountingListener.connectionCount).isEqualTo(2);
// order matters!
connection2.close();
assertThat(closingCountingListener.connectionCount).isEqualTo(1);
// order matters!
connection1.close();
assertThat(closingCountingListener.connectionCount).isEqualTo(2);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testDoesNotRegisterLoggingListenerIfDisabled() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.p6spy.enable-logging:false");
contextRunner.run(context -> {
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).extracting("class")
.doesNotContain(LoggingEventListener.class);
});
}
@Test
void testCanSetCustomLoggingFormat() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.p6spy.log-format:test %{connectionId}");
contextRunner.run(context -> {
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).extracting("class").contains(LoggingEventListener.class);
assertThat(P6LogQuery.getLogger()).extracting("strategy").extracting("class")
.isEqualTo(CustomLineFormat.class);
});
}
@Test
void testMultilineShouldNotOverrideCustomProperties() {
System.setProperty("p6spy.config.logMessageFormat", "com.p6spy.engine.spy.appender.CustomLineFormat");
System.setProperty("p6spy.config.excludecategories", "debug");
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.p6spy.multiline:true");
contextRunner.run(context -> {
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).extracting("class").contains(LoggingEventListener.class);
assertThat(P6LogQuery.getLogger()).extracting("strategy").extracting("class")
.isEqualTo(CustomLineFormat.class);
});
}
@Test
void testUseCustomLogger() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues(
"spring.sleuth.jdbc.p6spy.logging:custom",
"spring.sleuth.jdbc.p6spy.custom-appender-class:" + LogAccumulator.class.getName());
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
dataSource.getConnection().close();
assertThat(P6LogQuery.getLogger()).isInstanceOf(LogAccumulator.class);
});
}
@Test
void testLogFilterPattern() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues(
"spring.sleuth.jdbc.p6spy.logging:custom",
"spring.sleuth.jdbc.p6spy.custom-appender-class:" + LogAccumulator.class.getName(),
"spring.sleuth.jdbc.p6spy.log-filter.pattern:.*table1.*");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
try (Connection connection = dataSource.getConnection();
PreparedStatement ps1 = connection.prepareStatement("select 1 /* from table1 */");
PreparedStatement ps2 = connection.prepareStatement("select 1 /* from table2 */")) {
ps1.execute();
ps2.execute();
}
assertThat(LogAccumulator.MESSAGES).hasSize(1);
assertThat(LogAccumulator.MESSAGES).allMatch(message -> message.contains("table1"));
});
}
@Test
void testLogFilterPatternMatchAll() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues(
"spring.sleuth.jdbc.p6spy.logging:custom",
"spring.sleuth.jdbc.p6spy.custom-appender-class:" + LogAccumulator.class.getName(),
"spring.sleuth.jdbc.p6spy.log-filter.pattern:.*");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
try (Connection connection = dataSource.getConnection();
PreparedStatement ps1 = connection.prepareStatement("select 1 /* from table1 */");
PreparedStatement ps2 = connection.prepareStatement("select 1 /* from table2 */")) {
ps1.execute();
ps2.execute();
}
assertThat(LogAccumulator.MESSAGES).hasSize(2);
});
}
@Configuration
static class CustomListenerConfiguration {
@Bean
public GetCountingListener wrappingCountingListener() {
return new GetCountingListener();
}
@Bean
public ClosingCountingListener closingCountingListener() {
return new ClosingCountingListener();
}
}
static class GetCountingListener extends JdbcEventListener {
int connectionCount = 0;
@Override
public void onAfterGetConnection(ConnectionInformation connectionInformation, SQLException e) {
connectionCount++;
}
}
static class ClosingCountingListener extends JdbcEventListener {
int connectionCount = 0;
@Override
public void onAfterConnectionClose(ConnectionInformation connectionInformation, SQLException e) {
connectionCount++;
}
}
public static class LogAccumulator extends FormattedLogger {
static final List<String> MESSAGES = new ArrayList<>();
static final List<Exception> EXCEPTIONS = new ArrayList<>();
public static void reset() {
MESSAGES.clear();
EXCEPTIONS.clear();
}
@Override
public void logException(Exception e) {
EXCEPTIONS.add(e);
}
@Override
public void logText(String text) {
MESSAGES.add(text);
}
@Override
public boolean isCategoryEnabled(Category category) {
return true;
}
}
}

View File

@@ -1,246 +1,246 @@
/*
* 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.jdbc;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import javax.sql.DataSource;
import net.ttddyy.dsproxy.ExecutionInfo;
import net.ttddyy.dsproxy.QueryInfo;
import net.ttddyy.dsproxy.listener.ChainListener;
import net.ttddyy.dsproxy.listener.QueryExecutionListener;
import net.ttddyy.dsproxy.listener.logging.CommonsQueryLoggingListener;
import net.ttddyy.dsproxy.listener.logging.CommonsSlowQueryListener;
import net.ttddyy.dsproxy.listener.logging.JULQueryLoggingListener;
import net.ttddyy.dsproxy.listener.logging.JULSlowQueryListener;
import net.ttddyy.dsproxy.listener.logging.SLF4JQueryLoggingListener;
import net.ttddyy.dsproxy.listener.logging.SLF4JSlowQueryListener;
import net.ttddyy.dsproxy.listener.logging.SystemOutQueryLoggingListener;
import net.ttddyy.dsproxy.listener.logging.SystemOutSlowQueryListener;
import net.ttddyy.dsproxy.proxy.DefaultConnectionIdManager;
import net.ttddyy.dsproxy.proxy.GlobalConnectionIdManager;
import net.ttddyy.dsproxy.support.ProxyDataSource;
import net.ttddyy.dsproxy.transform.ParameterTransformer;
import net.ttddyy.dsproxy.transform.QueryTransformer;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceProxyConnectionIdManagerProvider;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import static org.assertj.core.api.Assertions.assertThat;
class ProxyDataSourceConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceDataSourceDecoratorAutoConfiguration.class, BraveAutoConfiguration.class,
TestSpanHandlerConfiguration.class, PropertyPlaceholderAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.datasource.url:jdbc:h2:mem:testdb-" + ThreadLocalRandom.current().nextInt())
.withClassLoader(new FilteredClassLoader("com.p6spy"));
@Test
void testRegisterLogAndSlowQueryLogByDefaultToSlf4j() {
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JQueryLoggingListener.class);
});
}
@Test
void testRegisterLogAndSlowQueryLogByUsingSlf4j() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.decorator.datasource.datasource-proxy.logging:slf4j");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JQueryLoggingListener.class);
});
}
@Test
void testRegisterLogAndSlowQueryLogUsingSystemOut() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.decorator.datasource.datasource-proxy.logging:sysout");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(SystemOutSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(SystemOutQueryLoggingListener.class);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testRegisterLogAndSlowQueryLogUsingJUL() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.decorator.datasource.datasourceProxy.logging:jul");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(JULSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(JULQueryLoggingListener.class);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testRegisterLogAndSlowQueryLogUsingApacheCommons() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.decorator.datasource.datasourceProxy.logging:commons");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(CommonsSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(CommonsQueryLoggingListener.class);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testCustomParameterAndQueryTransformer() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(CustomDataSourceProxyConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ParameterTransformer parameterTransformer = context.getBean(ParameterTransformer.class);
QueryTransformer queryTransformer = context.getBean(QueryTransformer.class);
assertThat(proxyDataSource.getProxyConfig().getParameterTransformer()).isSameAs(parameterTransformer);
assertThat(proxyDataSource.getProxyConfig().getQueryTransformer()).isSameAs(queryTransformer);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testCustomListeners() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(CustomListenerConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
QueryExecutionListener queryExecutionListener = context.getBean(QueryExecutionListener.class);
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).contains(queryExecutionListener);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testGlobalConnectionIdManagerByDefault() {
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
assertThat(proxyDataSource.getConnectionIdManager()).isInstanceOf(GlobalConnectionIdManager.class);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testCustomConnectionIdManager() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(CustomDataSourceProxyConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
assertThat(proxyDataSource.getConnectionIdManager()).isInstanceOf(DefaultConnectionIdManager.class);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Configuration
static class CustomDataSourceProxyConfiguration {
@Bean
public ParameterTransformer parameterTransformer() {
return (replacer, transformInfo) -> {
};
}
@Bean
public QueryTransformer queryTransformer() {
return (transformInfo) -> "TestQuery";
}
@Bean
public DataSourceProxyConnectionIdManagerProvider connectionIdManagerProvider() {
return DefaultConnectionIdManager::new;
}
}
@Configuration
static class CustomListenerConfiguration {
@Bean
@Primary
public QueryExecutionListener queryExecutionListener() {
return new QueryExecutionListener() {
@Override
public void beforeQuery(ExecutionInfo execInfo, List<QueryInfo> queryInfoList) {
System.out.println("beforeQuery");
}
@Override
public void afterQuery(ExecutionInfo execInfo, List<QueryInfo> queryInfoList) {
System.out.println("afterQuery");
}
};
}
}
}
/*
* 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.jdbc;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import javax.sql.DataSource;
import net.ttddyy.dsproxy.ExecutionInfo;
import net.ttddyy.dsproxy.QueryInfo;
import net.ttddyy.dsproxy.listener.ChainListener;
import net.ttddyy.dsproxy.listener.QueryExecutionListener;
import net.ttddyy.dsproxy.listener.logging.CommonsQueryLoggingListener;
import net.ttddyy.dsproxy.listener.logging.CommonsSlowQueryListener;
import net.ttddyy.dsproxy.listener.logging.JULQueryLoggingListener;
import net.ttddyy.dsproxy.listener.logging.JULSlowQueryListener;
import net.ttddyy.dsproxy.listener.logging.SLF4JQueryLoggingListener;
import net.ttddyy.dsproxy.listener.logging.SLF4JSlowQueryListener;
import net.ttddyy.dsproxy.listener.logging.SystemOutQueryLoggingListener;
import net.ttddyy.dsproxy.listener.logging.SystemOutSlowQueryListener;
import net.ttddyy.dsproxy.proxy.DefaultConnectionIdManager;
import net.ttddyy.dsproxy.proxy.GlobalConnectionIdManager;
import net.ttddyy.dsproxy.support.ProxyDataSource;
import net.ttddyy.dsproxy.transform.ParameterTransformer;
import net.ttddyy.dsproxy.transform.QueryTransformer;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceProxyConnectionIdManagerProvider;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import static org.assertj.core.api.Assertions.assertThat;
class ProxyDataSourceConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceDataSourceDecoratorAutoConfiguration.class, BraveAutoConfiguration.class,
TestSpanHandlerConfiguration.class, PropertyPlaceholderAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.datasource.url:jdbc:h2:mem:testdb-" + ThreadLocalRandom.current().nextInt())
.withClassLoader(new FilteredClassLoader("com.p6spy"));
@Test
void testRegisterLogAndSlowQueryLogByDefaultToSlf4j() {
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JQueryLoggingListener.class);
});
}
@Test
void testRegisterLogAndSlowQueryLogByUsingSlf4j() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.datasource-proxy.logging:slf4j");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JQueryLoggingListener.class);
});
}
@Test
void testRegisterLogAndSlowQueryLogUsingSystemOut() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.datasource-proxy.logging:sysout");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(SystemOutSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(SystemOutQueryLoggingListener.class);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testRegisterLogAndSlowQueryLogUsingJUL() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.datasourceProxy.logging:jul");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(JULSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(JULQueryLoggingListener.class);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testRegisterLogAndSlowQueryLogUsingApacheCommons() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.datasourceProxy.logging:commons");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(CommonsSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(CommonsQueryLoggingListener.class);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testCustomParameterAndQueryTransformer() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(CustomDataSourceProxyConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ParameterTransformer parameterTransformer = context.getBean(ParameterTransformer.class);
QueryTransformer queryTransformer = context.getBean(QueryTransformer.class);
assertThat(proxyDataSource.getProxyConfig().getParameterTransformer()).isSameAs(parameterTransformer);
assertThat(proxyDataSource.getProxyConfig().getQueryTransformer()).isSameAs(queryTransformer);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testCustomListeners() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(CustomListenerConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
QueryExecutionListener queryExecutionListener = context.getBean(QueryExecutionListener.class);
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).contains(queryExecutionListener);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testGlobalConnectionIdManagerByDefault() {
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
assertThat(proxyDataSource.getConnectionIdManager()).isInstanceOf(GlobalConnectionIdManager.class);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testCustomConnectionIdManager() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(CustomDataSourceProxyConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
assertThat(proxyDataSource.getConnectionIdManager()).isInstanceOf(DefaultConnectionIdManager.class);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Configuration
static class CustomDataSourceProxyConfiguration {
@Bean
public ParameterTransformer parameterTransformer() {
return (replacer, transformInfo) -> {
};
}
@Bean
public QueryTransformer queryTransformer() {
return (transformInfo) -> "TestQuery";
}
@Bean
public DataSourceProxyConnectionIdManagerProvider connectionIdManagerProvider() {
return DefaultConnectionIdManager::new;
}
}
@Configuration
static class CustomListenerConfiguration {
@Bean
@Primary
public QueryExecutionListener queryExecutionListener() {
return new QueryExecutionListener() {
@Override
public void beforeQuery(ExecutionInfo execInfo, List<QueryInfo> queryInfoList) {
System.out.println("beforeQuery");
}
@Override
public void afterQuery(ExecutionInfo execInfo, List<QueryInfo> queryInfoList) {
System.out.println("afterQuery");
}
};
}
}
}

View File

@@ -50,9 +50,7 @@ class TracingJdbcEventListenerTests extends TracingListenerStrategyTests {
@Test
void testShouldUsePlaceholderInSqlTagOfSpansForPreparedStatementIfIncludeParameterValuesIsSetToFalse() {
contextRunner
.withPropertyValues(
"spring.sleuth.jdbc.decorator.datasource.p6spy.tracing.include-parameter-values=false")
contextRunner.withPropertyValues("spring.sleuth.jdbc.p6spy.tracing.include-parameter-values=false")
.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class);

View File

@@ -490,29 +490,28 @@ abstract class TracingListenerStrategyTests {
@Test
void testShouldIncludeOnlyConnectionTraces() {
contextRunner.withPropertyValues("spring.sleuth.jdbc.decorator.datasource.includes: connection")
.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class);
contextRunner.withPropertyValues("spring.sleuth.jdbc.includes: connection").run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class);
Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select 1 FROM dual");
resultSet.next();
resultSet.close();
statement.close();
connection.close();
Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select 1 FROM dual");
resultSet.next();
resultSet.close();
statement.close();
connection.close();
assertThat(spanReporter.spans()).hasSize(1);
MutableSpan connectionSpan = spanReporter.spans().get(0);
assertThat(connectionSpan.name()).isEqualTo("connection");
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
assertThat(spanReporter.spans()).hasSize(1);
MutableSpan connectionSpan = spanReporter.spans().get(0);
assertThat(connectionSpan.name()).isEqualTo("connection");
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testShouldIncludeOnlyQueryTraces() {
contextRunner.withPropertyValues("spring.sleuth.jdbc.decorator.datasource.includes: query").run(context -> {
contextRunner.withPropertyValues("spring.sleuth.jdbc.includes: query").run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class);
@@ -533,7 +532,7 @@ abstract class TracingListenerStrategyTests {
@Test
void testShouldIncludeOnlyFetchTraces() {
contextRunner.withPropertyValues("spring.sleuth.jdbc.decorator.datasource.includes: fetch").run(context -> {
contextRunner.withPropertyValues("spring.sleuth.jdbc.includes: fetch").run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class);
@@ -554,74 +553,71 @@ abstract class TracingListenerStrategyTests {
@Test
void testShouldIncludeOnlyConnectionAndQueryTraces() {
contextRunner.withPropertyValues("spring.sleuth.jdbc.decorator.datasource.includes: connection, query")
.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class);
contextRunner.withPropertyValues("spring.sleuth.jdbc.includes: connection, query").run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class);
Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select 1 FROM dual");
resultSet.next();
resultSet.close();
statement.close();
connection.close();
Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select 1 FROM dual");
resultSet.next();
resultSet.close();
statement.close();
connection.close();
assertThat(spanReporter.spans()).hasSize(2);
MutableSpan connectionSpan = spanReporter.spans().get(1);
MutableSpan statementSpan = spanReporter.spans().get(0);
assertThat(connectionSpan.name()).isEqualTo("connection");
assertThat(statementSpan.name()).isEqualTo("select");
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
assertThat(spanReporter.spans()).hasSize(2);
MutableSpan connectionSpan = spanReporter.spans().get(1);
MutableSpan statementSpan = spanReporter.spans().get(0);
assertThat(connectionSpan.name()).isEqualTo("connection");
assertThat(statementSpan.name()).isEqualTo("select");
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testShouldIncludeOnlyConnectionAndFetchTraces() {
contextRunner.withPropertyValues("spring.sleuth.jdbc.decorator.datasource.includes: connection, fetch")
.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class);
contextRunner.withPropertyValues("spring.sleuth.jdbc.includes: connection, fetch").run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class);
Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select 1 FROM dual");
resultSet.next();
resultSet.close();
statement.close();
connection.close();
Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select 1 FROM dual");
resultSet.next();
resultSet.close();
statement.close();
connection.close();
assertThat(spanReporter.spans()).hasSize(2);
MutableSpan connectionSpan = spanReporter.spans().get(1);
MutableSpan resultSetSpan = spanReporter.spans().get(0);
assertThat(connectionSpan.name()).isEqualTo("connection");
assertThat(resultSetSpan.name()).isEqualTo("result-set");
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
assertThat(spanReporter.spans()).hasSize(2);
MutableSpan connectionSpan = spanReporter.spans().get(1);
MutableSpan resultSetSpan = spanReporter.spans().get(0);
assertThat(connectionSpan.name()).isEqualTo("connection");
assertThat(resultSetSpan.name()).isEqualTo("result-set");
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testShouldIncludeOnlyQueryAndFetchTraces() {
contextRunner.withPropertyValues("spring.sleuth.jdbc.decorator.datasource.includes: query, fetch")
.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class);
contextRunner.withPropertyValues("spring.sleuth.jdbc.includes: query, fetch").run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class);
Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select 1 FROM dual");
resultSet.next();
resultSet.close();
statement.close();
connection.close();
Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select 1 FROM dual");
resultSet.next();
resultSet.close();
statement.close();
connection.close();
assertThat(spanReporter.spans()).hasSize(2);
MutableSpan resultSetSpan = spanReporter.spans().get(1);
MutableSpan statementSpan = spanReporter.spans().get(0);
assertThat(statementSpan.name()).isEqualTo("select");
assertThat(resultSetSpan.name()).isEqualTo("result-set");
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
assertThat(spanReporter.spans()).hasSize(2);
MutableSpan resultSetSpan = spanReporter.spans().get(1);
MutableSpan statementSpan = spanReporter.spans().get(0);
assertThat(statementSpan.name()).isEqualTo("select");
assertThat(resultSetSpan.name()).isEqualTo("result-set");
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test

View File

@@ -136,9 +136,6 @@ public class DataSourceProxyBuilderCustomizer {
if (this.datasourceProxy.isJsonFormat()) {
proxyDataSourceBuilder.asJson();
}
if (this.datasourceProxy.isCountQuery()) {
ifAvailable(this.queryCountStrategy, proxyDataSourceBuilder::countQuery);
}
ifAvailable(this.listeners, l -> l.forEach(proxyDataSourceBuilder::listener));
ifAvailable(this.methodExecutionListeners, m -> m.forEach(proxyDataSourceBuilder::methodListener));
ifAvailable(this.parameterTransformer, proxyDataSourceBuilder::parameterTransformer);

View File

@@ -19,7 +19,6 @@ package org.springframework.cloud.sleuth.instrument.jdbc;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import net.ttddyy.dsproxy.QueryCountHolder;
import net.ttddyy.dsproxy.listener.logging.CommonsLogLevel;
import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
@@ -61,14 +60,6 @@ public class DataSourceProxyProperties {
*/
private boolean jsonFormat = false;
/**
* Creates listener to count queries.
*
* @see ProxyDataSourceBuilder#countQuery()
* @see QueryCountHolder
*/
private boolean countQuery = false;
public DataSourceProxyLogging getLogging() {
return logging;
}
@@ -109,14 +100,6 @@ public class DataSourceProxyProperties {
this.jsonFormat = jsonFormat;
}
public boolean isCountQuery() {
return countQuery;
}
public void setCountQuery(boolean countQuery) {
this.countQuery = countQuery;
}
/**
* Properties to configure query logging listener.
*

View File

@@ -21,10 +21,7 @@ import javax.sql.DataSource;
import org.springframework.jdbc.datasource.DelegatingDataSource;
/**
* Interface that implicitly added to the CGLIB proxy of {@link DataSource}.
*
* Returns link of both real {@link DataSource}, decorated {@link DataSource} and all
* decorating chain including decorator bean name, instance and result of decorating.
* Returns link of both real {@link DataSource}, decorated {@link DataSource}.
*
* @author Arthur Gavlyukovskiy
* @since 3.1.0

View File

@@ -1,66 +1,66 @@
/*
* 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.brave.instrument.kafka;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.header.Header;
import org.assertj.core.api.BDDAssertions;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.sleuth.brave.BraveTestTracing;
import org.springframework.cloud.sleuth.test.TestTracingAware;
public class KafkaProducerTest extends org.springframework.cloud.sleuth.instrument.kafka.KafkaProducerTest {
BraveTestTracing testTracing;
@Override
public TestTracingAware tracerTest() {
if (this.testTracing == null) {
this.testTracing = new BraveTestTracing();
}
return this.testTracing;
}
@Test
public void should_inject_native_headers() throws InterruptedException {
ProducerRecord<String, String> producerRecord = new ProducerRecord<>(testTopic, "test", "test");
startKafkaConsumer();
this.kafkaProducer.send(producerRecord);
Awaitility.await().atMost(1, TimeUnit.MINUTES).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> {
ConsumerRecord<String, String> consumerRecord = consumerRecords.poll(15, TimeUnit.SECONDS);
BDDAssertions.then(consumerRecord).isNotNull();
BDDAssertions.then(getHeaderValueOrNull(consumerRecord, "X-B3-TraceId")).isNotNull();
BDDAssertions.then(getHeaderValueOrNull(consumerRecord, "X-B3-SpanId")).isNotNull();
BDDAssertions.then(getHeaderValueOrNull(consumerRecord, "X-B3-Sampled")).isNotNull();
});
}
private static String getHeaderValueOrNull(ConsumerRecord<?, ?> consumerRecord, String header) {
return Optional.ofNullable(consumerRecord).map(ConsumerRecord::headers)
.map(headers -> headers.lastHeader(header)).map(Header::value).map(String::new).orElse(null);
}
}
/*
* 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.brave.instrument.kafka;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.header.Header;
import org.assertj.core.api.BDDAssertions;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.sleuth.brave.BraveTestTracing;
import org.springframework.cloud.sleuth.test.TestTracingAware;
public class KafkaProducerTest extends org.springframework.cloud.sleuth.instrument.kafka.KafkaProducerTest {
BraveTestTracing testTracing;
@Override
public TestTracingAware tracerTest() {
if (this.testTracing == null) {
this.testTracing = new BraveTestTracing();
}
return this.testTracing;
}
@Test
public void should_inject_native_headers() throws InterruptedException {
ProducerRecord<String, String> producerRecord = new ProducerRecord<>(testTopic, "test", "test");
startKafkaConsumer();
this.kafkaProducer.send(producerRecord);
Awaitility.await().atMost(1, TimeUnit.MINUTES).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> {
ConsumerRecord<String, String> consumerRecord = consumerRecords.poll(15, TimeUnit.SECONDS);
BDDAssertions.then(consumerRecord).isNotNull();
BDDAssertions.then(getHeaderValueOrNull(consumerRecord, "X-B3-TraceId")).isNotNull();
BDDAssertions.then(getHeaderValueOrNull(consumerRecord, "X-B3-SpanId")).isNotNull();
BDDAssertions.then(getHeaderValueOrNull(consumerRecord, "X-B3-Sampled")).isNotNull();
});
}
private static String getHeaderValueOrNull(ConsumerRecord<?, ?> consumerRecord, String header) {
return Optional.ofNullable(consumerRecord).map(ConsumerRecord::headers)
.map(headers -> headers.lastHeader(header)).map(Header::value).map(String::new).orElse(null);
}
}

View File

@@ -1,233 +1,237 @@
/*
* 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.brave.instrument.web;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import brave.Span;
import brave.Tracer;
import brave.handler.MutableSpan;
import brave.handler.SpanHandler;
import brave.sampler.Sampler;
import brave.test.TestSpanHandler;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.SpanName;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.BDDAssertions.then;
@SpringBootTest(classes = { TraceAsyncIntegrationTests.TraceAsyncITestConfiguration.class })
public class TraceAsyncIntegrationTests {
@Autowired
ClassPerformingAsyncLogic classPerformingAsyncLogic;
@Autowired
Tracer tracer;
@Autowired
TestSpanHandler spans;
@BeforeEach
public void cleanup() {
this.spans.clear();
this.classPerformingAsyncLogic.clear();
}
@Test
public void should_set_span_on_an_async_annotated_method() {
whenAsyncProcessingTakesPlace();
thenANewAsyncSpanGetsCreated();
}
@Test
public void should_set_span_with_custom_method_on_an_async_annotated_method() {
whenAsyncProcessingTakesPlaceWithCustomSpanName();
thenAsyncSpanHasCustomName();
}
@Test
public void should_continue_a_span_on_an_async_annotated_method() {
Span span = givenASpanInCurrentThread();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
whenAsyncProcessingTakesPlace();
}
finally {
span.finish();
}
thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOne(span);
}
@Test
public void should_continue_a_span_with_custom_method_on_an_async_annotated_method() {
Span span = givenASpanInCurrentThread();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
whenAsyncProcessingTakesPlaceWithCustomSpanName();
}
finally {
span.finish();
}
thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOneAndSpanHasCustomName(span);
}
private Span givenASpanInCurrentThread() {
return this.tracer.nextSpan().name("http:existing");
}
private void whenAsyncProcessingTakesPlace() {
this.classPerformingAsyncLogic.invokeAsynchronousLogic();
}
private void whenAsyncProcessingTakesPlaceWithCustomSpanName() {
this.classPerformingAsyncLogic.customNameInvokeAsynchronousLogic();
}
private void thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOne(final Span span) {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getSpan().context().traceId())
.isEqualTo(span.context().traceId());
then(this.spans).hasSize(2);
// HTTP
then(this.spans.get(0).name()).isEqualTo("http:existing");
// ASYNC
then(this.spans.get(1).tags()).containsEntry("class", "ClassPerformingAsyncLogic").containsEntry("method",
"invokeAsynchronousLogic");
});
}
private void thenANewAsyncSpanGetsCreated() {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(this.spans).hasSize(1);
MutableSpan storedSpan = this.spans.get(0);
then(storedSpan.name()).isEqualTo("invoke-asynchronous-logic");
then(storedSpan.tags()).containsEntry("class", "ClassPerformingAsyncLogic").containsEntry("method",
"invokeAsynchronousLogic");
});
}
private void thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOneAndSpanHasCustomName(final Span span) {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getSpan()).isNotNull();
then(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getSpan().context().traceId())
.isEqualTo(span.context().traceId());
then(this.spans).hasSize(2);
// HTTP
then(this.spans.get(0).name()).isEqualTo("http:existing");
// ASYNC
then(this.spans.get(1).tags()).containsEntry("class", "ClassPerformingAsyncLogic").containsEntry("method",
"customNameInvokeAsynchronousLogic");
});
}
private void thenAsyncSpanHasCustomName() {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(this.spans).hasSize(1);
MutableSpan storedSpan = this.spans.get(0);
then(storedSpan.name()).isEqualTo("foo");
then(storedSpan.tags()).containsEntry("class", "ClassPerformingAsyncLogic").containsEntry("method",
"customNameInvokeAsynchronousLogic");
});
}
@AfterEach
public void cleanTrace() {
this.spans.clear();
}
@EnableAutoConfiguration
@EnableAsync
@Configuration(proxyBeanMethods = false)
static class TraceAsyncITestConfiguration {
@Bean
ClassPerformingAsyncLogic asyncClass(Tracer tracer) {
return new ClassPerformingAsyncLogic(tracer);
}
@Bean
Sampler defaultSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
SpanHandler testSpanHandler() {
return new TestSpanHandler();
}
@Bean
Executor fooExecutor() {
return new SimpleAsyncTaskExecutor();
}
@Bean
Executor barExecutor() {
return new SimpleAsyncTaskExecutor();
}
}
static class ClassPerformingAsyncLogic {
private final Tracer tracer;
AtomicReference<Span> span = new AtomicReference<>();
ClassPerformingAsyncLogic(Tracer tracer) {
this.tracer = tracer;
}
@Async("fooExecutor")
public void invokeAsynchronousLogic() {
this.span.set(this.tracer.currentSpan());
}
@Async
@SpanName("foo")
public void customNameInvokeAsynchronousLogic() {
this.span.set(this.tracer.currentSpan());
}
public Span getSpan() {
return this.span.get();
}
public void clear() {
this.span.set(null);
}
}
}
/*
* 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.brave.instrument.web;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import brave.Span;
import brave.Tracer;
import brave.handler.MutableSpan;
import brave.handler.SpanHandler;
import brave.sampler.Sampler;
import brave.test.TestSpanHandler;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.SpanName;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.BDDAssertions.then;
@SpringBootTest(classes = { TraceAsyncIntegrationTests.TraceAsyncITestConfiguration.class })
public class TraceAsyncIntegrationTests {
@Autowired
ClassPerformingAsyncLogic classPerformingAsyncLogic;
@Autowired
Tracer tracer;
@Autowired
TestSpanHandler spans;
@BeforeEach
public void cleanup() {
this.spans.clear();
this.classPerformingAsyncLogic.clear();
}
@Test
public void should_set_span_on_an_async_annotated_method() {
whenAsyncProcessingTakesPlace();
thenANewAsyncSpanGetsCreated();
}
@Test
public void should_set_span_with_custom_method_on_an_async_annotated_method() {
whenAsyncProcessingTakesPlaceWithCustomSpanName();
thenAsyncSpanHasCustomName();
}
@Test
public void should_continue_a_span_on_an_async_annotated_method() {
Span span = givenASpanInCurrentThread();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
whenAsyncProcessingTakesPlace();
}
finally {
span.finish();
}
thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOne(span);
}
@Test
public void should_continue_a_span_with_custom_method_on_an_async_annotated_method() {
Span span = givenASpanInCurrentThread();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
whenAsyncProcessingTakesPlaceWithCustomSpanName();
}
finally {
span.finish();
}
thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOneAndSpanHasCustomName(span);
}
private Span givenASpanInCurrentThread() {
return this.tracer.nextSpan().name("http:existing");
}
private void whenAsyncProcessingTakesPlace() {
this.classPerformingAsyncLogic.invokeAsynchronousLogic();
}
private void whenAsyncProcessingTakesPlaceWithCustomSpanName() {
this.classPerformingAsyncLogic.customNameInvokeAsynchronousLogic();
}
private void thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOne(final Span span) {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getSpan().context().traceId())
.isEqualTo(span.context().traceId());
List<MutableSpan> webSpans = this.spans.spans().stream().filter(mutableSpan -> mutableSpan.traceId().equalsIgnoreCase(span.context().traceIdString()))
.collect(Collectors.toList());
then(webSpans).hasSize(2);
// HTTP
then(webSpans.get(0).name()).isEqualTo("http:existing");
// ASYNC
then(webSpans.get(1).tags()).containsEntry("class", "ClassPerformingAsyncLogic").containsEntry("method",
"invokeAsynchronousLogic");
});
}
private void thenANewAsyncSpanGetsCreated() {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(this.spans).hasSize(1);
MutableSpan storedSpan = this.spans.get(0);
then(storedSpan.name()).isEqualTo("invoke-asynchronous-logic");
then(storedSpan.tags()).containsEntry("class", "ClassPerformingAsyncLogic").containsEntry("method",
"invokeAsynchronousLogic");
});
}
private void thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOneAndSpanHasCustomName(final Span span) {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getSpan()).isNotNull();
then(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getSpan().context().traceId())
.isEqualTo(span.context().traceId());
then(this.spans).hasSize(2);
// HTTP
then(this.spans.get(0).name()).isEqualTo("http:existing");
// ASYNC
then(this.spans.get(1).tags()).containsEntry("class", "ClassPerformingAsyncLogic").containsEntry("method",
"customNameInvokeAsynchronousLogic");
});
}
private void thenAsyncSpanHasCustomName() {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(this.spans).hasSize(1);
MutableSpan storedSpan = this.spans.get(0);
then(storedSpan.name()).isEqualTo("foo");
then(storedSpan.tags()).containsEntry("class", "ClassPerformingAsyncLogic").containsEntry("method",
"customNameInvokeAsynchronousLogic");
});
}
@AfterEach
public void cleanTrace() {
this.spans.clear();
}
@EnableAutoConfiguration
@EnableAsync
@Configuration(proxyBeanMethods = false)
static class TraceAsyncITestConfiguration {
@Bean
ClassPerformingAsyncLogic asyncClass(Tracer tracer) {
return new ClassPerformingAsyncLogic(tracer);
}
@Bean
Sampler defaultSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
SpanHandler testSpanHandler() {
return new TestSpanHandler();
}
@Bean
Executor fooExecutor() {
return new SimpleAsyncTaskExecutor();
}
@Bean
Executor barExecutor() {
return new SimpleAsyncTaskExecutor();
}
}
static class ClassPerformingAsyncLogic {
private final Tracer tracer;
AtomicReference<Span> span = new AtomicReference<>();
ClassPerformingAsyncLogic(Tracer tracer) {
this.tracer = tracer;
}
@Async("fooExecutor")
public void invokeAsynchronousLogic() {
this.span.set(this.tracer.currentSpan());
}
@Async
@SpanName("foo")
public void customNameInvokeAsynchronousLogic() {
this.span.set(this.tracer.currentSpan());
}
public Span getSpan() {
return this.span.get();
}
public void clear() {
this.span.set(null);
}
}
}

View File

@@ -1,102 +1,102 @@
/*
* 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.instrument.r2dbc;
import java.time.Duration;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import io.r2dbc.spi.ConnectionFactory;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.cloud.sleuth.exporter.FinishedSpan;
import org.springframework.cloud.sleuth.test.TestSpanHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.DataAccessException;
import org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer;
import org.springframework.r2dbc.connection.init.ResourceDatabasePopulator;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import static org.assertj.core.api.BDDAssertions.then;
@ContextConfiguration(classes = R2dbcIntegrationTests.TestConfig.class)
@TestPropertySource(properties = { "spring.application.name=MyApplication", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" })
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY)
public abstract class R2dbcIntegrationTests {
@Autowired
TestSpanHandler spans;
@Test
public void should_pass_tracing_information_when_using_r2dbc() {
Set<String> traceIds = this.spans.reportedSpans().stream().map(FinishedSpan::getTraceId)
.collect(Collectors.toSet());
then(traceIds).as("There's one traceid").hasSize(1);
Set<String> spanIds = this.spans.reportedSpans().stream().map(FinishedSpan::getSpanId)
.collect(Collectors.toSet());
// 2 transactions - 9 database interactions
then(spanIds).as("There are 11 spans").hasSize(11);
List<String> spanNames = this.spans.reportedSpans().stream().map(FinishedSpan::getName)
.collect(Collectors.toList());
List<String> remoteServiceNames = this.spans.reportedSpans().stream().map(FinishedSpan::getRemoteServiceName)
.collect(Collectors.toList());
then(spanNames.stream().filter("tx"::equalsIgnoreCase).collect(Collectors.toList())).hasSize(2);
then(remoteServiceNames.stream().filter("h2"::equalsIgnoreCase).collect(Collectors.toList())).hasSize(9);
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@ComponentScan
public static class TestConfig {
private static final Logger log = LoggerFactory.getLogger(TestConfig.class);
@Bean
public CommandLineRunner demo(ReactiveNewTransactionService reactiveNewTransactionService) {
return (args) -> {
try {
reactiveNewTransactionService.newTransaction().block(Duration.ofSeconds(50));
}
catch (DataAccessException e) {
log.info("Expected to throw an exception so that we see if rollback works", e);
}
};
}
@Bean
ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {
ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
initializer.setConnectionFactory(connectionFactory);
initializer.setDatabasePopulator(new ResourceDatabasePopulator(new ClassPathResource("schema.sql")));
return initializer;
}
}
}
/*
* 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.instrument.r2dbc;
import java.time.Duration;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import io.r2dbc.spi.ConnectionFactory;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.cloud.sleuth.exporter.FinishedSpan;
import org.springframework.cloud.sleuth.test.TestSpanHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.DataAccessException;
import org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer;
import org.springframework.r2dbc.connection.init.ResourceDatabasePopulator;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import static org.assertj.core.api.BDDAssertions.then;
@ContextConfiguration(classes = R2dbcIntegrationTests.TestConfig.class)
@TestPropertySource(properties = { "spring.application.name=MyApplication", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" })
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY)
public abstract class R2dbcIntegrationTests {
@Autowired
TestSpanHandler spans;
@Test
public void should_pass_tracing_information_when_using_r2dbc() {
Set<String> traceIds = this.spans.reportedSpans().stream().map(FinishedSpan::getTraceId)
.collect(Collectors.toSet());
then(traceIds).as("There's one traceid").hasSize(1);
Set<String> spanIds = this.spans.reportedSpans().stream().map(FinishedSpan::getSpanId)
.collect(Collectors.toSet());
// 2 transactions - 9 database interactions
then(spanIds).as("There are 11 spans").hasSize(11);
List<String> spanNames = this.spans.reportedSpans().stream().map(FinishedSpan::getName)
.collect(Collectors.toList());
List<String> remoteServiceNames = this.spans.reportedSpans().stream().map(FinishedSpan::getRemoteServiceName)
.collect(Collectors.toList());
then(spanNames.stream().filter("tx"::equalsIgnoreCase).collect(Collectors.toList())).hasSize(2);
then(remoteServiceNames.stream().filter("h2"::equalsIgnoreCase).collect(Collectors.toList())).hasSize(9);
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@ComponentScan
public static class TestConfig {
private static final Logger log = LoggerFactory.getLogger(TestConfig.class);
@Bean
public CommandLineRunner demo(ReactiveNewTransactionService reactiveNewTransactionService) {
return (args) -> {
try {
reactiveNewTransactionService.newTransaction().block(Duration.ofSeconds(50));
}
catch (DataAccessException e) {
log.info("Expected to throw an exception so that we see if rollback works", e);
}
};
}
@Bean
ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {
ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
initializer.setConnectionFactory(connectionFactory);
initializer.setDatabasePopulator(new ResourceDatabasePopulator(new ClassPathResource("schema.sql")));
return initializer;
}
}
}