Merge branch '3.1.x'

This commit is contained in:
spencergibb
2021-07-09 14:44:13 -04:00
369 changed files with 28634 additions and 3891 deletions

View File

@@ -1,24 +1,33 @@
root = true
[*.java]
indent_style = tab
indent_size = 4
continuation_indent_size = 8
[*.groovy]
indent_style = tab
indent_size = 4
continuation_indent_size = 8
[*.xml]
indent_style = tab
indent_size = 4
continuation_indent_size = 8
[*.yml]
indent_style = space
indent_size = 2
[*.yaml]
indent_style = space
indent_size = 2
root = true
[*]
end_of_line = crlf
insert_final_newline = true
[*.java]
indent_style = tab
indent_size = 4
continuation_indent_size = 8
[*.kt]
indent_style = tab
indent_size = 4
continuation_indent_size = 8
[*.groovy]
indent_style = tab
indent_size = 4
continuation_indent_size = 8
[*.xml]
indent_style = tab
indent_size = 4
continuation_indent_size = 8
[*.yml]
indent_style = space
indent_size = 2
[*.yaml]
indent_style = space
indent_size = 2

View File

@@ -1,32 +1,32 @@
name: Build
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
java: ["8", "11", "16"]
steps:
- uses: actions/checkout@v2
- name: Setup java
uses: actions/setup-java@v2
with:
distribution: 'zulu'
java-version: ${{ matrix.java }}
- name: Cache local Maven repository
uses: actions/cache@v2
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Build with Maven
run: ./mvnw clean install -B -U
name: Build
on:
push:
branches: [ 3.1.x ]
pull_request:
branches: [ 3.1.x ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
java: ["8", "11", "16"]
steps:
- uses: actions/checkout@v2
- name: Setup java
uses: actions/setup-java@v2
with:
distribution: 'zulu'
java-version: ${{ matrix.java }}
- name: Cache local Maven repository
uses: actions/cache@v2
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Build with Maven
run: ./mvnw clean install -B -U -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false

File diff suppressed because it is too large Load Diff

View File

@@ -1,82 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2013-2018 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ 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.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth</artifactId>
<version>3.0.4-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-sleuth-docs</artifactId>
<packaging>jar</packaging>
<name>Spring Cloud Sleuth Docs</name>
<description>Spring Sleuth Docs</description>
<properties>
<docs.main>spring-cloud-sleuth</docs.main>
<main.basedir>${basedir}/..</main.basedir>
<configprops.inclusionPattern>spring.sleuth.*|spring.zipkin.*</configprops.inclusionPattern>
<upload-docs-zip.phase>deploy</upload-docs-zip.phase>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/asciidoc</sourceDirectory>
</build>
<profiles>
<profile>
<id>docs</id>
<build>
<plugins>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2013-2018 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ 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.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-sleuth-docs</artifactId>
<packaging>jar</packaging>
<name>Spring Cloud Sleuth Docs</name>
<description>Spring Sleuth Docs</description>
<properties>
<docs.main>spring-cloud-sleuth</docs.main>
<main.basedir>${basedir}/..</main.basedir>
<configprops.inclusionPattern>spring.sleuth.*|spring.zipkin.*</configprops.inclusionPattern>
<upload-docs-zip.phase>deploy</upload-docs-zip.phase>
<!-- Tag generation script -->
<roaster.version>2.22.3.Final</roaster.version>
<module.inclusionPattern>.*/spring-cloud-sleuth-instrumentation/.*|.*/spring-cloud-sleuth-brave/.*</module.inclusionPattern>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.forge.roaster</groupId>
<artifactId>roaster-api</artifactId>
<version>${roaster.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.forge.roaster</groupId>
<artifactId>roaster-jdt</artifactId>
<version>${roaster.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>${build-helper-maven-plugin.version}</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/main/asciidoc</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>docs</id>
<build>
<plugins>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>build-tags-and-spans</id>
<phase>prepare-package</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>org.springframework.cloud.sleuth.documentation.DocsFromSources</mainClass>
<arguments>
<argument>${maven.multiModuleProjectDirectory}</argument>
<argument>${module.inclusionPattern}</argument>
<argument>${maven.multiModuleProjectDirectory}/docs/src/main/asciidoc/</argument>
</arguments>
<includeProjectDependencies>true</includeProjectDependencies>
<includePluginDependencies>true</includePluginDependencies>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -5,20 +5,50 @@
|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 | |
|spring.sleuth.baggage.local-fields | |
|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 | |
|spring.sleuth.baggage.tag-fields | | List of fields that should automatically become tags.
|spring.sleuth.batch.enabled | `true` | Enable Spring Batch instrumentation.
|spring.sleuth.cassandra.enabled | `true` | Enable Cassandra 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 sleuth instrumentation.
|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.enabled | `true` | Should the datasource-proxy tracing be enabled?
|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 | `false` | 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.enable-logging | `false` | Enable logging slow queries to the log.
|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 JDBC instrumentation.
|spring.sleuth.jdbc.excluded-data-source-bean-names | | List of DataSource bean names that will not 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 | `false` | Enables logging JDBC events.
|spring.sleuth.jdbc.p6spy.enabled | `true` | Should the p6spy tracing be enabled?
|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.
@@ -32,12 +62,14 @@
|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).
@@ -45,12 +77,16 @@
|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.session.enabled | `true` | Enable Spring Session instrumentation.
|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.
@@ -59,6 +95,7 @@
|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.

View File

@@ -0,0 +1,627 @@
=== Annotation New Or Continue Span
> Span that wraps a @NewSpan or @ContinueSpan annotations.
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.annotation.SleuthAnnotationSpan`
.Tag Keys
|===
|Name | Description
|class|Class name where a method got annotated with a Sleuth annotation.
|method|Method name that got annotated with Sleuth annotation.
|===
.Event Values
|===
|Name | Description
|%s.after|Annotated after executing a method annotated with @ContinueSpan or @NewSpan. (since the name contains `%s` the final value will be resolved at runtime)
|%s.afterFailure|Annotated after throwing an exception from a method annotated with @ContinueSpan or @NewSpan. (since the name contains `%s` the final value will be resolved at runtime)
|%s.before|Annotated before executing a method annotated with @ContinueSpan or @NewSpan. (since the name contains `%s` the final value will be resolved at runtime)
|===
=== Async Annotation Span
> Span that wraps a @Async annotation. Either continues an existing one or creates a new one if there was no present one.
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.async.SleuthAsyncSpan`
.Tag Keys
|===
|Name | Description
|class|Class name where a method got annotated with @Async.
|method|Method name that got annotated with @Async.
|===
=== Async Callable Span
> Span created whenever a Callable needs to be instrumented.
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.async.SleuthAsyncSpan`
=== Async Runnable Span
> Span created whenever a Runnable needs to be instrumented.
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.async.SleuthAsyncSpan`
=== Batch Job Span
> Span created around a Job execution.
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.batch.SleuthBatchSpan`
.Tag Keys
|===
|Name | Description
|batch.job.executionId|ID of the Spring Batch execution.
|batch.job.instanceId|ID of the Spring Batch job instance.
|batch.job.name|Name of the Spring Batch job.
|===
=== Batch Step Span
> Span created around a Job execution.
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.batch.SleuthBatchSpan`
.Tag Keys
|===
|Name | Description
|batch.job.executionId|ID of the Spring Batch execution.
|batch.step.executionId|ID of the Spring Batch execution.
|batch.step.name|Name of the Spring Batch job.
|batch.step.type|Type of the Spring Batch job.
|===
=== Cassandra Span
> Span created around CqlSession executions.
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.cassandra.SleuthCassandraSpan`
IMPORTANT: All tags and events must be prefixed with `cassandra.` prefix!
.Tag Keys
|===
|Name | Description
|cassandra.cql|A tag containing Cassandra CQL.
|cassandra.keyspace|Name of the Cassandra keyspace.
|cassandra.node[%s].error|A tag containing error that occurred for the given node. (since the name contains `%s` the final value will be resolved at runtime)
|===
.Event Values
|===
|Name | Description
|cassandra.node.error|Set whenever an error occurred for the given node.
|cassandra.node.success|Set when a success occurred for the session processing.
|===
=== Circuit Breaker Function Span
> Span created when we wrap a Function passed to the CircuitBreaker. as fallback.
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.circuitbreaker.SleuthCircuitBreakerSpan`
=== Circuit Breaker Supplier Span
> Span created when we wrap a Supplier passed to the CircuitBreaker.
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.circuitbreaker.SleuthCircuitBreakerSpan`
=== Config Span
> Span created around an EnvironmentRepository.
**Span name** `find`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.config.SleuthConfigSpan`
.Tag Keys
|===
|Name | Description
|config.environment.class|Implementation of the EnvironmentRepository.
|config.environment.method|Method executed on the EnvironmentRepository.
|===
=== Deployer Deploy Span
> Span created upon deploying of an application.
**Span name** `deploy`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.deployer.SleuthDeployerSpan`
.Tag Keys
|===
|Name | Description
|deployer.app.group|Group of the deployed application.
|deployer.app.id|ID of the deployed application.
|deployer.app.name|Name of the deployed application.
|deployer.platform.cf.org|CloudFoundry org.
|deployer.platform.cf.space|CloudFoundry space.
|deployer.platform.cf.url|CloudFoundry API URL.
|deployer.platform.k8s.namespace|Kubernetes namespace.
|deployer.platform.k8s.url|Kubernetes API URL.
|deployer.platform.name|Name of the platform to which apps are being deployed.
|===
.Event Values
|===
|Name | Description
|%s|When deployer changes the state of the deployed application. (since the name contains `%s` the final value will be resolved at runtime)
|deployer.start|When deployer started deploying the application.
|===
=== Deployer Get Log Span
> Span created upon asking for logs of deployed applications.
**Span name** `getLog`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.deployer.SleuthDeployerSpan`
.Tag Keys
|===
|Name | Description
|deployer.app.group|Group of the deployed application.
|deployer.app.id|ID of the deployed application.
|deployer.app.name|Name of the deployed application.
|deployer.platform.cf.org|CloudFoundry org.
|deployer.platform.cf.space|CloudFoundry space.
|deployer.platform.cf.url|CloudFoundry API URL.
|deployer.platform.k8s.namespace|Kubernetes namespace.
|deployer.platform.k8s.url|Kubernetes API URL.
|deployer.platform.name|Name of the platform to which apps are being deployed.
|===
.Event Values
|===
|Name | Description
|%s|When deployer changes the state of the deployed application. (since the name contains `%s` the final value will be resolved at runtime)
|deployer.start|When deployer started deploying the application.
|===
=== Deployer Scale Span
> Span created upon asking for logs of deployed applications.
**Span name** `scale`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.deployer.SleuthDeployerSpan`
.Tag Keys
|===
|Name | Description
|deployer.app.group|Group of the deployed application.
|deployer.app.id|ID of the deployed application.
|deployer.app.name|Name of the deployed application.
|deployer.platform.cf.org|CloudFoundry org.
|deployer.platform.cf.space|CloudFoundry space.
|deployer.platform.cf.url|CloudFoundry API URL.
|deployer.platform.k8s.namespace|Kubernetes namespace.
|deployer.platform.k8s.url|Kubernetes API URL.
|deployer.platform.name|Name of the platform to which apps are being deployed.
|deployer.scale.count|Scale count.
|deployer.scale.deploymentId|Scale command deployment id.
|===
.Event Values
|===
|Name | Description
|%s|When deployer changes the state of the deployed application. (since the name contains `%s` the final value will be resolved at runtime)
|deployer.start|When deployer started deploying the application.
|===
=== Deployer Statuses Span
> Span created upon asking for statuses of deployed applications.
**Span name** `statuses`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.deployer.SleuthDeployerSpan`
.Tag Keys
|===
|Name | Description
|deployer.app.group|Group of the deployed application.
|deployer.app.id|ID of the deployed application.
|deployer.app.name|Name of the deployed application.
|deployer.platform.cf.org|CloudFoundry org.
|deployer.platform.cf.space|CloudFoundry space.
|deployer.platform.cf.url|CloudFoundry API URL.
|deployer.platform.k8s.namespace|Kubernetes namespace.
|deployer.platform.k8s.url|Kubernetes API URL.
|deployer.platform.name|Name of the platform to which apps are being deployed.
|===
.Event Values
|===
|Name | Description
|%s|When deployer changes the state of the deployed application. (since the name contains `%s` the final value will be resolved at runtime)
|deployer.start|When deployer started deploying the application.
|===
=== Deployer Status Span
> Span created upon asking for a status of a deployed application.
**Span name** `status`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.deployer.SleuthDeployerSpan`
.Tag Keys
|===
|Name | Description
|deployer.app.group|Group of the deployed application.
|deployer.app.id|ID of the deployed application.
|deployer.app.name|Name of the deployed application.
|deployer.platform.cf.org|CloudFoundry org.
|deployer.platform.cf.space|CloudFoundry space.
|deployer.platform.cf.url|CloudFoundry API URL.
|deployer.platform.k8s.namespace|Kubernetes namespace.
|deployer.platform.k8s.url|Kubernetes API URL.
|deployer.platform.name|Name of the platform to which apps are being deployed.
|===
.Event Values
|===
|Name | Description
|%s|When deployer changes the state of the deployed application. (since the name contains `%s` the final value will be resolved at runtime)
|deployer.start|When deployer started deploying the application.
|===
=== Deployer Undeploy Span
> Span created upon undeploying of an application.
**Span name** `undeploy`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.deployer.SleuthDeployerSpan`
.Tag Keys
|===
|Name | Description
|deployer.app.group|Group of the deployed application.
|deployer.app.id|ID of the deployed application.
|deployer.app.name|Name of the deployed application.
|deployer.platform.cf.org|CloudFoundry org.
|deployer.platform.cf.space|CloudFoundry space.
|deployer.platform.cf.url|CloudFoundry API URL.
|deployer.platform.k8s.namespace|Kubernetes namespace.
|deployer.platform.k8s.url|Kubernetes API URL.
|deployer.platform.name|Name of the platform to which apps are being deployed.
|===
.Event Values
|===
|Name | Description
|%s|When deployer changes the state of the deployed application. (since the name contains `%s` the final value will be resolved at runtime)
|deployer.start|When deployer started deploying the application.
|===
=== Jdbc Connection Span
> Span created when a JDBC connection takes place.
**Span name** `connection`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.jdbc.SleuthJdbcSpan`
IMPORTANT: All tags and events must be prefixed with `jdbc.` prefix!
.Tag Keys
|===
|Name | Description
|jdbc.datasource.driver|Name of the JDBC datasource driver.
|jdbc.datasource.pool|Name of the JDBC datasource pool.
|===
=== Jdbc Query Span
> Span created when a JDBC query gets executed.
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.jdbc.SleuthJdbcSpan`
IMPORTANT: All tags and events must be prefixed with `jdbc.` prefix!
.Tag Keys
|===
|Name | Description
|jdbc.query|The SQL query value.
|jdbc.row-count|Number of SQL rows.
|===
.Event Values
|===
|Name | Description
|jdbc.commit|When the transaction gets commited.
|jdbc.rollback|When the transaction gets rolled back.
|===
=== Jdbc Result Set Span
> Span created when working with JDBC result set.
**Span name** `result-set`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.jdbc.SleuthJdbcSpan`
IMPORTANT: All tags and events must be prefixed with `jdbc.` prefix!
.Tag Keys
|===
|Name | Description
|jdbc.query|The SQL query value.
|jdbc.row-count|Number of SQL rows.
|===
.Event Values
|===
|Name | Description
|jdbc.commit|When the transaction gets commited.
|jdbc.rollback|When the transaction gets rolled back.
|===
=== Kafka Consumer Span
> Span created on the Kafka consumer side.
**Span name** `kafka.consume`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.kafka.SleuthKafkaSpan`
IMPORTANT: All tags and events must be prefixed with `kafka.` prefix!
.Tag Keys
|===
|Name | Description
|kafka.offset|Kafka offset number.
|kafka.partition|Kafka partition number.
|kafka.topic|Name of the Kafka topic.
|===
=== Kafka Producer Span
> Span created on the Kafka consumer side.
**Span name** `kafka.produce`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.kafka.SleuthKafkaSpan`
IMPORTANT: All tags and events must be prefixed with `kafka.` prefix!
.Tag Keys
|===
|Name | Description
|kafka.topic|Name of the Kafka topic.
|===
=== Messaging Span
> Span created when message is sent or received.
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.messaging.SleuthMessagingSpan`
.Tag Keys
|===
|Name | Description
|%s|User provided keys via customization options. (since the name contains `%s` the final value will be resolved at runtime)
|channel|Name of the Spring Integration channel.
|===
=== Mvc Handler Interceptor Span
> Span around a HandlerInterceptor. Will continue the current span and tag it
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.web.mvc.SleuthMvcSpan`
.Tag Keys
|===
|Name | Description
|mvc.controller.class|Class name where a method got annotated with @Scheduled.
|mvc.controller.method|Method name that got annotated with @Scheduled.
|===
=== Quartz Trigger Span
> Span created when trigger is fired and then completed.
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.quartz.SleuthQuartzSpan`
.Tag Keys
|===
|Name | Description
|quartz.trigger|Name of the trigger.
|===
=== R2dbc Query Span
> Span created on the Kafka consumer side.
**Span name** `query`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.r2dbc.SleuthR2dbcSpan`
IMPORTANT: All tags and events must be prefixed with `r2dbc.` prefix!
.Tag Keys
|===
|Name | Description
|r2dbc.connection|Name of the R2DBC connection.
|r2dbc.query[%s]|Name of the R2DBC query. (since the name contains `%s` the final value will be resolved at runtime)
|r2dbc.thread|Name of the R2DBC thread.
|===
=== Rsocket Requester Span
> Span created on the RSocket responder side.
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.rsocket.SleuthRSocketSpan`
IMPORTANT: All tags and events must be prefixed with `rsocket.` prefix!
.Tag Keys
|===
|Name | Description
|rsocket.request-type|Name of the R2DBC thread.
|rsocket.route|Name of the RSocket route.
|===
=== Rsocket Responder Span
> Span created on the RSocket responder side.
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.rsocket.SleuthRSocketSpan`
=== Rx Java Trace Action Span
> Span that wraps a Rx Java .
**Span name** `rxjava`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.rxjava.SleuthRxJavaSpan`
.Tag Keys
|===
|Name | Description
|thread|Name of the thread.
|===
=== Scheduled Annotation Span
> Span that wraps a annotated method. Either creates a new span or continues an existing one.
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.scheduling.SleuthSchedulingSpan`
.Tag Keys
|===
|Name | Description
|class|Class name where a method got annotated with @Scheduled.
|method|Method name that got annotated with @Scheduled.
|===
=== Session Create Span
> Span created when a new session has to be created.
**Span name** `session.create`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.session.SleuthSessionSpan`
=== Session Delete Span
> Span created when a session is deleted.
**Span name** `session.delete`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.session.SleuthSessionSpan`
=== Session Find Span
> Span created when a new session is searched for.
**Span name** `session.find`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.session.SleuthSessionSpan`
IMPORTANT: All tags and events must be prefixed with `session.` prefix!
.Tag Keys
|===
|Name | Description
|session.index.name|
|===
=== Session Save Span
> Span created when a new session is saved.
**Span name** `session.save`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.session.SleuthSessionSpan`
=== Task Execution Listener Span
> Span created within the lifecycle of a task.
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.task.SleuthTaskSpan`
=== Task Runner Span
> Span created when a task runner is executed.
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.task.SleuthTaskSpan`
=== Tx Span
> Span created when there was no previous transaction. If there was one, we will continue it unless propagation is required.
**Span name** `tx`.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.tx.SleuthTxSpan`
IMPORTANT: All tags and events must be prefixed with `tx.` prefix!
.Tag Keys
|===
|Name | Description
|tx.isolation-level|Transaction isolation level.
|tx.name|Transaction name.
|tx.propagation-level|Transaction propagation level.
|tx.read-only|Whether the transaction is read-only.
|tx.timeout|Transaction timeout.
|tx.transaction-manager|Name of the TransactionManager.
|===
=== Web Filter Span
> Span around a WebFilter. Will continue the current span or create a new one and tag it
**Span name** `%s` - since it contains `%s`, the name is dynamic and will be resolved at runtime.
Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.web.SleuthWebSpan`
.Tag Keys
|===
|Name | Description
|http.status_code|Response status code.
|mvc.controller.class|Name of the class that is processing the request.
|mvc.controller.method|Name of the method that is processing the request.
|===

View File

@@ -1,15 +1,22 @@
:numbered!:
[[appendix]]
[[common-application-properties]]
== Common application properties
include::_attributes.adoc[]
Various properties can be specified inside your `application.properties` file, inside your `application.yml` file, or as command line switches.
This appendix provides a list of common {project-full-name} properties and references to the underlying classes that consume them.
NOTE: Property contributions can come from additional jar files on your classpath, so you should not consider this an exhaustive list.
Also, you can define your own properties.
include::_configprops.adoc[]
:numbered!:
[[appendix]]
[[common-application-properties]]
== Common application properties
include::_attributes.adoc[]
Various properties can be specified inside your `application.properties` file, inside your `application.yml` file, or as command line switches.
This appendix provides a list of common {project-full-name} properties and references to the underlying classes that consume them.
NOTE: Property contributions can come from additional jar files on your classpath, so you should not consider this an exhaustive list.
Also, you can define your own properties.
include::_configprops.adoc[]
[[sleuth-spans]]
== Spring Cloud Sleuth Spans
Below you can find a list of all the spans that are created by Spring Cloud Sleuth.
include::_spans.adoc[]

View File

@@ -77,6 +77,7 @@ Need more details about {project-full-name}'s core features?
Finally, we have topics related to instrumentation integrations:
* *Integrations:*
<<integrations.adoc#sleuth-kafka-integration, Apache Kafka>> |
<<integrations.adoc#sleuth-async-integration, Asynchronous Communication>> |
<<integrations.adoc#sleuth-http-client-integration, HTTP Client Integration>> |
<<integrations.adoc#sleuth-http-server-integration, HTTP Server Integration>> |
@@ -91,4 +92,4 @@ Finally, we have topics related to instrumentation integrations:
<<integrations.adoc#sleuth-runnablecallable-integration, Runnable and Callable>> |
<<integrations.adoc#sleuth-rxjava-integration, RxJava>> |
<<integrations.adoc#sleuth-circuitbreaker-integration, Spring Cloud CircuitBreaker>> |
<<integrations.adoc#sleuth-quartz-integration, Quartz>>
<<integrations.adoc#sleuth-quartz-integration, Quartz>>

View File

@@ -1 +0,0 @@
spring-cloud-sleuth.adoc

View File

@@ -0,0 +1 @@
include::spring-cloud-sleuth.adoc[]

View File

@@ -1 +0,0 @@
spring-cloud-sleuth.adoc

View File

@@ -1 +0,0 @@
spring-cloud-sleuth.htmlsingleadoc

View File

@@ -1 +0,0 @@
spring-cloud-sleuth.pdfadoc

View File

@@ -3,7 +3,26 @@
include::_attributes.adoc[]
In this section, we describe how to customize various parts of Spring Cloud Sleuth.
In this section, we describe how to customize various parts of Spring Cloud Sleuth. Please check the <<appendix.adoc#sleuth-spans,appendix>> for the list of spans, tags and events.
[[sleuth-kafka-integration]]
== Apache Kafka
This feature is available for all tracer implementations.
We decorate the Kafka clients (`KafkaProducer` and `KafkaConsumer`) to create a span for each event that is produced or consumed. You can disable this feature by setting the value of `spring.sleuth.kafka.enabled` to `false`.
IMPORTANT: You have to register the `Producer` or `Consumer` as beans in order for Sleuth's auto-configuration to decorate them. When you then inject the beans, the expected type must be `Producer` or `Consumer` (and NOT e.g. `KafkaProducer`).
We also provide `TracingKafkaProducerFactory` and `TracingKafkaConsumerFactory` to be used with the https://projectreactor.io/docs/kafka/release/reference/[Reactor Kafka] clients (`KafkaSender` and `KafkaReceiver`, respectively). See an example in the snippet below:
[source,java,indent=0]
----
@Bean
KafkaReceiver<String, String> reactiveKafkaReceiver(TracingKafkaConsumerFactory tracingKafkaConsumerFactory, KafkaReceiverOptions kafkaReceiverOptions) {
return KafkaReceiver.create(tracingKafkaConsumerFactory, kafkaReceiverOptions);
}
----
[[sleuth-async-integration]]
== Asynchronous Communication
@@ -579,5 +598,144 @@ IMPORTANT: The suggested approach to reactive programming and Sleuth is to use t
This feature is available for all tracer implementations.
If you have Spring Cloud CircuitBreaker on the classpath, we will wrap the passed command `Supplier` and the fallback `Function` in its trace representations.
In order to disable this instrumentation set `spring.sleuth.circuitbreaker.enabled` to `false`.
If you have Spring Cloud CircuitBreaker on the classpath, we will wrap the passed command `Supplier` and the fallback `Function` in its trace representations. We will also instrument the reactive implementation of the CircuitBreaker.
In order to disable this instrumentation set `spring.sleuth.circuitbreaker.enabled` to `false`.
[[sleuth-config-server-integration]]
== Spring Cloud Config Server
This feature is available for all tracer implementations.
If you have Spring Cloud Config Server running on the classpath, we will wrap the `EnvironmentRepository` in a span.
In order to disable this instrumentation set `spring.sleuth.config.server.enabled` to `false`.
[[sleuth-deployer-integration]]
== Spring Cloud Deployer
This feature is available for all tracer implementations.
If you have Spring Cloud Deployer running on the classpath, we wrap the `AppDeployer` in a trace representation. We are polling the application for its status at a default interval. You can change that default by setting the `spring.sleuth.deployer.status-poll-delay` property.
In order to disable this instrumentation set `spring.sleuth.deployer.enabled` to `false`.
[[sleuth-rsocket-integration]]
== Spring RSocket
This feature is available for all tracer implementations.
If you have Spring RSocket running on the classpath, we wrap the inbound and outbound communication to propagate the tracing context via the metadata.
In order to disable this instrumentation set `spring.sleuth.rsocket.enabled` to `false`.
[[sleuth-batch-integration]]
== Spring Batch
This feature is available for all tracer implementations.
If you have Spring Batch running on the classpath, we wrap the `StepBuilderFactory` and the `JobBuilderFactory` to propagate the tracing context.
In order to disable this instrumentation set `spring.sleuth.batch.enabled` to `false`.
[[sleuth-tx-integration]]
== Spring Tx
This feature is available for all tracer implementations.
If you have Spring Tx on the classpath we will instrument the `PlatformTransactionManager` and the `ReactiveTransactionManager` to create a span whenever a new transaction is created.
In order to disable this instrumentation set `spring.sleuth.tx.enabled` to `false`.
[[sleuth-r2dbc-integration]]
== R2DBC
This feature is available for all tracer implementations.
If you have R2DBC Proxy on the classpath we will instrument the `ConnectionFactory`so that it contains a custom `ProxyExecutionListener`.
In order to disable this instrumentation set `spring.sleuth.r2dbc.enabled` to `false`.
[[sleuth-vault-integration]]
== Spring Vault
This feature is available for all tracer implementations.
We're instrumenting the `RestTemplate` or `WebClient` instances used by Spring Vault to communicate with Vault.
In order to disable this instrumentation set `spring.sleuth.vault.enabled` to `false`.
[[sleuth-tomcat-integration]]
== Spring Tomcat
This feature is available for all tracer implementations.
We're adding an instrumented Tomcat's `Valve` that originates the span.
In order to disable this instrumentation set `spring.sleuth.web.tomcat.enabled` to `false`.
[[sleuth-cassandra-integration]]
== Spring Data Cassandra
This feature is available for all tracer implementations.
We're instrumenting Casandra's `CqlSession` and `ReactiveSession` interfaces and we're providing our own implementation of the `RequestTracker`.
In order to disable this instrumentation set `spring.sleuth.cassandra.enabled` to `false`.
[[sleuth-jdbc-integration]]
== Spring JDBC
This feature is available for all tracer implementations. It has been ported from the https://github.com/gavlyukovskiy/spring-boot-data-source-decorator/[spring-boot-datasource-decorator] project.
We're decorating `DataSource`s in a trace representation. We delegate actual proxying to either https://github.com/p6spy/p6spy[p6spy] or https://github.com/ttddyy/datasource-proxy[datasource-proxy]. In order to use this feature you need to have them on the classpath.
====
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.P6Spy Maven
----
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>${p6spy.version}</version>
<scope>runtime</scope>
</dependency>
----
[source,yml,indent=0,subs="verbatim,attributes",role="secondary"]
.P6Spy Gradle
----
runtimeOnly "p6spy:p6spy:${p6spyVersion}"
----
[source,xml,indent=0,subs="verbatim,attributes",role="secondary"]
.Datasource Proxy Maven
----
<dependency>
<groupId>net.ttddyy</groupId>
<artifactId>datasource-proxy</artifactId>
<version>${datasource-proxy.version}</version>
<scope>runtime</scope>
</dependency>
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Datasource Proxy Gradle
----
runtimeOnly "net.ttddyy:datasource-proxy:${datasourceProxyVersion}"
----
====
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].
By default logging queries will be disabled, set `spring.sleuth.jdbc.datasource-proxy.slow-query.enable-logging` to `true` to enable logging slow queries
and set `spring.sleuth.jdbc.datasource-proxy.query.enable-logging` to `true` to enable logging all queries.
In order to disable this instrumentation set `spring.sleuth.jdbc.enabled` to `false`.
[[sleuth-session-integration]]
== Spring Session
This feature is available for all tracer implementations.
We're instrumenting the `Session` repositories that wraps all operations in a span.
In order to disable this instrumentation set `spring.sleuth.session.enabled` to `false`.
[[sleuth-kotlin-integration]]
== Kotlin Coroutines
This feature is available for all tracer implementations.
We're adding Kotlin Coroutines that allow you to retrieve the current span via the `Tracer` bean. You can either pass the bean to the Kotlin Coroutine context via `Tracer.asContextElement()` method execution or if you have Reactor Kotlin Coroutine integration on the classpath, we will retrieve it from Reactor's context. To retrieve the current span you can call the `currentSpan()` method within the Kotlin Coroutine.

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,18 @@
[[spring-cloud-sleuth-reference-documentation]]
= Spring Cloud Sleuth Reference Documentation
Adrian Cole, Spencer Gibb, Marcin Grzejszczak, Dave Syer, Jay Bryant
:docinfo: shared
include::_attributes.adoc[]
The reference documentation consists of the following sections:
[horizontal]
<<legal.adoc#legal,Legal>> :: Legal information.
<<documentation-overview.adoc#sleuth-documentation-about,Documentation Overview>> :: About the Documentation, Getting Help, First Steps, and more.
<<getting-started.adoc#getting-started,Getting Started>> :: Introducing {project-full-name}, Developing Your First {project-full-name}-based Application
<<using.adoc#using,Using {project-full-name}>> :: {project-full-name} usage examples and workflows.
<<project-features.adoc#features,{project-full-name} Features>> :: Span creation, context propagation, and more.
<<howto.adoc#howto,"`How-to`" Guides>> :: Add sampling, propagate remote tags, and more.
<<integrations.adoc#sleuth-integration,{project-full-name} Integrations>> :: Instrumentation configuration, context propagation, and more.
<<appendix.adoc#appendix,Appendices>> :: Configuration properties.
[[spring-cloud-sleuth-reference-documentation]]
= Spring Cloud Sleuth Reference Documentation
Adrian Cole, Spencer Gibb, Marcin Grzejszczak, Dave Syer, Jay Bryant
:docinfo: shared
include::_attributes.adoc[]
The reference documentation consists of the following sections:
[horizontal]
<<legal.adoc#legal,Legal>> :: Legal information.
<<documentation-overview.adoc#sleuth-documentation-about,Documentation Overview>> :: About the Documentation, Getting Help, First Steps, and more.
<<getting-started.adoc#getting-started,Getting Started>> :: Introducing {project-full-name}, Developing Your First {project-full-name}-based Application
<<using.adoc#using,Using {project-full-name}>> :: {project-full-name} usage examples and workflows.
<<project-features.adoc#features,{project-full-name} Features>> :: Span creation, context propagation, and more.
<<howto.adoc#howto,"`How-to`" Guides>> :: Add sampling, propagate remote tags, and more.
<<integrations.adoc#sleuth-integration,{project-full-name} Integrations>> :: Instrumentation configuration, context propagation, and more.
<<appendix.adoc#appendix,Appendices>> :: Span definitions and configuration properties.

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2013-2020 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.documentation;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.TreeSet;
import java.util.regex.Pattern;
public class DocsFromSources {
private final File projectRoot;
private final Pattern inclusionPattern;
private final File outputDir;
public DocsFromSources(File projectRoot, Pattern inclusionPattern, File outputDir) {
this.projectRoot = projectRoot;
this.inclusionPattern = inclusionPattern;
this.outputDir = outputDir;
}
public static void main(String... args) {
String projectRoot = args[0];
String inclusionPattern = args[1];
inclusionPattern = inclusionPattern.replace("/", File.separator);
String output = args[2];
new DocsFromSources(new File(projectRoot), Pattern.compile(inclusionPattern), new File(output)).generate();
}
public void generate() {
Path path = this.projectRoot.toPath();
System.out.println("Inclusion pattern is [" + this.inclusionPattern + "]");
Collection<SpanEntry> spanEntries = new TreeSet<>();
FileVisitor<Path> fv = new SpanSearchingFileVisitor(this.inclusionPattern, spanEntries);
try {
Files.walkFileTree(path, fv);
Path output = new File(this.outputDir, "_spans.adoc").toPath();
StringBuilder stringBuilder = new StringBuilder();
System.out.println("======================================");
System.out.println("Summary of sources analysis");
System.out.println("Found [" + spanEntries.size() + "] spans");
System.out.println(
"Found [" + spanEntries.stream().flatMap(e -> e.tagKeys.stream()).distinct().count() + "] tags");
System.out.println(
"Found [" + spanEntries.stream().flatMap(e -> e.events.stream()).distinct().count() + "] events");
spanEntries.forEach(spanEntry -> stringBuilder.append(spanEntry.toString()).append("\n\n"));
Files.write(output, stringBuilder.toString().getBytes());
}
catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2013-2020 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.documentation;
import java.util.Objects;
class KeyValueEntry implements Comparable<KeyValueEntry> {
final String name;
final String description;
KeyValueEntry(String name, String description) {
this.name = name;
this.description = description;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
KeyValueEntry tag = (KeyValueEntry) o;
return Objects.equals(name, tag.name) && Objects.equals(description, tag.description);
}
@Override
public int hashCode() {
return Objects.hash(name, description);
}
@Override
public int compareTo(KeyValueEntry o) {
return name.compareTo(o.name);
}
@Override
public String toString() {
return "|" + name + "|" + description();
}
private String description() {
String suffix = "";
if (this.name.contains("%s")) {
suffix = " (since the name contains `%s` the final value will be resolved at runtime)";
}
return description + suffix;
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2013-2020 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.documentation;
import java.util.Arrays;
import java.util.Collection;
import java.util.Locale;
import java.util.Objects;
import java.util.stream.Collectors;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
class SpanEntry implements Comparable<SpanEntry> {
final String name;
final String enclosingClass;
final String enumName;
final String description;
final String prefix;
final Collection<KeyValueEntry> tagKeys;
final Collection<KeyValueEntry> events;
SpanEntry(String name, String enclosingClass, String enumName, String description, String prefix,
Collection<KeyValueEntry> tagKeys, Collection<KeyValueEntry> events) {
Assert.isTrue(StringUtils.hasText(name), "Span name must not be empty");
Assert.isTrue(StringUtils.hasText(description), "Span description must not be empty");
this.name = name;
this.enclosingClass = enclosingClass;
this.enumName = enumName;
this.description = description;
this.prefix = prefix;
this.tagKeys = tagKeys;
this.events = events;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SpanEntry spanEntry = (SpanEntry) o;
return Objects.equals(name, spanEntry.name) && Objects.equals(enclosingClass, spanEntry.enclosingClass)
&& Objects.equals(enumName, spanEntry.enumName) && Objects.equals(description, spanEntry.description)
&& Objects.equals(tagKeys, spanEntry.tagKeys) && Objects.equals(events, spanEntry.events);
}
@Override
public int hashCode() {
return Objects.hash(name, enclosingClass, enumName, description, tagKeys, events);
}
@Override
public int compareTo(SpanEntry o) {
return enumName.compareTo(o.enumName);
}
@Override
//@formatter:off
public String toString() {
StringBuilder text = new StringBuilder()
.append("=== ").append(Arrays.stream(enumName.replace("_", " ").split(" ")).map(s -> StringUtils.capitalize(s.toLowerCase(Locale.ROOT))).collect(Collectors.joining(" ")))
.append("\n\n> ").append(description).append("\n\n")
.append("**Span name** `").append(name).append("`");
if (name.contains("%s")) {
text.append(" - since it contains `%s`, the name is dynamic and will be resolved at runtime.");
}
else {
text.append(".");
}
text.append("\n\n").append("Fully qualified name of the enclosing class `").append(this.enclosingClass).append("`");
if (StringUtils.hasText(prefix)) {
text.append("\n\nIMPORTANT: All tags and events must be prefixed with `").append(this.prefix).append("` prefix!");
}
if (!tagKeys.isEmpty()) {
text.append("\n\n.Tag Keys\n|===\n|Name | Description\n").append(this.tagKeys.stream().map(KeyValueEntry::toString).collect(Collectors.joining("\n")))
.append("\n|===");
}
if (!events.isEmpty()) {
text.append("\n\n.Event Values\n|===\n|Name | Description\n").append(this.events.stream().map(KeyValueEntry::toString).collect(Collectors.joining("\n")))
.append("\n|===");
}
return text.toString();
}
//@formatter:on
}

View File

@@ -0,0 +1,234 @@
/*
* Copyright 2013-2020 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.documentation;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jboss.forge.roaster.Roaster;
import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Expression;
import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.MethodDeclaration;
import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.MethodInvocation;
import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ReturnStatement;
import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.StringLiteral;
import org.jboss.forge.roaster.model.JavaType;
import org.jboss.forge.roaster.model.JavaUnit;
import org.jboss.forge.roaster.model.impl.JavaEnumImpl;
import org.jboss.forge.roaster.model.source.EnumConstantSource;
import org.jboss.forge.roaster.model.source.JavaSource;
import org.jboss.forge.roaster.model.source.MemberSource;
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
import org.springframework.cloud.sleuth.docs.EventValue;
import org.springframework.cloud.sleuth.docs.TagKey;
class SpanSearchingFileVisitor extends SimpleFileVisitor<Path> {
private final Pattern pattern;
private final Collection<SpanEntry> spanEntries;
SpanSearchingFileVisitor(Pattern pattern, Collection<SpanEntry> spanEntries) {
this.pattern = pattern;
this.spanEntries = spanEntries;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (!pattern.matcher(file.toString()).matches()) {
return FileVisitResult.CONTINUE;
}
else if (!file.toString().endsWith(".java")) {
return FileVisitResult.CONTINUE;
}
try (InputStream stream = Files.newInputStream(file)) {
JavaUnit unit = Roaster.parseUnit(stream);
JavaType myClass = unit.getGoverningType();
if (!(myClass instanceof JavaEnumImpl)) {
return FileVisitResult.CONTINUE;
}
JavaEnumImpl myEnum = (JavaEnumImpl) myClass;
if (!myEnum.getInterfaces().contains(DocumentedSpan.class.getCanonicalName())) {
return FileVisitResult.CONTINUE;
}
System.out.println("Checking [" + myEnum.getName() + "]");
if (myEnum.getEnumConstants().size() == 0) {
return FileVisitResult.CONTINUE;
}
for (EnumConstantSource enumConstant : myEnum.getEnumConstants()) {
SpanEntry entry = parseSpan(enumConstant, myEnum);
if (entry != null) {
spanEntries.add(entry);
System.out.println(
"Found [" + entry.tagKeys.size() + "] tags and [" + entry.events.size() + "] events");
}
}
return FileVisitResult.CONTINUE;
}
}
private SpanEntry parseSpan(EnumConstantSource enumConstant, JavaEnumImpl myEnum) {
List<MemberSource<EnumConstantSource.Body, ?>> members = enumConstant.getBody().getMembers();
if (members.isEmpty()) {
return null;
}
String name = "";
String description = enumConstant.getJavaDoc().getText();
String prefix = "";
Collection<KeyValueEntry> tags = new TreeSet<>();
Collection<KeyValueEntry> events = new TreeSet<>();
for (MemberSource<EnumConstantSource.Body, ?> member : members) {
Object internal = member.getInternal();
if (!(internal instanceof MethodDeclaration)) {
return null;
}
MethodDeclaration methodDeclaration = (MethodDeclaration) internal;
String methodName = methodDeclaration.getName().getIdentifier();
if ("getName".equals(methodName)) {
name = readStringReturnValue(methodDeclaration);
}
else if ("getTagKeys".equals(methodName)) {
tags.addAll(keyValueEntries(myEnum, methodDeclaration, TagKey.class));
}
else if ("getEvents".equals(methodName)) {
events.addAll(keyValueEntries(myEnum, methodDeclaration, EventValue.class));
}
else if ("prefix".equals(methodName)) {
prefix = readStringReturnValue(methodDeclaration);
}
}
return new SpanEntry(name, myEnum.getCanonicalName(), enumConstant.getName(), description, prefix, tags,
events);
}
private Collection<KeyValueEntry> keyValueEntries(JavaEnumImpl myEnum, MethodDeclaration methodDeclaration,
Class requiredClass) {
Collection<String> enumNames = readClassValue(methodDeclaration);
Collection<KeyValueEntry> keyValues = new TreeSet<>();
enumNames.forEach(enumName -> {
List<JavaSource<?>> nestedTypes = myEnum.getNestedTypes();
JavaSource<?> nestedSource = nestedTypes.stream()
.filter(javaSource -> javaSource.getName().equals(enumName)).findFirst().orElseThrow(
() -> new IllegalStateException("There's no nested type with name [" + enumName + "]"));
updateKeyValuesFromEnum(myEnum, nestedSource, requiredClass, keyValues);
});
return keyValues;
}
private void updateKeyValuesFromEnum(JavaEnumImpl parentEnum, JavaSource<?> source, Class requiredClass,
Collection<KeyValueEntry> keyValues) {
if (!(source instanceof JavaEnumImpl)) {
return;
}
JavaEnumImpl myEnum = (JavaEnumImpl) source;
if (!myEnum.getInterfaces().contains(requiredClass.getCanonicalName())) {
return;
}
System.out.println("Checking [" + parentEnum.getName() + "." + myEnum.getName() + "]");
if (myEnum.getEnumConstants().size() == 0) {
return;
}
for (EnumConstantSource enumConstant : myEnum.getEnumConstants()) {
String keyValue = enumKeyValue(enumConstant);
keyValues.add(new KeyValueEntry(keyValue, enumConstant.getJavaDoc().getText()));
}
}
private String enumKeyValue(EnumConstantSource enumConstant) {
List<MemberSource<EnumConstantSource.Body, ?>> members = enumConstant.getBody().getMembers();
if (members.isEmpty()) {
System.err.println("No method declarations in the enum.");
return "";
}
Object internal = members.get(0).getInternal();
if (!(internal instanceof MethodDeclaration)) {
System.err.println("Can't read the member [" + internal.getClass() + "] as a method declaration.");
return "";
}
MethodDeclaration methodDeclaration = (MethodDeclaration) internal;
if (methodDeclaration.getBody().statements().isEmpty()) {
System.err.println("Body was empty. Continuing...");
return "";
}
return stringFromReturnMethodDeclaration(methodDeclaration);
}
private String stringFromReturnMethodDeclaration(MethodDeclaration methodDeclaration) {
Object statement = methodDeclaration.getBody().statements().get(0);
if (!(statement instanceof ReturnStatement)) {
System.err.println("Statement [" + statement.getClass() + "] is not a return statement.");
return "";
}
ReturnStatement returnStatement = (ReturnStatement) statement;
Expression expression = returnStatement.getExpression();
if (!(expression instanceof StringLiteral)) {
System.err.println("Statement [" + statement.getClass() + "] is not a string literal statement.");
return "";
}
return ((StringLiteral) expression).getLiteralValue();
}
private String readStringReturnValue(MethodDeclaration methodDeclaration) {
return stringFromReturnMethodDeclaration(methodDeclaration);
}
private Collection<String> readClassValue(MethodDeclaration methodDeclaration) {
Object statement = methodDeclaration.getBody().statements().get(0);
if (!(statement instanceof ReturnStatement)) {
System.err.println("Statement [" + statement.getClass() + "] is not a return statement.");
return Collections.emptyList();
}
ReturnStatement returnStatement = (ReturnStatement) statement;
Expression expression = returnStatement.getExpression();
if (!(expression instanceof MethodInvocation)) {
System.err.println("Statement [" + statement.getClass() + "] is not a method invocation.");
return Collections.emptyList();
}
MethodInvocation methodInvocation = (MethodInvocation) expression;
if ("merge".equals(methodInvocation.getName().getIdentifier())) {
// TODO: There must be a better way to do this...
// TagKey.merge(TestSpanTags.values(),AsyncSpanTags.values())
String invocationString = methodInvocation.toString();
Matcher matcher = Pattern.compile("([a-zA-Z]+.values)").matcher(invocationString);
Collection<String> classNames = new TreeSet<>();
while (matcher.find()) {
String className = matcher.group(1).split("\\.")[0];
classNames.add(className);
}
return classNames;
}
else if (!methodInvocation.toString().endsWith(".values()")) {
throw new IllegalStateException("You have to use the static .values() method on the enum that implements "
+ TagKey.class + " or " + EventValue.class
+ " interface or use [TagKey.merge(...)] method to merge multiple values from tags");
}
// will return Tags
return Collections.singletonList(methodInvocation.getExpression().toString());
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2013-2020 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.documentation;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.regex.Pattern;
import org.assertj.core.api.BDDAssertions;
import org.junit.jupiter.api.Test;
class DocsFromSourcesTests {
@Test
void should_build_a_table_out_of_enum_tag_key() throws IOException {
File root = new File(".");
File output = new File(root, "target");
new DocsFromSources(root, Pattern.compile(".*"), output).generate();
BDDAssertions.then(new String(Files.readAllBytes(new File(output, "_spans.adoc").toPath())))
.contains("=== Async Annotation Span").contains("> Span that wraps a")
.contains("**Span name** `%s` - since").contains("Fully qualified name of")
.contains("|class|Class name where a method got annotated with @Async.")
.contains("=== Annotation New Or Continue Span")
.contains("|%s.before|Annotated before executing a method annotated with @ContinueSpan or @NewSpan.")
.contains("=== Test Span").contains("**Span name** `fixed`.").contains("|foooooo|Test foo");
}
}

View File

@@ -0,0 +1,111 @@
/*
* 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.documentation;
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
import org.springframework.cloud.sleuth.docs.EventValue;
import org.springframework.cloud.sleuth.docs.TagKey;
enum SleuthAnnotationSpan implements DocumentedSpan {
/**
* Span that wraps a @NewSpan or @ContinueSpan annotations.
*/
ANNOTATION_NEW_OR_CONTINUE_SPAN {
@Override
public String getName() {
return "%s";
}
@Override
public TagKey[] getTagKeys() {
return Tags.values();
}
@Override
public EventValue[] getEvents() {
return Events.values();
}
};
/**
* Tags related to Sleuth annotations.
*
* @author Marcin Grzejszczak
* @since 3.0.3
*/
enum Tags implements TagKey {
/**
* Class name where a method got annotated with a Sleuth annotation.
*/
CLASS {
@Override
public String getKey() {
return "class";
}
},
/**
* Method name that got annotated with Sleuth annotation.
*/
METHOD {
@Override
public String getKey() {
return "method";
}
}
}
enum Events implements EventValue {
/**
* Annotated before executing a method annotated with @ContinueSpan or @NewSpan.
*/
BEFORE {
@Override
public String getValue() {
return "%s.before";
}
},
/**
* Annotated after executing a method annotated with @ContinueSpan or @NewSpan.
*/
AFTER {
@Override
public String getValue() {
return "%s.after";
}
},
/**
* Annotated after throwing an exception from a method annotated
* with @ContinueSpan or @NewSpan.
*/
AFTER_FAILURE {
@Override
public String getValue() {
return "%.afterFailure";
}
}
}
}

View File

@@ -0,0 +1,95 @@
/*
* 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.documentation;
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
import org.springframework.cloud.sleuth.docs.TagKey;
enum SleuthAsyncSpan implements DocumentedSpan {
/**
* Span that wraps a @Async annotation. Either continues an existing one or creates a
* new one if there was no present one.
*/
ASYNC_ANNOTATION_SPAN {
@Override
public String getName() {
return "%s";
}
@Override
public TagKey[] getTagKeys() {
return AsyncSpanTags.values();
}
},
/**
* Test span.
*/
TEST_SPAN {
@Override
public String getName() {
return "fixed";
}
@Override
public TagKey[] getTagKeys() {
return TagKey.merge(TestSpanTags.values(), AsyncSpanTags.values());
}
};
enum AsyncSpanTags implements TagKey {
/**
* Class name where a method got annotated with @Async.
*/
CLASS {
@Override
public String getKey() {
return "class";
}
},
/**
* Method name that got annotated with @Async.
*/
METHOD {
@Override
public String getKey() {
return "method";
}
}
}
enum TestSpanTags implements TagKey {
/**
* Test foo
*/
FOO {
@Override
public String getKey() {
return "foooooo";
}
}
}
}

922
pom.xml
View File

@@ -1,428 +1,494 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2013-2018 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ 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.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-sleuth</artifactId>
<version>3.0.4-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Cloud Sleuth</name>
<description>Spring Cloud Sleuth</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>3.0.4-SNAPSHOT</version>
<relativePath/>
<!-- lookup parent from repository -->
</parent>
<scm>
<url>https://github.com/spring-cloud/spring-cloud-sleuth</url>
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-sleuth.git
</connection>
<developerConnection>
scm:git:ssh://git@github.com/spring-cloud/spring-cloud-sleuth.git
</developerConnection>
<tag>HEAD</tag>
</scm>
<modules>
<module>spring-cloud-sleuth-dependencies</module>
<module>spring-cloud-sleuth-api</module>
<module>spring-cloud-sleuth-instrumentation</module>
<module>spring-cloud-sleuth-brave</module>
<module>spring-cloud-sleuth-autoconfigure</module>
<module>tests</module>
<module>spring-cloud-sleuth-zipkin</module>
<module>spring-cloud-starter-sleuth</module>
<module>spring-cloud-sleuth-samples</module>
<module>docs</module>
</modules>
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.testTarget>1.8</maven.compiler.testTarget>
<maven.compiler.testSource>1.8</maven.compiler.testSource>
<spring-cloud-build.version>3.0.4-SNAPSHOT</spring-cloud-build.version>
<spring-cloud-commons.version>3.0.4-SNAPSHOT</spring-cloud-commons.version>
<spring-cloud-gateway.version>3.0.4-SNAPSHOT</spring-cloud-gateway.version>
<spring-cloud-circuitbreaker.version>2.0.3-SNAPSHOT</spring-cloud-circuitbreaker.version>
<spring-cloud-stream.version>3.1.4-SNAPSHOT</spring-cloud-stream.version>
<spring-cloud-function.version>3.1.4-SNAPSHOT</spring-cloud-function.version>
<spring-cloud-netflix.version>3.0.4-SNAPSHOT</spring-cloud-netflix.version>
<spring-cloud-openfeign.version>3.0.4-SNAPSHOT</spring-cloud-openfeign.version>
<brave.version>5.13.2</brave.version>
<opentracing.version>0.32.0</opentracing.version>
<spring-security-boot-autoconfigure.version>2.3.4.RELEASE</spring-security-boot-autoconfigure.version>
<disable.nohttp.checks>false</disable.nohttp.checks>
<okhttp.version>4.9.0</okhttp.version>
<mockwebserver.version>4.8.0</mockwebserver.version>
<guava.version>20.0</guava.version>
<javax.resource-api.version>1.7.1</javax.resource-api.version>
<cglib-nodep.version>3.3.0</cglib-nodep.version>
<objenesis.version>3.0.1</objenesis.version>
<spring-security-oauth2.version>2.2.0.RELEASE</spring-security-oauth2.version>
<!-- Until we switch it to true in sc-build -->
<javadoc.failOnError>true</javadoc.failOnError>
<javadoc.failOnWarnings>false</javadoc.failOnWarnings>
<commons-lang3.version>3.8.1</commons-lang3.version>
<hamcrest-core.version>2.2</hamcrest-core.version>
<awaitility.version>4.0.3</awaitility.version>
<brave-propagation-aws.version>0.21.3</brave-propagation-aws.version>
<archunit-junit5.version>0.14.1</archunit-junit5.version>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<executions>
<execution>
<id>default-compile</id>
<configuration>
<showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings>
<compilerArguments>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</compilerArguments>
</configuration>
</execution>
<execution>
<id>default-testCompile</id>
<configuration>
<showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings>
<compilerArguments>
<source>${maven.compiler.testSource}</source>
<target>${maven.compiler.testTarget}</target>
</compilerArguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>
<executions>
<execution>
<id>enforce-java</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireJavaVersion>
<version>${maven.compiler.testTarget}</version>
</requireJavaVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>io.spring.javaformat</groupId>
<artifactId>spring-javaformat-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-surefire-report-plugin</artifactId>
</plugin>
</plugins>
</reporting>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-dependencies</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-dependencies</artifactId>
<version>${spring-cloud-netflix.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons-dependencies</artifactId>
<version>${spring-cloud-commons.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gateway-dependencies</artifactId>
<version>${spring-cloud-gateway.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-circuitbreaker-dependencies</artifactId>
<version>${spring-cloud-circuitbreaker.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-dependencies</artifactId>
<version>${spring-cloud-stream.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-dependencies</artifactId>
<version>${spring-cloud-function.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-dependencies</artifactId>
<version>${spring-cloud-openfeign.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>${spring-security-boot-autoconfigure.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>${cglib-nodep.version}</version>
</dependency>
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>${objenesis.version}</version>
<!-- not test because we need it in stream -->
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<version>${mockwebserver.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>${spring-security-oauth2.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.aws</groupId>
<artifactId>brave-propagation-aws</artifactId>
<version>${brave-propagation-aws.version}</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>${hamcrest-core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>${awaitility.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5</artifactId>
<version>${archunit-junit5.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<profiles>
<profile>
<id>spring</id>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<!-- BRAVE -->
<repository>
<id>jfrog-snapshots</id>
<name>JFrog Snapshots</name>
<url>https://oss.jfrog.org/oss-snapshot-local/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
<profile>
<id>ide</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.testSource}</source>
<target>${maven.compiler.testTarget}</target>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>benchmarks</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<modules>
<module>benchmarks</module>
</modules>
</profile>
<profile>
<id>sonar</id>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>pre-unit-test</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<propertyName>surefireArgLine</propertyName>
<destFile>${project.build.directory}/jacoco.exec
</destFile>
</configuration>
</execution>
<execution>
<id>post-unit-test</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<!-- Sets the path to the file which contains the execution data. -->
<dataFile>${project.build.directory}/jacoco.exec
</dataFile>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!-- Sets the VM argument line used when unit tests are run. -->
<argLine>${surefireArgLine}</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2013-2018 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ 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.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-sleuth</artifactId>
<version>3.1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Cloud Sleuth</name>
<description>Spring Cloud Sleuth</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>3.1.0-SNAPSHOT</version>
<relativePath/>
<!-- lookup parent from repository -->
</parent>
<scm>
<url>https://github.com/spring-cloud/spring-cloud-sleuth</url>
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-sleuth.git
</connection>
<developerConnection>
scm:git:ssh://git@github.com/spring-cloud/spring-cloud-sleuth.git
</developerConnection>
<tag>HEAD</tag>
</scm>
<modules>
<module>spring-cloud-sleuth-dependencies</module>
<module>spring-cloud-sleuth-api</module>
<module>spring-cloud-sleuth-instrumentation</module>
<module>spring-cloud-sleuth-brave</module>
<module>spring-cloud-sleuth-autoconfigure</module>
<module>tests</module>
<module>spring-cloud-sleuth-zipkin</module>
<module>spring-cloud-starter-sleuth</module>
<module>spring-cloud-sleuth-samples</module>
<module>docs</module>
</modules>
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.testTarget>1.8</maven.compiler.testTarget>
<maven.compiler.testSource>1.8</maven.compiler.testSource>
<spring-cloud-build.version>3.1.0-SNAPSHOT</spring-cloud-build.version>
<spring-cloud-commons.version>3.0.4-SNAPSHOT</spring-cloud-commons.version>
<spring-cloud-gateway.version>3.0.4-SNAPSHOT</spring-cloud-gateway.version>
<spring-cloud-config.version>3.0.4-SNAPSHOT</spring-cloud-config.version>
<spring-cloud-circuitbreaker.version>2.0.3-SNAPSHOT</spring-cloud-circuitbreaker.version>
<spring-cloud-stream.version>3.1.4-SNAPSHOT</spring-cloud-stream.version>
<spring-cloud-function.version>3.1.4-SNAPSHOT</spring-cloud-function.version>
<spring-cloud-netflix.version>3.0.4-SNAPSHOT</spring-cloud-netflix.version>
<spring-cloud-openfeign.version>3.0.4-SNAPSHOT</spring-cloud-openfeign.version>
<spring-cloud-task.version>2.3.2-SNAPSHOT</spring-cloud-task.version>
<spring-cloud-deployer.version>2.5.1</spring-cloud-deployer.version>
<brave.version>5.13.2</brave.version>
<opentracing.version>0.32.0</opentracing.version>
<spring-security-boot-autoconfigure.version>2.3.4.RELEASE</spring-security-boot-autoconfigure.version>
<disable.nohttp.checks>false</disable.nohttp.checks>
<okhttp.version>4.9.0</okhttp.version>
<mockwebserver.version>4.8.0</mockwebserver.version>
<guava.version>20.0</guava.version>
<javax.resource-api.version>1.7.1</javax.resource-api.version>
<cglib-nodep.version>3.3.0</cglib-nodep.version>
<objenesis.version>3.0.1</objenesis.version>
<spring-security-oauth2.version>2.2.0.RELEASE</spring-security-oauth2.version>
<p6spy.version>3.9.1</p6spy.version>
<datasource-proxy.version>1.7</datasource-proxy.version>
<tomcat-jdbc.version>10.0.6</tomcat-jdbc.version>
<commons-dbcp2.version>2.8.0</commons-dbcp2.version>
<kotlin.version>1.5.10</kotlin.version>
<!-- Until we switch it to true in sc-build -->
<javadoc.failOnError>true</javadoc.failOnError>
<javadoc.failOnWarnings>false</javadoc.failOnWarnings>
<commons-lang3.version>3.8.1</commons-lang3.version>
<hamcrest-core.version>2.2</hamcrest-core.version>
<awaitility.version>4.0.3</awaitility.version>
<brave-propagation-aws.version>0.21.3</brave-propagation-aws.version>
<archunit-junit5.version>0.14.1</archunit-junit5.version>
<testcontainers.version>1.15.3</testcontainers.version>
<build-helper-maven-plugin.version>3.2.0</build-helper-maven-plugin.version>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<executions>
<execution>
<id>default-compile</id>
<configuration>
<showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings>
<compilerArguments>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</compilerArguments>
</configuration>
</execution>
<execution>
<id>default-testCompile</id>
<configuration>
<showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings>
<compilerArguments>
<source>${maven.compiler.testSource}</source>
<target>${maven.compiler.testTarget}</target>
</compilerArguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>
<executions>
<execution>
<id>enforce-java</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireJavaVersion>
<version>${maven.compiler.testTarget}</version>
</requireJavaVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<spring.cloud.sleuth.assertions.enabled>true</spring.cloud.sleuth.assertions.enabled>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>io.spring.javaformat</groupId>
<artifactId>spring-javaformat-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-surefire-report-plugin</artifactId>
</plugin>
</plugins>
</reporting>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-dependencies</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-dependencies</artifactId>
<version>${spring-cloud-netflix.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons-dependencies</artifactId>
<version>${spring-cloud-commons.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gateway-dependencies</artifactId>
<version>${spring-cloud-gateway.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-circuitbreaker-dependencies</artifactId>
<version>${spring-cloud-circuitbreaker.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-dependencies</artifactId>
<version>${spring-cloud-stream.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-dependencies</artifactId>
<version>${spring-cloud-function.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-dependencies</artifactId>
<version>${spring-cloud-openfeign.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-dependencies</artifactId>
<version>${spring-cloud-config.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-task-dependencies</artifactId>
<version>${spring-cloud-task.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-deployer-dependencies</artifactId>
<version>${spring-cloud-deployer.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>${spring-security-boot-autoconfigure.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>${cglib-nodep.version}</version>
</dependency>
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>${objenesis.version}</version>
<!-- not test because we need it in stream -->
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<version>${mockwebserver.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>${spring-security-oauth2.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.aws</groupId>
<artifactId>brave-propagation-aws</artifactId>
<version>${brave-propagation-aws.version}</version>
</dependency>
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>${p6spy.version}</version>
</dependency>
<dependency>
<groupId>net.ttddyy</groupId>
<artifactId>datasource-proxy</artifactId>
<version>${datasource-proxy.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>${commons-dbcp2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
<version>${tomcat-jdbc.version}</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>${hamcrest-core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>${awaitility.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5</artifactId>
<version>${archunit-junit5.version}</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-bom</artifactId>
<version>${testcontainers.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<profiles>
<profile>
<id>spring</id>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<!-- BRAVE -->
<repository>
<id>jfrog-snapshots</id>
<name>JFrog Snapshots</name>
<url>https://oss.jfrog.org/oss-snapshot-local/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
<profile>
<id>ide</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.testSource}</source>
<target>${maven.compiler.testTarget}</target>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>benchmarks</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<modules>
<module>benchmarks</module>
</modules>
</profile>
<profile>
<id>sonar</id>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>pre-unit-test</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<propertyName>surefireArgLine</propertyName>
<destFile>${project.build.directory}/jacoco.exec
</destFile>
</configuration>
</execution>
<execution>
<id>post-unit-test</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<!-- Sets the path to the file which contains the execution data. -->
<dataFile>${project.build.directory}/jacoco.exec
</dataFile>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!-- Sets the VM argument line used when unit tests are run. -->
<argLine>${surefireArgLine}</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -28,7 +28,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth</artifactId>
<version>3.0.4-SNAPSHOT</version>
<version>3.1.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -99,6 +99,17 @@ public interface Span extends SpanCustomizer {
return this;
}
/**
* Sets the remote url on the span.
* @param ip remote ip
* @param port remote port
* @return this span
* @since 3.1.0
*/
default Span remoteIpAndPort(String ip, int port) {
return this;
}
/**
* Type of span. Can be used to specify additional relationships between spans in
* addition to a parent/child relationship.
@@ -202,6 +213,16 @@ public interface Span extends SpanCustomizer {
*/
Builder remoteServiceName(String remoteServiceName);
/**
* Sets the remote URL for the span.
* @param ip remote service ip
* @param port remote service port
* @return this
*/
default Builder remoteIpAndPort(String ip, int port) {
return this;
}
/**
* Builds and starts the span.
* @return started span

View File

@@ -0,0 +1,65 @@
/*
* 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;
import java.io.Closeable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Container object for {@link Span} and its corresponding {@link Tracer.SpanInScope}.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public class SpanAndScope implements Closeable {
private static final Log log = LogFactory.getLog(SpanAndScope.class);
private final Span span;
private final Tracer.SpanInScope scope;
public SpanAndScope(Span span, Tracer.SpanInScope scope) {
this.span = span;
this.scope = scope;
}
public Span getSpan() {
return this.span;
}
public Tracer.SpanInScope getScope() {
return this.scope;
}
@Override
public String toString() {
return "SpanAndScope{" + "span=" + this.span + '}';
}
@Override
public void close() {
if (log.isTraceEnabled()) {
log.trace("Closing span [" + this.span + "]");
}
this.scope.close();
this.span.end();
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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;
/**
*
* Describes the behaviour of an object that can be tagged.
*
* @author Marcin Grzejszczak
* @since 3.0.3
*/
public interface Taggable {
/**
* Sets a tag.
* @param key tag key
* @param value tag value
* @return this, for chaining
*/
Taggable tag(String key, String value);
}

View File

@@ -0,0 +1,90 @@
/*
* 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;
import java.util.Deque;
import java.util.NoSuchElementException;
import java.util.concurrent.LinkedBlockingDeque;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Represents a {@link Span} stored in thread local.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public class ThreadLocalSpan {
private static final Log log = LogFactory.getLog(ThreadLocalSpan.class);
private final ThreadLocal<SpanAndScope> threadLocalSpan = new ThreadLocal<>();
private final Deque<SpanAndScope> spans = new LinkedBlockingDeque<>();
private final Tracer tracer;
public ThreadLocalSpan(Tracer tracer) {
this.tracer = tracer;
}
/**
* Sets given span and scope.
* @param span - span to be put in scope
*/
public void set(Span span) {
Tracer.SpanInScope spanInScope = this.tracer.withSpan(span);
SpanAndScope newSpanAndScope = new SpanAndScope(span, spanInScope);
SpanAndScope scope = this.threadLocalSpan.get();
if (scope != null) {
this.spans.addFirst(scope);
}
this.threadLocalSpan.set(newSpanAndScope);
}
/**
* @return currently stored span and scope
*/
public SpanAndScope get() {
return this.threadLocalSpan.get();
}
/**
* Removes the current span from thread local and brings back the previous span to the
* current thread local.
*/
public void remove() {
this.threadLocalSpan.remove();
if (this.spans.isEmpty()) {
return;
}
try {
SpanAndScope span = this.spans.removeFirst();
if (log.isDebugEnabled()) {
log.debug("Took span [" + span + "] from thread local");
}
this.threadLocalSpan.set(span);
}
catch (NoSuchElementException ex) {
if (log.isTraceEnabled()) {
log.trace("Failed to remove a span from the queue", ex);
}
}
}
}

View File

@@ -51,4 +51,47 @@ public interface TraceContext {
*/
Boolean sampled();
/**
* Builder for {@link TraceContext}.
*
* @since 3.1.0
*/
interface Builder {
/**
* Sets trace id on the trace context.
* @param traceId trace id
* @return this
*/
TraceContext.Builder traceId(String traceId);
/**
* Sets parent id on the trace context.
* @param parentId parent trace id
* @return this
*/
TraceContext.Builder parentId(String parentId);
/**
* Sets span id on the trace context.
* @param spanId span id
* @return this
*/
TraceContext.Builder spanId(String spanId);
/**
* Sets sampled on the trace context.
* @param sampled if span is sampled
* @return this
*/
TraceContext.Builder sampled(Boolean sampled);
/**
* Builds the trace context.
* @return trace context
*/
TraceContext build();
}
}

View File

@@ -134,6 +134,12 @@ public interface Tracer extends BaggageManager {
*/
Span.Builder spanBuilder();
/**
* Builder for {@link TraceContext}.
* @return a trace context builder
*/
TraceContext.Builder traceContextBuilder();
/**
* Allows to customize the current span in scope.
* @return current span customizer

View File

@@ -0,0 +1,172 @@
/*
* 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.docs;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.TraceContext;
/**
* {@link Span} that performs additional assertions such as allowed name, tag, event
* verification and upon reporting, whether the span had been started in the first place.
*
* You need to turn on assertions via system properties or environment variables to start
* breaking your tests or production code. Check {@link DocumentedSpanAssertions} for more
* information.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public interface AssertingSpan extends Span {
/**
* @return a {@link DocumentedSpan} with span configuration
*/
DocumentedSpan getDocumentedSpan();
/**
* @return wrapped {@link Span}
*/
Span getDelegate();
/**
* @return {@code true} when this span was started
*/
default boolean isStarted() {
return false;
}
@Override
default AssertingSpan tag(String key, String value) {
DocumentedSpanAssertions.assertThatKeyIsValid(key, getDocumentedSpan());
getDelegate().tag(key, value);
return this;
}
/**
* Tags a span via {@link TagKey}.
* @param key tag key
* @param value tag value
* @return this for chaining
*/
default AssertingSpan tag(TagKey key, String value) {
DocumentedSpanAssertions.assertThatKeyIsValid(key, getDocumentedSpan());
getDelegate().tag(key.getKey(), value);
return this;
}
@Override
default AssertingSpan event(String value) {
DocumentedSpanAssertions.assertThatEventIsValid(value, getDocumentedSpan());
getDelegate().event(value);
return this;
}
/**
* Annotates with an event via {@link EventValue}.
* @param value event value
* @return this for chaining
*/
default AssertingSpan event(EventValue value) {
DocumentedSpanAssertions.assertThatEventIsValid(value, getDocumentedSpan());
getDelegate().event(value.getValue());
return this;
}
@Override
default AssertingSpan name(String name) {
DocumentedSpanAssertions.assertThatNameIsValid(name, getDocumentedSpan());
getDelegate().name(name);
return this;
}
@Override
default boolean isNoop() {
return getDelegate().isNoop();
}
@Override
default TraceContext context() {
return getDelegate().context();
}
@Override
default AssertingSpan start() {
getDelegate().start();
return this;
}
@Override
default AssertingSpan error(Throwable throwable) {
getDelegate().error(throwable);
return this;
}
@Override
default void end() {
DocumentedSpanAssertions.assertThatSpanStartedBeforeEnd(this);
getDelegate().end();
}
@Override
default void abandon() {
getDelegate().abandon();
}
@Override
default AssertingSpan remoteServiceName(String remoteServiceName) {
getDelegate().remoteServiceName(remoteServiceName);
return this;
}
@Override
default Span remoteIpAndPort(String ip, int port) {
getDelegate().remoteIpAndPort(ip, port);
return this;
}
/**
* @param documentedSpan span configuration
* @param span span to wrap in assertions
* @return asserting span
*/
static AssertingSpan of(DocumentedSpan documentedSpan, Span span) {
if (span == null) {
return null;
}
else if (span instanceof AssertingSpan) {
return (AssertingSpan) span;
}
return new ImmutableAssertingSpan(documentedSpan, span);
}
/**
* Returns the underlying delegate. Used when casting is necessary.
* @param span span to check for wrapping
* @param <T> type extending a span
* @return unwrapped object
*/
static <T extends Span> T unwrap(Span span) {
if (span == null) {
return null;
}
else if (span instanceof AssertingSpan) {
return (T) ((AssertingSpan) span).getDelegate();
}
return (T) span;
}
}

View File

@@ -0,0 +1,162 @@
/*
* 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.docs;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.TraceContext;
/**
* A {@link Span.Builder} that can perform assertions on itself.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public interface AssertingSpanBuilder extends Span.Builder {
/**
* @return a {@link DocumentedSpan} with span configuration
*/
DocumentedSpan getDocumentedSpan();
/**
* @return wrapped {@link Span.Builder}
*/
Span.Builder getDelegate();
@Override
default AssertingSpanBuilder tag(String key, String value) {
DocumentedSpanAssertions.assertThatKeyIsValid(key, getDocumentedSpan());
getDelegate().tag(key, value);
return this;
}
/**
* Sets a tag on a span.
* @param key tag key
* @param value tag
* @return this, for chaining
*/
default AssertingSpanBuilder tag(TagKey key, String value) {
DocumentedSpanAssertions.assertThatKeyIsValid(key, getDocumentedSpan());
getDelegate().tag(key.getKey(), value);
return this;
}
@Override
default AssertingSpanBuilder event(String value) {
DocumentedSpanAssertions.assertThatEventIsValid(value, getDocumentedSpan());
getDelegate().event(value);
return this;
}
/**
* Sets an event on a span.
* @param value event
* @return this, for chaining
*/
default AssertingSpanBuilder event(EventValue value) {
DocumentedSpanAssertions.assertThatEventIsValid(value, getDocumentedSpan());
getDelegate().event(value.getValue());
return this;
}
@Override
default AssertingSpanBuilder name(String name) {
DocumentedSpanAssertions.assertThatNameIsValid(name, getDocumentedSpan());
getDelegate().name(name);
return this;
}
@Override
default AssertingSpanBuilder error(Throwable throwable) {
getDelegate().error(throwable);
return this;
}
@Override
default AssertingSpanBuilder remoteServiceName(String remoteServiceName) {
getDelegate().remoteServiceName(remoteServiceName);
return this;
}
@Override
default Span.Builder remoteIpAndPort(String ip, int port) {
getDelegate().remoteIpAndPort(ip, port);
return this;
}
@Override
default AssertingSpanBuilder setParent(TraceContext context) {
getDelegate().setParent(context);
return this;
}
@Override
default AssertingSpanBuilder setNoParent() {
getDelegate().setNoParent();
return this;
}
@Override
default AssertingSpanBuilder kind(Span.Kind spanKind) {
getDelegate().kind(spanKind);
return this;
}
@Override
default AssertingSpan start() {
Span span = getDelegate().start();
DocumentedSpan documentedSpan = getDocumentedSpan();
return new AssertingSpan() {
@Override
public DocumentedSpan getDocumentedSpan() {
return documentedSpan;
}
@Override
public Span getDelegate() {
return span;
}
@Override
public boolean isStarted() {
return true;
}
@Override
public String toString() {
return getDelegate().toString();
}
};
}
/**
* @param documentedSpan span configuration
* @param builder builder to wrap in assertions
* @return asserting span builder
*/
static AssertingSpanBuilder of(DocumentedSpan documentedSpan, Span.Builder builder) {
if (builder == null) {
return null;
}
else if (builder instanceof AssertingSpanBuilder) {
return (AssertingSpanBuilder) builder;
}
return new ImmutableAssertingSpanBuilder(documentedSpan, builder);
}
}

View File

@@ -0,0 +1,111 @@
/*
* 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.docs;
import org.springframework.cloud.sleuth.SpanCustomizer;
/**
* A {@link SpanCustomizer} that can perform assertions on itself.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public interface AssertingSpanCustomizer extends SpanCustomizer {
/**
* @return a {@link DocumentedSpan} with span configuration
*/
DocumentedSpan getDocumentedSpan();
/**
* @return wrapped {@link SpanCustomizer}
*/
SpanCustomizer getDelegate();
@Override
default AssertingSpanCustomizer tag(String key, String value) {
DocumentedSpanAssertions.assertThatKeyIsValid(key, getDocumentedSpan());
getDelegate().tag(key, value);
return this;
}
/**
* Sets a tag on a span.
* @param key tag key
* @param value tag
* @return this, for chaining
*/
default AssertingSpanCustomizer tag(TagKey key, String value) {
DocumentedSpanAssertions.assertThatKeyIsValid(key, getDocumentedSpan());
getDelegate().tag(key.getKey(), value);
return this;
}
@Override
default AssertingSpanCustomizer event(String value) {
DocumentedSpanAssertions.assertThatEventIsValid(value, getDocumentedSpan());
getDelegate().event(value);
return this;
}
/**
* Sets an event on a span.
* @param value event
* @return this, for chaining
*/
default AssertingSpanCustomizer event(EventValue value) {
DocumentedSpanAssertions.assertThatEventIsValid(value, getDocumentedSpan());
getDelegate().event(value.getValue());
return this;
}
@Override
default AssertingSpanCustomizer name(String name) {
DocumentedSpanAssertions.assertThatNameIsValid(name, getDocumentedSpan());
getDelegate().name(name);
return this;
}
/**
* @param documentedSpan span configuration
* @param span span to wrap in assertions
* @return asserting span customizer
*/
static AssertingSpanCustomizer of(DocumentedSpan documentedSpan, SpanCustomizer span) {
if (span instanceof AssertingSpanCustomizer) {
return (AssertingSpanCustomizer) span;
}
return new ImmutableAssertingSpanCustomizer(documentedSpan, span);
}
/**
* Returns the underlying delegate. Used when casting is necessary.
* @param span span to check for wrapping
* @param <T> type extending a span
* @return unwrapped object
*/
static <T extends SpanCustomizer> T unwrap(SpanCustomizer span) {
if (span == null) {
return null;
}
else if (span instanceof AssertingSpanCustomizer) {
return (T) ((AssertingSpanCustomizer) span).getDelegate();
}
return (T) span;
}
}

View File

@@ -0,0 +1,116 @@
/*
* 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.docs;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.SpanCustomizer;
/**
* In order to describe your spans via e.g. enums instead of Strings you can use this
* interface that returns all the characteristics of a span. In Spring Cloud Sleuth we
* analyze the sources and reuse this information to build a table of known spans, their
* names, tags and events.
*
* We can generate documentation for all created spans but certain requirements need to be
* met
*
* - spans are grouped within an enum - the enum implements the {@link DocumentedSpan}
* interface - if the span contains {@link TagKey} or {@link EventValue} then those need
* to be declared as nested enums - the {@link DocumentedSpan#getTagKeys()} and
* {@link DocumentedSpan#getEvents()} need to call the nested enum's {@code values()}
* method to retrieve the array of allowed keys / events
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public interface DocumentedSpan {
/**
* @return span name
*/
String getName();
/**
* @return allowed tag keys
*/
default TagKey[] getTagKeys() {
return new TagKey[0];
}
/**
* @return allowed events
*/
default EventValue[] getEvents() {
return new EventValue[0];
}
/**
* Returns required prefix to be there for events and tags. Example {@code foo.} would
* require the tags and events to have a {code foo} prefix like this for tags:
* {@code foo.bar=true} and {@code foo.started} for events.
* @return required prefix
*/
default String prefix() {
return "";
}
/**
* Asserts on tags, names and allowed events.
* @param span to wrap
* @return wrapped span
*/
default AssertingSpan wrap(Span span) {
if (span == null) {
return null;
}
else if (span instanceof AssertingSpan) {
return (AssertingSpan) span;
}
return AssertingSpan.of(this, span);
}
/**
* Asserts on tags, names and allowed events.
* @param span to wrap
* @return wrapped span
*/
default AssertingSpanCustomizer wrap(SpanCustomizer span) {
if (span == null) {
return null;
}
else if (span instanceof AssertingSpanCustomizer) {
return (AssertingSpanCustomizer) span;
}
return AssertingSpanCustomizer.of(this, span);
}
/**
* Asserts on tags, names and allowed events.
* @param span builder to wrap
* @return wrapped span
*/
default AssertingSpanBuilder wrap(Span.Builder span) {
if (span == null) {
return null;
}
else if (span instanceof AssertingSpanBuilder) {
return (AssertingSpanBuilder) span;
}
return AssertingSpanBuilder.of(this, span);
}
}

View File

@@ -0,0 +1,149 @@
/*
* 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.docs;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.springframework.util.StringUtils;
/**
* In order to turn on the assertions you need to either turn on the
* {@code spring.cloud.sleuth.assertions.enabled} system property or
* {@code SPRING_CLOUD_SLEUTH_ASSERTIONS_ENABLED} environment variable.
*/
final class DocumentedSpanAssertions {
static boolean SLEUTH_SPAN_ASSERTIONS_ON = Boolean.parseBoolean(System.getProperty(
"spring.cloud.sleuth.assertions.enabled", System.getenv("SPRING_CLOUD_SLEUTH_ASSERTIONS_ENABLED") != null
? System.getenv("SPRING_CLOUD_SLEUTH_ASSERTIONS_ENABLED") : "false"));
private static final Map<String, Pattern> PATTERN_CACHE = new ConcurrentHashMap<>();
private static final Pattern SPECIAL_REGEX_CHARS = Pattern.compile("[{}()\\[\\].+*?^$\\\\|]");
private DocumentedSpanAssertions() {
throw new IllegalStateException("Can't instantiate utility class");
}
static void assertThatKeyIsValid(String key, DocumentedSpan documentedSpan) {
if (SLEUTH_SPAN_ASSERTIONS_ON) {
TagKey[] allowedKeys = documentedSpan.getTagKeys();
if (allowedKeys.length == 0) {
return;
}
boolean validTagKey = Arrays.stream(allowedKeys)
.anyMatch(tagKey -> patternOrValueMatches(key, tagKey.getKey())
&& hasRequiredPrefix(key, documentedSpan.prefix()));
if (!validTagKey) {
throw new AssertionError("The key [" + key + "] is invalid. You can use only one matching "
+ Arrays.stream(allowedKeys).map(TagKey::getKey).collect(Collectors.toList())
+ prefixWarningIfPresent(documentedSpan));
}
}
}
private static String prefixWarningIfPresent(DocumentedSpan documentedSpan) {
return StringUtils.hasText(documentedSpan.prefix())
? ". Also it has start with [" + documentedSpan.prefix() + "] prefix" : "";
}
static void assertThatKeyIsValid(TagKey key, DocumentedSpan documentedSpan) {
if (SLEUTH_SPAN_ASSERTIONS_ON) {
TagKey[] allowedKeys = documentedSpan.getTagKeys();
if (allowedKeys.length == 0) {
return;
}
if (Arrays.stream(allowedKeys).noneMatch(tagKey -> patternOrValueMatches(key.getKey(), tagKey.getKey())
&& hasRequiredPrefix(key.getKey(), documentedSpan.prefix()))) {
throw new AssertionError("The key [" + key.getKey() + "] is invalid. You can use only one matching "
+ Arrays.stream(allowedKeys).map(TagKey::getKey).collect(Collectors.toList())
+ prefixWarningIfPresent(documentedSpan));
}
}
}
static void assertThatNameIsValid(String name, DocumentedSpan documentedSpan) {
String allowedName = documentedSpan.getName();
if (SLEUTH_SPAN_ASSERTIONS_ON && !patternOrValueMatches(name, allowedName)) {
throw new AssertionError(
"The name [" + name + "] is invalid. You can use only one matching [" + allowedName + "]");
}
}
static void assertThatEventIsValid(String eventValue, DocumentedSpan documentedSpan) {
if (SLEUTH_SPAN_ASSERTIONS_ON) {
EventValue[] allowed = documentedSpan.getEvents();
if (allowed.length == 0) {
return;
}
boolean valid = Arrays.stream(allowed).anyMatch(value -> patternOrValueMatches(eventValue, value.getValue())
&& hasRequiredPrefix(eventValue, documentedSpan.prefix()));
if (!valid) {
throw new AssertionError("The event [" + eventValue + "] is invalid. You can use only one matching "
+ Arrays.stream(allowed).map(EventValue::getValue).collect(Collectors.toList())
+ prefixWarningIfPresent(documentedSpan));
}
}
}
static void assertThatEventIsValid(EventValue eventValue, DocumentedSpan documentedSpan) {
if (SLEUTH_SPAN_ASSERTIONS_ON) {
EventValue[] allowed = documentedSpan.getEvents();
if (allowed.length == 0) {
return;
}
if (Arrays.stream(allowed).noneMatch(value -> patternOrValueMatches(eventValue.getValue(), value.getValue())
&& hasRequiredPrefix(eventValue.getValue(), documentedSpan.prefix()))) {
throw new AssertionError(
"The event [" + eventValue.getValue() + "] is invalid. You can use only one matching "
+ Arrays.stream(allowed).map(EventValue::getValue).collect(Collectors.toList())
+ prefixWarningIfPresent(documentedSpan));
}
}
}
static void assertThatSpanStartedBeforeEnd(AssertingSpan span) {
if (SLEUTH_SPAN_ASSERTIONS_ON && !span.isStarted()) {
throw new AssertionError("The span was not started, however you're trying to end it");
}
}
private static boolean patternOrValueMatches(String pickedValue, String allowedValue) {
if (allowedValue.contains("%s")) {
String stringPattern = escapeSpecialRegexWithSingleEscape(allowedValue).replaceAll("%s", ".*?");
Pattern pattern = PATTERN_CACHE.computeIfAbsent(stringPattern, Pattern::compile);
return pattern.matcher(pickedValue).matches();
}
return allowedValue.equals(pickedValue);
}
private static boolean hasRequiredPrefix(String value, String prefix) {
if (StringUtils.hasText(prefix)) {
return value.startsWith(prefix);
}
return true;
}
private static String escapeSpecialRegexWithSingleEscape(String str) {
return SPECIAL_REGEX_CHARS.matcher(str).replaceAll("\\\\$0");
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.docs;
/**
* Event value representing a notable event in time.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public interface EventValue {
/**
* @return event value
*/
String getValue();
}

View File

@@ -0,0 +1,83 @@
/*
* 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.docs;
import java.util.Objects;
import org.springframework.cloud.sleuth.Span;
import static java.util.Objects.requireNonNull;
class ImmutableAssertingSpan implements AssertingSpan {
private final DocumentedSpan documentedSpan;
private final Span delegate;
boolean isStarted;
ImmutableAssertingSpan(DocumentedSpan documentedSpan, Span delegate) {
requireNonNull(documentedSpan);
requireNonNull(delegate);
this.documentedSpan = documentedSpan;
this.delegate = delegate;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ImmutableAssertingSpan that = (ImmutableAssertingSpan) o;
return Objects.equals(documentedSpan, that.documentedSpan) && Objects.equals(delegate, that.delegate);
}
@Override
public String toString() {
return this.delegate.toString();
}
@Override
public int hashCode() {
return Objects.hash(documentedSpan, delegate);
}
@Override
public DocumentedSpan getDocumentedSpan() {
return this.documentedSpan;
}
@Override
public Span getDelegate() {
return this.delegate;
}
@Override
public AssertingSpan start() {
this.isStarted = true;
return AssertingSpan.super.start();
}
@Override
public boolean isStarted() {
return this.isStarted;
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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.docs;
import java.util.Objects;
import org.springframework.cloud.sleuth.Span;
import static java.util.Objects.requireNonNull;
class ImmutableAssertingSpanBuilder implements AssertingSpanBuilder {
private final DocumentedSpan documentedSpan;
private final Span.Builder delegate;
ImmutableAssertingSpanBuilder(DocumentedSpan documentedSpan, Span.Builder delegate) {
requireNonNull(documentedSpan);
requireNonNull(delegate);
this.documentedSpan = documentedSpan;
this.delegate = delegate;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ImmutableAssertingSpanBuilder that = (ImmutableAssertingSpanBuilder) o;
return Objects.equals(documentedSpan, that.documentedSpan) && Objects.equals(delegate, that.delegate);
}
@Override
public String toString() {
return this.delegate.toString();
}
@Override
public int hashCode() {
return Objects.hash(documentedSpan, delegate);
}
@Override
public DocumentedSpan getDocumentedSpan() {
return this.documentedSpan;
}
@Override
public Span.Builder getDelegate() {
return this.delegate;
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.docs;
import java.util.Objects;
import org.springframework.cloud.sleuth.SpanCustomizer;
import static java.util.Objects.requireNonNull;
class ImmutableAssertingSpanCustomizer implements AssertingSpanCustomizer {
private final DocumentedSpan documentedSpan;
private final SpanCustomizer delegate;
ImmutableAssertingSpanCustomizer(DocumentedSpan documentedSpan, SpanCustomizer delegate) {
requireNonNull(documentedSpan);
requireNonNull(delegate);
this.documentedSpan = documentedSpan;
this.delegate = delegate;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ImmutableAssertingSpanCustomizer that = (ImmutableAssertingSpanCustomizer) o;
return Objects.equals(documentedSpan, that.documentedSpan) && Objects.equals(delegate, that.delegate);
}
@Override
public int hashCode() {
return Objects.hash(documentedSpan, delegate);
}
@Override
public DocumentedSpan getDocumentedSpan() {
return this.documentedSpan;
}
@Override
public SpanCustomizer getDelegate() {
return this.delegate;
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.docs;
import java.util.Arrays;
/**
* Represents a tag key.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public interface TagKey {
/**
* @return tag key
*/
String getKey();
/**
* Merges arrays of tags.
* @param tags array of tags
* @return a merged array of tags
*/
static TagKey[] merge(TagKey[]... tags) {
return Arrays.stream(tags).flatMap(Arrays::stream).toArray(TagKey[]::new);
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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.exporter;
/**
* An interface that allows to process spans after they got finished.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public interface SpanReporter {
/**
* Reports the finished span.
* @param span a span that was ended and is ready to be reported.
*/
void report(FinishedSpan span);
}

View File

@@ -1,100 +0,0 @@
{
"properties": [
{
"name": "spring.sleuth.integration.enabled",
"type": "java.lang.Boolean",
"description": "Enable Spring Integration sleuth instrumentation.",
"defaultValue": true
},
{
"name": "spring.sleuth.integration.websockets.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing for WebSockets.",
"defaultValue": true
},
{
"name": "spring.sleuth.async.enabled",
"type": "java.lang.Boolean",
"description": "Enable instrumenting async related components so that the tracing information is passed between threads.",
"defaultValue": true
},
{
"name": "spring.sleuth.async.configurer.enabled",
"type": "java.lang.Boolean",
"description": "Enable default AsyncConfigurer.",
"defaultValue": true
},
{
"name": "spring.sleuth.feign.enabled",
"type": "java.lang.Boolean",
"description": "Enable span information propagation when using Feign.",
"defaultValue": true
},
{
"name": "spring.sleuth.feign.processor.enabled",
"type": "java.lang.Boolean",
"description": "Enable post processor that wraps Feign Context in its tracing representations.",
"defaultValue": true
},
{
"name": "spring.sleuth.grpc.enabled",
"type": "java.lang.Boolean",
"description": "Enable span information propagation when using GRPC.",
"defaultValue": true
},
{
"name": "spring.sleuth.messaging.jms.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing of JMS.",
"defaultValue": true
},
{
"name": "spring.sleuth.messaging.rabbit.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing of RabbitMQ.",
"defaultValue": true
},
{
"name": "spring.sleuth.messaging.kafka.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing of Kafka.",
"defaultValue": true
},
{
"name": "spring.sleuth.messaging.kafka.mapper.enabled",
"type": "java.lang.Boolean",
"description": "Enable DefaultKafkaHeaderMapper tracing for Kafka.",
"defaultValue": true
},
{
"name": "spring.sleuth.quartz.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing for Quartz.",
"defaultValue": true
},
{
"name": "spring.sleuth.mongodb.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing for MongoDb.",
"defaultValue": true
},
{
"name": "spring.sleuth.rpc.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing of RPC.",
"defaultValue": true
},
{
"name": "spring.sleuth.sampler.refresh.enabled",
"type": "java.lang.Boolean",
"description": "Enable refresh scope for sampler.",
"defaultValue": true
},
{
"name": "spring.sleuth.web.webclient.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing instrumentation for WebClient.",
"defaultValue": true
}
]
}

View File

@@ -0,0 +1,307 @@
/*
* 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.docs;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.BDDMockito;
import org.springframework.cloud.sleuth.Span;
import static org.assertj.core.api.BDDAssertions.thenThrownBy;
import static org.springframework.cloud.sleuth.docs.DocumentedSpanAssertions.assertThatEventIsValid;
import static org.springframework.cloud.sleuth.docs.DocumentedSpanAssertions.assertThatKeyIsValid;
import static org.springframework.cloud.sleuth.docs.DocumentedSpanAssertions.assertThatNameIsValid;
import static org.springframework.cloud.sleuth.docs.DocumentedSpanAssertions.assertThatSpanStartedBeforeEnd;
import static org.springframework.cloud.sleuth.docs.DocumentedSpanAssertionsTests.MyEventsWithNotMatchingPrefix.A_BAR_EVENT;
import static org.springframework.cloud.sleuth.docs.DocumentedSpanAssertionsTests.MySpan.SPAN_WITH_DYNAMIC_ENTRIES;
import static org.springframework.cloud.sleuth.docs.DocumentedSpanAssertionsTests.MySpan.SPAN_WITH_EMPTY_TAGS_AND_EVENTS;
import static org.springframework.cloud.sleuth.docs.DocumentedSpanAssertionsTests.MySpan.SPAN_WITH_NOT_MATCHING_PREFIX;
import static org.springframework.cloud.sleuth.docs.DocumentedSpanAssertionsTests.MySpan.SPAN_WITH_PREFIX;
import static org.springframework.cloud.sleuth.docs.DocumentedSpanAssertionsTests.MyTags.A_FOO_TAG;
class DocumentedSpanAssertionsTests {
@BeforeEach
void setup() {
DocumentedSpanAssertions.SLEUTH_SPAN_ASSERTIONS_ON = true;
}
@Test
void should_do_nothing_when_system_property_not_turned_on() {
DocumentedSpanAssertions.SLEUTH_SPAN_ASSERTIONS_ON = false;
assertThatKeyIsValid("unknown_key", SPAN_WITH_NOT_MATCHING_PREFIX);
assertThatKeyIsValid(A_FOO_TAG, SPAN_WITH_PREFIX);
assertThatEventIsValid("unknown_event", SPAN_WITH_PREFIX);
assertThatEventIsValid(A_BAR_EVENT, SPAN_WITH_PREFIX);
assertThatNameIsValid("unknown_name", SPAN_WITH_NOT_MATCHING_PREFIX);
assertThatSpanStartedBeforeEnd(
new ImmutableAssertingSpan(SPAN_WITH_NOT_MATCHING_PREFIX, BDDMockito.mock(Span.class)));
}
@Test
void should_do_nothing_when_tags_or_events_are_empty() {
assertThatKeyIsValid("unknown_key", SPAN_WITH_EMPTY_TAGS_AND_EVENTS);
assertThatKeyIsValid(A_FOO_TAG, SPAN_WITH_EMPTY_TAGS_AND_EVENTS);
assertThatEventIsValid("unknown_event", SPAN_WITH_EMPTY_TAGS_AND_EVENTS);
assertThatEventIsValid(A_BAR_EVENT, SPAN_WITH_EMPTY_TAGS_AND_EVENTS);
}
@Test
void should_not_fail_when_keys_and_values_are_properly_prefixed() {
assertThatKeyIsValid("some.key", SPAN_WITH_DYNAMIC_ENTRIES);
assertThatKeyIsValid(String.format(MyDynamicTags.A_DYNAMIC_TAG.getKey(), "some"), SPAN_WITH_DYNAMIC_ENTRIES);
assertThatEventIsValid("some.value", SPAN_WITH_DYNAMIC_ENTRIES);
assertThatEventIsValid(String.format(MyDynamicEvents.A_DYNAMIC_EVENT.getValue(), "some"),
SPAN_WITH_DYNAMIC_ENTRIES);
}
@Test
void should_not_fail_when_span_was_started_and_then_ended() {
assertThatSpanStartedBeforeEnd(
new ImmutableAssertingSpan(SPAN_WITH_NOT_MATCHING_PREFIX, BDDMockito.mock(Span.class)).start());
}
@Test
void should_fail_when_assertion_is_on_and_a_key_is_unknown() {
thenThrownBy(() -> assertThatKeyIsValid("unknown_key", SPAN_WITH_NOT_MATCHING_PREFIX))
.hasMessageContaining("The key [unknown_key] is invalid");
thenThrownBy(() -> assertThatKeyIsValid(A_FOO_TAG, SPAN_WITH_NOT_MATCHING_PREFIX))
.hasMessageContaining("The key [foo.key] is invalid");
}
@Test
void should_fail_when_assertion_is_on_and_an_event_is_unknown() {
thenThrownBy(() -> assertThatEventIsValid("unknown_event", SPAN_WITH_PREFIX))
.hasMessageContaining("The event [unknown_event] is invalid");
thenThrownBy(() -> assertThatEventIsValid(A_BAR_EVENT, SPAN_WITH_PREFIX))
.hasMessageContaining("The event [bar.value] is invalid");
}
@Test
void should_fail_when_assertion_is_on_and_a_key_is_known_but_wrongly_prefixed() {
thenThrownBy(() -> assertThatKeyIsValid("bar.key", SPAN_WITH_NOT_MATCHING_PREFIX))
.hasMessageContaining("Also it has start with [foo.] prefix");
thenThrownBy(() -> assertThatKeyIsValid(MyTagsWithNotMatchingPrefix.A_BAR_TAG, SPAN_WITH_NOT_MATCHING_PREFIX))
.hasMessageContaining("Also it has start with [foo.] prefix");
}
@Test
void should_fail_when_assertion_is_on_and_an_event_is_known_but_wrongly_prefixed() {
thenThrownBy(() -> assertThatEventIsValid("bar.value", SPAN_WITH_NOT_MATCHING_PREFIX))
.hasMessageContaining("Also it has start with [foo.] prefix");
thenThrownBy(() -> assertThatEventIsValid(A_BAR_EVENT, SPAN_WITH_NOT_MATCHING_PREFIX))
.hasMessageContaining("Also it has start with [foo.] prefix");
}
@Test
void should_fail_when_assertion_is_on_and_a_key_is_known_but_dynamic_key_is_not_matched() {
thenThrownBy(() -> assertThatKeyIsValid("notmatching", SPAN_WITH_DYNAMIC_ENTRIES))
.hasMessageContaining("The key [notmatching] is invalid. You can use only one matching [%s.key]");
thenThrownBy(() -> assertThatKeyIsValid(MySimpleTag.A_SIMPLE_TAG, SPAN_WITH_DYNAMIC_ENTRIES))
.hasMessageContaining("The key [simple] is invalid. You can use only one matching [%s.key]");
}
@Test
void should_fail_when_assertion_is_on_and_an_event_is_known_but_dynamic_value_is_not_matched() {
thenThrownBy(() -> assertThatEventIsValid("notmatching", SPAN_WITH_DYNAMIC_ENTRIES))
.hasMessageContaining("The event [notmatching] is invalid. You can use only one matching [%s.value]");
thenThrownBy(() -> assertThatEventIsValid(MySimpleEvent.A_SIMPLE_EVENT, SPAN_WITH_DYNAMIC_ENTRIES))
.hasMessageContaining("The event [simple] is invalid. You can use only one matching [%s.value]");
}
@Test
void should_fail_when_assertion_is_on_and_name_is_invalid() {
thenThrownBy(() -> assertThatNameIsValid("unknown_name", SPAN_WITH_NOT_MATCHING_PREFIX))
.hasMessageContaining("The name [unknown_name] is invalid");
}
@Test
void should_fail_when_assertion_is_on_and_name_is_not_matching() {
thenThrownBy(() -> assertThatNameIsValid("unknown_name", SPAN_WITH_DYNAMIC_ENTRIES)).hasMessageContaining(
"The name [unknown_name] is invalid. You can use only one matching [%s somename]");
}
@Test
void should_fail_when_span_was_ended_but_not_started() {
thenThrownBy(() -> assertThatSpanStartedBeforeEnd(
new ImmutableAssertingSpan(SPAN_WITH_NOT_MATCHING_PREFIX, BDDMockito.mock(Span.class))))
.hasMessageContaining("The span was not started");
}
enum MySpan implements DocumentedSpan {
SPAN_WITH_PREFIX {
@Override
public String getName() {
return "foo";
}
@Override
public TagKey[] getTagKeys() {
return MyTags.values();
}
@Override
public EventValue[] getEvents() {
return MyEvents.values();
}
@Override
public String prefix() {
return "foo.";
}
},
SPAN_WITH_NOT_MATCHING_PREFIX {
@Override
public String getName() {
return "bar";
}
@Override
public TagKey[] getTagKeys() {
return MyTagsWithNotMatchingPrefix.values();
}
@Override
public EventValue[] getEvents() {
return MyEventsWithNotMatchingPrefix.values();
}
@Override
public String prefix() {
return "foo.";
}
},
SPAN_WITH_EMPTY_TAGS_AND_EVENTS {
@Override
public String getName() {
return "baz";
}
},
SPAN_WITH_DYNAMIC_ENTRIES {
@Override
public String getName() {
return "%s somename";
}
@Override
public TagKey[] getTagKeys() {
return MyDynamicTags.values();
}
@Override
public EventValue[] getEvents() {
return MyDynamicEvents.values();
}
}
}
enum MyTags implements TagKey {
A_FOO_TAG {
@Override
public String getKey() {
return "foo.key";
}
}
}
enum MyEvents implements EventValue {
A_FOO_EVENT {
@Override
public String getValue() {
return "foo.value";
}
}
}
enum MyTagsWithNotMatchingPrefix implements TagKey {
A_BAR_TAG {
@Override
public String getKey() {
return "bar.key";
}
}
}
enum MyEventsWithNotMatchingPrefix implements EventValue {
A_BAR_EVENT {
@Override
public String getValue() {
return "bar.value";
}
}
}
enum MySimpleTag implements TagKey {
A_SIMPLE_TAG {
@Override
public String getKey() {
return "simple";
}
}
}
enum MySimpleEvent implements EventValue {
A_SIMPLE_EVENT {
@Override
public String getValue() {
return "simple";
}
}
}
enum MyDynamicTags implements TagKey {
A_DYNAMIC_TAG {
@Override
public String getKey() {
return "%s.key";
}
}
}
enum MyDynamicEvents implements EventValue {
A_DYNAMIC_EVENT {
@Override
public String getValue() {
return "%s.value";
}
}
}
}

View File

@@ -1,435 +1,523 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2013-2018 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ 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.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-sleuth-autoconfigure</artifactId>
<packaging>jar</packaging>
<name>Spring Cloud Sleuth AutoConfigure</name>
<description>Spring Cloud Sleuth AutoConfigure</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth</artifactId>
<version>3.0.4-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<dependencies>
<!-- CORE -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-instrumentation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.reactivestreams</groupId>
<artifactId>reactive-streams</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-context</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
<!-- TODO: why is this needed? -->
<version>${spring-cloud-stream.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.reactivex</groupId>
<artifactId>rxjava</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<!-- For Instrumentation of Quartz -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<optional>true</optional>
</dependency>
<!-- BRAVE -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-brave</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave</artifactId>
<exclusions>
<exclusion>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>io.zipkin.zipkin2</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-context-slf4j</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-messaging</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-rpc</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-spring-rabbit</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-kafka-clients</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-kafka-streams</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-httpclient</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-httpasyncclient</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-jms</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-mongodb</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.aws</groupId>
<artifactId>brave-propagation-aws</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.jms</groupId>
<artifactId>javax.jms-api</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.opentracing.brave</groupId>
<artifactId>brave-opentracing</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpasyncclient</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<optional>true</optional>
</dependency>
<!-- GRPC Optional Dependencies -->
<dependency>
<groupId>io.github.lognet</groupId>
<artifactId>grpc-spring-boot-starter</artifactId>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-grpc</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-metrics-micrometer</artifactId>
<exclusions>
<exclusion>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</exclusion>
</exclusions>
<optional>true</optional>
</dependency>
<!-- Instrumentation of Lettuce -->
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-streams</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<optional>true</optional>
</dependency>
<!-- Zipkin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.zipkin2</groupId>
<artifactId>zipkin</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-sender-kafka</artifactId>
<optional>true</optional>
<exclusions>
<!-- assigned with spring-kafka -->
<exclusion>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-sender-activemq-client</artifactId>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-client</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-sender-amqp-client</artifactId>
<optional>true</optional>
<exclusions>
<!-- assigned with spring-rabbit -->
<exclusion>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-reactivestreams</artifactId>
<scope>test</scope>
</dependency>
<!-- Brave -->
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-http-tests</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<scope>test</scope>
</dependency>
<!-- Zipkin -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<!-- Kotlin... -->
<version>4.8.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>fast</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<forkCount>4</forkCount>
<reuseForks>true</reuseForks>
<argLine>-Xmx1024m -XX:MaxPermSize=256m</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2013-2018 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ 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.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-sleuth-autoconfigure</artifactId>
<packaging>jar</packaging>
<name>Spring Cloud Sleuth AutoConfigure</name>
<description>Spring Cloud Sleuth AutoConfigure</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth</artifactId>
<version>3.1.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<dependencies>
<!-- CORE -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-instrumentation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.reactivestreams</groupId>
<artifactId>reactive-streams</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-context</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
<!-- TODO: why is this needed? -->
<version>${spring-cloud-stream.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-task</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra-reactive</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-deployer-spi</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.reactivex</groupId>
<artifactId>rxjava</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-proxy</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.ttddyy</groupId>
<artifactId>datasource-proxy</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<optional>true</optional>
</dependency>
<!-- BRAVE -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-brave</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave</artifactId>
<exclusions>
<exclusion>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>io.zipkin.zipkin2</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-context-slf4j</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-messaging</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-rpc</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-spring-rabbit</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-kafka-clients</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-kafka-streams</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-httpclient</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-httpasyncclient</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-jms</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-mongodb</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.aws</groupId>
<artifactId>brave-propagation-aws</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.jms</groupId>
<artifactId>javax.jms-api</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.opentracing.brave</groupId>
<artifactId>brave-opentracing</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpasyncclient</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor.kafka</groupId>
<artifactId>reactor-kafka</artifactId>
<optional>true</optional>
</dependency>
<!-- GRPC Optional Dependencies -->
<dependency>
<groupId>io.github.lognet</groupId>
<artifactId>grpc-spring-boot-starter</artifactId>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-grpc</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-metrics-micrometer</artifactId>
<exclusions>
<exclusion>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</exclusion>
</exclusions>
<optional>true</optional>
</dependency>
<!-- Instrumentation of Lettuce -->
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-streams</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-rsocket</artifactId>
<optional>true</optional>
</dependency>
<!-- Zipkin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.zipkin2</groupId>
<artifactId>zipkin</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-sender-kafka</artifactId>
<optional>true</optional>
<exclusions>
<!-- assigned with spring-kafka -->
<exclusion>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-sender-activemq-client</artifactId>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-client</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-sender-amqp-client</artifactId>
<optional>true</optional>
<exclusions>
<!-- assigned with spring-rabbit -->
<exclusion>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-reactivestreams</artifactId>
<scope>test</scope>
</dependency>
<!-- Brave -->
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-http-tests</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<scope>test</scope>
</dependency>
<!-- Zipkin -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<!-- Kotlin... -->
<version>4.8.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>fast</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<forkCount>4</forkCount>
<reuseForks>true</reuseForks>
<argLine>-Xmx1024m -XX:MaxPermSize=256m</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -36,19 +36,24 @@ public class SleuthBaggageProperties {
private boolean correlationEnabled = true;
/**
* List of fields that should be propagated over the wire.
*/
private List<String> correlationFields = new ArrayList<>();
/**
* List of fields that should be accessible within the JVM process but not propagated
* over the wire.
*/
private List<String> localFields = new ArrayList<>();
/**
* 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.
*
*/
private List<String> remoteFields = new ArrayList<>();
/**
* List of fields that should automatically become tags.
*/
private List<String> tagFields = new ArrayList<>();

View File

@@ -0,0 +1,46 @@
/*
* 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.actuate;
import java.util.List;
import java.util.stream.Collectors;
import brave.Tags;
import brave.handler.MutableSpanBytesEncoder;
import org.springframework.cloud.sleuth.brave.bridge.BraveFinishedSpan;
import org.springframework.cloud.sleuth.exporter.FinishedSpan;
import org.springframework.cloud.sleuth.exporter.SpanReporter;
/**
* A {@link SpanReporter} that buffers finished spans.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
class BraveFinishedSpanWriter implements FinishedSpanWriter<String> {
@Override
public String write(TextOutputFormat format, List<FinishedSpan> spans) {
if (format == TextOutputFormat.CONTENT_TYPE_OPENZIPKIN_JSON_V2) {
return new String(MutableSpanBytesEncoder.zipkinJsonV2(Tags.ERROR)
.encodeList(spans.stream().map(BraveFinishedSpan::toBrave).collect(Collectors.toList())));
}
return null;
}
}

View File

@@ -0,0 +1,89 @@
/*
* 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.actuate;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.boot.context.metrics.buffering.StartupTimeline;
import org.springframework.cloud.sleuth.exporter.FinishedSpan;
import org.springframework.cloud.sleuth.exporter.SpanReporter;
/**
* A {@link SpanReporter} that buffers finished spans.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public class BufferingSpanReporter implements SpanReporter {
private final int capacity;
private final AtomicInteger estimatedSize = new AtomicInteger();
final ConcurrentLinkedQueue<FinishedSpan> spans = new ConcurrentLinkedQueue<>();
public BufferingSpanReporter(int capacity) {
this.capacity = capacity;
}
/**
* Return a snapshot of currently buffered spans.
* <p>
* This will not remove spans from the buffer, see {@link #drainFinishedSpans()} ()}
* for its counterpart.
* @return a snapshot of currently buffered spans.
*/
public List<FinishedSpan> getFinishedSpans() {
return new ArrayList<>(this.spans);
}
/**
* Return the {@link StartupTimeline timeline} by pulling spans from the buffer.
* <p>
* This removes steps from the buffer, see {@link #getFinishedSpans()} for its
* read-only counterpart.
* @return buffered steps drained from the buffer.
*/
public List<FinishedSpan> drainFinishedSpans() {
List<FinishedSpan> events = new ArrayList<>();
Iterator<FinishedSpan> iterator = this.spans.iterator();
while (iterator.hasNext()) {
events.add(iterator.next());
iterator.remove();
}
this.estimatedSize.set(0);
return events;
}
@Override
public void report(FinishedSpan span) {
if (this.estimatedSize.get() < this.capacity) {
this.estimatedSize.incrementAndGet();
this.spans.add(span);
}
else {
this.spans.poll();
this.estimatedSize.decrementAndGet();
report(span);
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.actuate;
import java.util.List;
import org.springframework.cloud.sleuth.exporter.FinishedSpan;
/**
* Writes finished spans in a provided format.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public interface FinishedSpanWriter<T> {
/**
* Writes the spans in a given format to String.
* @param format format in which spans should be stored
* @param spans spans to store
* @return string representation of spans or {@code null} if {@link TextOutputFormat}
* is not supported.
*/
T write(TextOutputFormat format, List<FinishedSpan> spans);
}

View File

@@ -0,0 +1,43 @@
/*
* 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.actuate;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Settings for Sleuth actuator.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
@ConfigurationProperties(prefix = "management.endpoint.traces")
public class SleuthActuatorProperties {
/**
* Max capacity of the span queue.
*/
private int capacity = 10_000;
public int getCapacity() {
return this.capacity;
}
public void setCapacity(int capacity) {
this.capacity = capacity;
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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.actuate;
import org.springframework.boot.actuate.endpoint.Producible;
import org.springframework.http.MediaType;
import org.springframework.util.MimeType;
/**
* A {@link Producible} enum for supported span outputs.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public enum TextOutputFormat implements Producible<TextOutputFormat> {
/**
* OpenZipkin text.
*/
CONTENT_TYPE_OPENZIPKIN_JSON_V2(MediaType.APPLICATION_JSON),
/**
* OTLP protobuf format.
*/
CONTENT_TYPE_OTLP_PROTOBUF(MediaType.parseMediaType("application/x-protobuf"));
private final MimeType mimeType;
TextOutputFormat(MimeType mimeType) {
this.mimeType = mimeType;
}
@Override
public MimeType getProducedMimeType() {
return this.mimeType;
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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.actuate;
import brave.handler.SpanHandler;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
import org.springframework.boot.actuate.endpoint.Producible;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.autoconfig.brave.ConditionalOnBraveEnabled;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} for Sleuth actuator endpoint.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "spring.sleuth.enabled", matchIfMissing = true)
@ConditionalOnAvailableEndpoint(endpoint = TracesScrapeEndpoint.class)
@AutoConfigureBefore(BraveAutoConfiguration.class)
@EnableConfigurationProperties(SleuthActuatorProperties.class)
@ConditionalOnClass(Producible.class)
public class TraceSleuthActuatorAutoConfiguration {
@Bean
@ConditionalOnMissingBean
BufferingSpanReporter sleuthBufferingSpanReporter(SleuthActuatorProperties sleuthActuatorProperties) {
return new BufferingSpanReporter(sleuthActuatorProperties.getCapacity());
}
@Bean
@ConditionalOnMissingBean
TracesScrapeEndpoint sleuthTracesScrapeEndpoint(BufferingSpanReporter bufferingSpanReporter,
FinishedSpanWriter finishedSpanWriter) {
return new TracesScrapeEndpoint(bufferingSpanReporter, finishedSpanWriter);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(brave.Tracer.class)
@ConditionalOnBraveEnabled
static class BraveActuatorConfiguration {
@Bean
@ConditionalOnMissingBean
FinishedSpanWriter sleuthBraveFinishedSpanWriter() {
return new BraveFinishedSpanWriter();
}
/**
* We need to register at least one {@link SpanHandler} for sampling to hook in.
* If there are no span handlers all spans will be noop and no spans will get
* reported.
* @return a noop span handler
*/
@Bean
SpanHandler sleuthBraveCustomSpanHandler() {
return new SpanHandler() {
};
}
}
}

View File

@@ -0,0 +1,71 @@
/*
* 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.actuate;
import java.util.List;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;
import org.springframework.cloud.sleuth.exporter.FinishedSpan;
import org.springframework.http.HttpStatus;
import org.springframework.lang.NonNull;
/**
* {@link Endpoint @Endpoint} that outputs spans in a format that can be scraped by a
* collector.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
@WebEndpoint(id = "traces")
public class TracesScrapeEndpoint {
private final BufferingSpanReporter bufferingSpanReporter;
private final FinishedSpanWriter finishedSpanWriter;
public TracesScrapeEndpoint(BufferingSpanReporter bufferingSpanReporter, FinishedSpanWriter finishedSpanWriter) {
this.bufferingSpanReporter = bufferingSpanReporter;
this.finishedSpanWriter = finishedSpanWriter;
}
@ReadOperation(producesFrom = TextOutputFormat.class)
public WebEndpointResponse<Object> spansSnapshot(TextOutputFormat format) {
List<FinishedSpan> finishedSpans = this.bufferingSpanReporter.getFinishedSpans();
return response(format, finishedSpans);
}
@NonNull
private WebEndpointResponse<Object> response(TextOutputFormat format, List<FinishedSpan> finishedSpans) {
Object spans = this.finishedSpanWriter.write(format, finishedSpans);
if (spans == null) {
return new WebEndpointResponse<>("The format [" + format.getProducedMimeType() + " ] is not supported",
HttpStatus.NOT_ACCEPTABLE.value());
}
return new WebEndpointResponse<>(spans, format);
}
@WriteOperation(producesFrom = TextOutputFormat.class)
public WebEndpointResponse<Object> spans(TextOutputFormat format) {
List<FinishedSpan> finishedSpans = this.bufferingSpanReporter.drainFinishedSpans();
return response(format, finishedSpans);
}
}

View File

@@ -16,12 +16,14 @@
package org.springframework.cloud.sleuth.autoconfig.brave;
import java.util.ArrayList;
import java.util.List;
import brave.Tracing;
import brave.handler.SpanHandler;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.sleuth.SpanCustomizer;
@@ -35,10 +37,10 @@ import org.springframework.cloud.sleuth.brave.bridge.CompositePropagationFactory
import org.springframework.cloud.sleuth.brave.bridge.CompositeSpanHandler;
import org.springframework.cloud.sleuth.brave.propagation.PropagationFactorySupplier;
import org.springframework.cloud.sleuth.exporter.SpanFilter;
import org.springframework.cloud.sleuth.exporter.SpanReporter;
import org.springframework.cloud.sleuth.propagation.Propagator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SleuthPropagationProperties.class)
@@ -75,8 +77,10 @@ class BraveBridgeConfiguration {
// Name is important for sampling conditions
@Bean(name = "traceCompositeSpanHandler")
SpanHandler compositeSpanHandler(@Nullable List<SpanFilter> exporters) {
return new CompositeSpanHandler(exporters);
SpanHandler compositeSpanHandler(ObjectProvider<List<SpanFilter>> exporters,
ObjectProvider<List<SpanReporter>> reporters) {
return new CompositeSpanHandler(exporters.getIfAvailable(ArrayList::new),
reporters.getIfAvailable(ArrayList::new));
}
}

View File

@@ -113,7 +113,9 @@ public class TraceAsyncDefaultAutoConfiguration {
return this.beanFactory.getBean(TaskExecutor.class);
}
catch (NoUniqueBeanDefinitionException ex) {
log.debug("Could not find unique TaskExecutor bean", 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);

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.batch;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that registers instrumentation for Spring Batch.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(JobBuilderFactory.class)
@ConditionalOnBean(Tracer.class)
@ConditionalOnProperty(value = "spring.sleuth.batch.enabled", matchIfMissing = true)
@AutoConfigureAfter(BraveAutoConfiguration.class)
public class TraceBatchAutoConfiguration {
@Bean
static TraceJobBuilderFactoryBeanPostProcessor traceJobBuilderFactoryBeanPostProcessor(BeanFactory beanFactory) {
return new TraceJobBuilderFactoryBeanPostProcessor(beanFactory);
}
@Bean
static TraceStepBuilderFactoryBeanPostProcessor traceStepBuilderFactoryBeanPostProcessor(BeanFactory beanFactory) {
return new TraceStepBuilderFactoryBeanPostProcessor(beanFactory);
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.batch;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.sleuth.instrument.batch.TraceJobBuilderFactory;
/**
* Bean post processor for {@link JobBuilderFactory}.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public class TraceJobBuilderFactoryBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
public TraceJobBuilderFactoryBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof JobBuilderFactory && !(bean instanceof TraceJobBuilderFactory)) {
return new TraceJobBuilderFactory(this.beanFactory, (JobBuilderFactory) bean);
}
return bean;
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.batch;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.sleuth.instrument.batch.TraceStepBuilderFactory;
/**
* Bean post processor for {@link StepBuilderFactory}.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public class TraceStepBuilderFactoryBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
public TraceStepBuilderFactoryBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof StepBuilderFactory && !(bean instanceof TraceStepBuilderFactory)) {
return new TraceStepBuilderFactory(this.beanFactory, (StepBuilderFactory) bean);
}
return bean;
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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.cassandra;
import com.datastax.oss.driver.api.core.CqlSession;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.cassandra.TraceCqlSessionBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that registers instrumentation for Cassandra.
*
* @author Mark Paluch
* @author Marcin Grzejszczak
* @since 3.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(Tracer.class)
@ConditionalOnProperty(value = "spring.sleuth.cassandra.enabled", matchIfMissing = true)
@AutoConfigureAfter(BraveAutoConfiguration.class)
@AutoConfigureBefore(CassandraAutoConfiguration.class)
@ConditionalOnClass(CqlSession.class)
public class TraceCassandraAutoConfiguration {
@Bean
static TraceCqlSessionBeanPostProcessor traceCqlSessionBeanPostProcessor(BeanFactory beanFactory) {
return new TraceCqlSessionBeanPostProcessor(beanFactory);
}
@Bean
TraceCqlSessionBuilderCustomizer traceCqlSessionBuilderCustomizer() {
return new TraceCqlSessionBuilderCustomizer();
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.cassandra;
import reactor.core.publisher.Flux;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.ReactiveSession;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that registers instrumentation for Cassandra.
*
* @author Mark Paluch
* @author Marcin Grzejszczak
* @since 3.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(Tracer.class)
@ConditionalOnClass({ ReactiveSession.class, Flux.class })
@ConditionalOnProperty(value = "spring.sleuth.cassandra.enabled", matchIfMissing = true)
@AutoConfigureAfter(BraveAutoConfiguration.class)
@AutoConfigureBefore({ CassandraAutoConfiguration.class, CassandraReactiveDataAutoConfiguration.class })
public class TraceCassandraReactiveAutoConfiguration {
@Bean
static TraceReactiveSessionBeanPostProcessor traceReactiveSessionBeanPostProcessor(BeanFactory beanFactory) {
return new TraceReactiveSessionBeanPostProcessor(beanFactory);
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.cassandra;
import com.datastax.oss.driver.api.core.CqlSession;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.sleuth.instrument.cassandra.TraceCqlSession;
/**
* {@link BeanPostProcessor} to wrap a {@link CqlSession} instance into its trace
* representation.
*
* @author Marcin Grzejszczak
* @author Mark Paluch
* @since 3.1.0
*/
public class TraceCqlSessionBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
public TraceCqlSessionBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof CqlSession) {
return create((CqlSession) bean);
}
return bean;
}
private CqlSession create(CqlSession session) {
return TraceCqlSession.create(session, this.beanFactory);
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.cassandra;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.sleuth.instrument.cassandra.TraceReactiveSession;
import org.springframework.data.cassandra.ReactiveSession;
/**
* {@link BeanPostProcessor} to wrap a
* {@link org.springframework.data.cassandra.ReactiveSession} instance into its trace
* representation.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public class TraceReactiveSessionBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
public TraceReactiveSessionBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof ReactiveSession) {
return create((ReactiveSession) bean);
}
return bean;
}
private ReactiveSession create(ReactiveSession session) {
return TraceReactiveSession.create(session, this.beanFactory);
}
}

View File

@@ -21,10 +21,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.sleuth.CurrentTraceContext;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.circuitbreaker.TraceCircuitBreakerFactoryAspect;
import org.springframework.cloud.sleuth.instrument.circuitbreaker.TraceReactiveCircuitBreakerFactoryAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -36,7 +37,6 @@ import org.springframework.context.annotation.Configuration;
* @since 2.2.1
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(CircuitBreaker.class)
@ConditionalOnBean(Tracer.class)
@ConditionalOnProperty(value = "spring.sleuth.circuitbreaker.enabled", matchIfMissing = true)
@EnableConfigurationProperties(SleuthCircuitBreakerProperties.class)
@@ -44,8 +44,17 @@ import org.springframework.context.annotation.Configuration;
public class TraceCircuitBreakerAutoConfiguration {
@Bean
@ConditionalOnClass(name = "org.springframework.cloud.client.circuitbreaker.CircuitBreaker")
TraceCircuitBreakerFactoryAspect traceCircuitBreakerFactoryAspect(Tracer tracer) {
return new TraceCircuitBreakerFactoryAspect(tracer);
}
@Bean
@ConditionalOnClass(name = { "reactor.core.publisher.Mono",
"org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreaker" })
TraceReactiveCircuitBreakerFactoryAspect traceReactiveCircuitBreakerFactoryAspect(Tracer tracer,
CurrentTraceContext currentTraceContext) {
return new TraceReactiveCircuitBreakerFactoryAspect(tracer, currentTraceContext);
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.config;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.config.server.config.ConfigServerConfiguration;
import org.springframework.cloud.config.server.config.ConfigServerProperties;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.config.TraceEnvironmentRepositoryAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that registers instrumentation for Spring Cloud Config Server and
* Client.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean({ Tracer.class, ConfigServerProperties.class })
@ConditionalOnClass(ConfigServerConfiguration.class)
@ConditionalOnProperty(value = "spring.sleuth.config.server.enabled", matchIfMissing = true)
@AutoConfigureAfter(BraveAutoConfiguration.class)
public class TraceSpringCloudConfigAutoConfiguration {
@Bean
TraceEnvironmentRepositoryAspect traceEnvironmentRepositoryAspect(Tracer tracer) {
return new TraceEnvironmentRepositoryAspect(tracer);
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.deployer;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.deployer.spi.app.AppDeployer;
import org.springframework.cloud.sleuth.instrument.deployer.TraceAppDeployer;
import org.springframework.core.env.Environment;
/**
* {@link BeanPostProcessor} to wrap a {@link AppDeployer} instance into its trace
* representation.
*
* @author Marcin Grzejszczak
* @since 2.0.0
*/
public class TraceAppDeployerBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
private final Environment environment;
public TraceAppDeployerBeanPostProcessor(BeanFactory beanFactory, Environment environment) {
this.beanFactory = beanFactory;
this.environment = environment;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof AppDeployer && !(bean instanceof TraceAppDeployer)) {
return new TraceAppDeployer((AppDeployer) bean, this.beanFactory, this.environment);
}
return bean;
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.deployer;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.deployer.spi.app.AppDeployer;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that registers instrumentation for app deployers.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(Tracer.class)
@ConditionalOnProperty(value = "spring.sleuth.deployer.enabled", matchIfMissing = true)
@ConditionalOnClass(AppDeployer.class)
@AutoConfigureAfter(BraveAutoConfiguration.class)
public class TraceDeployerAutoConfiguration {
@Bean
static TraceAppDeployerBeanPostProcessor traceAppDeployerBeanPostProcessor(BeanFactory beanFactory,
Environment environment) {
return new TraceAppDeployerBeanPostProcessor(beanFactory, environment);
}
}

View File

@@ -0,0 +1,114 @@
/*
* 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.ArrayList;
import java.util.List;
import net.ttddyy.dsproxy.listener.MethodExecutionListener;
import net.ttddyy.dsproxy.listener.QueryCountStrategy;
import net.ttddyy.dsproxy.listener.QueryExecutionListener;
import net.ttddyy.dsproxy.proxy.GlobalConnectionIdManager;
import net.ttddyy.dsproxy.proxy.ResultSetProxyLogicFactory;
import net.ttddyy.dsproxy.proxy.SimpleResultSetProxyLogicFactory;
import net.ttddyy.dsproxy.support.ProxyDataSource;
import net.ttddyy.dsproxy.transform.ParameterTransformer;
import net.ttddyy.dsproxy.transform.QueryTransformer;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceNameResolver;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceProxyBuilderCustomizer;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceProxyConnectionIdManagerProvider;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceProxyDataSourceDecorator;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceProxyProperties;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceListenerStrategySpanCustomizer;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceQueryExecutionListener;
import org.springframework.context.annotation.Bean;
/**
* Configuration for integration with datasource-proxy, allows to use define custom
* {@link QueryExecutionListener}, {@link ParameterTransformer} and
* {@link QueryTransformer}.
*
* @author Arthur Gavlyukovskiy
* @author Chintan Radia
*/
@ConditionalOnClass(ProxyDataSource.class)
@ConditionalOnProperty(name = "spring.sleuth.jdbc.datasource-proxy.enabled", havingValue = "true",
matchIfMissing = true)
class DataSourceProxyConfiguration {
@Bean
@ConditionalOnMissingBean
DataSourceProxyConnectionIdManagerProvider traceConnectionIdManagerProvider() {
return GlobalConnectionIdManager::new;
}
@Bean
DataSourceProxyBuilderCustomizer proxyDataSourceBuilderConfigurer(
ObjectProvider<QueryCountStrategy> queryCountStrategy,
ObjectProvider<List<QueryExecutionListener>> listeners,
ObjectProvider<List<MethodExecutionListener>> methodExecutionListeners,
ObjectProvider<ParameterTransformer> parameterTransformer,
ObjectProvider<QueryTransformer> queryTransformer,
ObjectProvider<ResultSetProxyLogicFactory> resultSetProxyLogicFactory,
ObjectProvider<DataSourceProxyConnectionIdManagerProvider> dataSourceProxyConnectionIdManagerProvider,
TraceJdbcProperties traceJdbcProperties) {
return new DataSourceProxyBuilderCustomizer(queryCountStrategy.getIfAvailable(() -> null),
listeners.getIfAvailable(() -> null), methodExecutionListeners.getIfAvailable(() -> null),
parameterTransformer.getIfAvailable(() -> null), queryTransformer.getIfAvailable(() -> null),
resultSetProxyLogicFactory.getIfAvailable(() -> null),
dataSourceProxyConnectionIdManagerProvider.getIfAvailable(() -> null), props(traceJdbcProperties));
}
private DataSourceProxyProperties props(TraceJdbcProperties traceJdbcProperties) {
TraceJdbcProperties.DataSourceProxyProperties originalProxy = traceJdbcProperties.getDatasourceProxy();
DataSourceProxyProperties props = new DataSourceProxyProperties();
BeanUtils.copyProperties(originalProxy, props);
props.setLogging(DataSourceProxyProperties.DataSourceProxyLogging.valueOf(originalProxy.getLogging().name()));
BeanUtils.copyProperties(originalProxy.getQuery(), props.getQuery());
BeanUtils.copyProperties(originalProxy.getSlowQuery(), props.getSlowQuery());
return props;
}
@Bean
DataSourceProxyDataSourceDecorator proxyDataSourceDecorator(
DataSourceProxyBuilderCustomizer dataSourceProxyBuilderCustomizer,
DataSourceNameResolver dataSourceNameResolver) {
return new DataSourceProxyDataSourceDecorator(dataSourceProxyBuilderCustomizer, dataSourceNameResolver);
}
@Bean
TraceQueryExecutionListener traceQueryExecutionListener(Tracer tracer,
TraceJdbcProperties dataSourceDecoratorProperties,
ObjectProvider<List<TraceListenerStrategySpanCustomizer>> customizers) {
return new TraceQueryExecutionListener(tracer, dataSourceDecoratorProperties.getIncludes(),
customizers.getIfAvailable(ArrayList::new));
}
@Bean
@ConditionalOnMissingBean
ResultSetProxyLogicFactory traceResultSetProxyLogicFactory() {
return new SimpleResultSetProxyLogicFactory();
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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.ArrayList;
import java.util.List;
import com.p6spy.engine.event.JdbcEventListener;
import com.p6spy.engine.spy.DefaultJdbcEventListenerFactory;
import com.p6spy.engine.spy.JdbcEventListenerFactory;
import com.p6spy.engine.spy.P6DataSource;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceNameResolver;
import org.springframework.cloud.sleuth.instrument.jdbc.P6SpyContextJdbcEventListenerFactory;
import org.springframework.cloud.sleuth.instrument.jdbc.P6SpyDataSourceDecorator;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceJdbcEventListener;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceListenerStrategySpanCustomizer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
/**
* Configuration for integration with p6spy, allows to define custom
* {@link JdbcEventListener}.
*
* @author Arthur Gavlyukovskiy
*/
@ConditionalOnClass(P6DataSource.class)
@ConditionalOnProperty(name = "spring.sleuth.jdbc.p6spy.enabled", havingValue = "true", matchIfMissing = true)
class P6SpyConfiguration {
@Bean
static P6SpyPropertiesSetter p6SpyPropertiesSetter(ConfigurableApplicationContext context) {
return new P6SpyPropertiesSetter(context);
}
@Bean
@ConditionalOnMissingBean
JdbcEventListenerFactory traceJdbcEventListenerFactory(ObjectProvider<List<JdbcEventListener>> listeners) {
JdbcEventListenerFactory jdbcEventListenerFactory = new DefaultJdbcEventListenerFactory();
List<JdbcEventListener> listenerList = listeners.getIfAvailable(() -> null);
return listenerList != null ? new P6SpyContextJdbcEventListenerFactory(jdbcEventListenerFactory, listenerList)
: jdbcEventListenerFactory;
}
@Bean
P6SpyDataSourceDecorator p6SpyDataSourceDecorator(JdbcEventListenerFactory jdbcEventListenerFactory) {
return new P6SpyDataSourceDecorator(jdbcEventListenerFactory);
}
@Bean
TraceJdbcEventListener tracingJdbcEventListener(Tracer tracer, DataSourceNameResolver dataSourceNameResolver,
TraceJdbcProperties traceJdbcProperties,
ObjectProvider<List<TraceListenerStrategySpanCustomizer>> customizers) {
return new TraceJdbcEventListener(tracer, dataSourceNameResolver, traceJdbcProperties.getIncludes(),
traceJdbcProperties.getP6spy().getTracing().isIncludeParameterValues(),
customizers.getIfAvailable(ArrayList::new));
}
}

View File

@@ -0,0 +1,188 @@
/*
* 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.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.p6spy.engine.logging.P6LogFactory;
import com.p6spy.engine.spy.P6ModuleManager;
import com.p6spy.engine.spy.P6SpyFactory;
import com.p6spy.engine.spy.option.EnvironmentVariables;
import com.p6spy.engine.spy.option.P6OptionsSource;
import com.p6spy.engine.spy.option.SpyDotProperties;
import com.p6spy.engine.spy.option.SystemProperties;
import org.slf4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Sets p6spy properties to / from system properties.
*
* @author Arthur Gavlyukovskiy
* @since 3.1.0
*/
class P6SpyPropertiesSetter implements BeanDefinitionRegistryPostProcessor, Closeable {
private static final Logger log = getLogger(P6SpyPropertiesSetter.class);
private final ConfigurableApplicationContext context;
private final Map<String, String> initialP6SpyOptions;
P6SpyPropertiesSetter(ConfigurableApplicationContext context) {
this.context = context;
this.initialP6SpyOptions = findDefinedOptions();
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
ConfigurableEnvironment environment = this.context.getEnvironment();
String customModuleList = initialP6SpyOptions.get("modulelist");
boolean isEnableLogging = environment.getProperty("spring.sleuth.jdbc.p6spy.enable-logging", Boolean.class,
false);
if (customModuleList != null) {
log.info("P6Spy modulelist is overridden, some p6spy configuration features will not be applied");
}
else {
List<String> moduleList = new ArrayList<>();
// default factory, holds P6Spy configuration
moduleList.add(P6SpyFactory.class.getName());
if (isEnableLogging) {
moduleList.add(P6LogFactory.class.getName());
}
System.setProperty("p6spy.config.modulelist", String.join(",", moduleList));
}
if (!initialP6SpyOptions.containsKey("logMessageFormat")) {
String logFormat = logFormat(environment);
boolean isMultiline = multiLine(environment);
if (logFormat != null) {
System.setProperty("p6spy.config.logMessageFormat", "com.p6spy.engine.spy.appender.CustomLineFormat");
System.setProperty("p6spy.config.customLogMessageFormat", logFormat);
}
else if (isMultiline) {
System.setProperty("p6spy.config.logMessageFormat", "com.p6spy.engine.spy.appender.MultiLineFormat");
}
}
if (isEnableLogging && !initialP6SpyOptions.containsKey("appender")) {
TraceJdbcProperties.P6SpyProperties.P6SpyLogging logging = TraceJdbcProperties.P6SpyProperties.P6SpyLogging
.valueOf(
environment
.getProperty("spring.sleuth.jdbc.p6spy.logging", String.class,
TraceJdbcProperties.P6SpyProperties.P6SpyLogging.SLF4J.toString())
.toUpperCase());
switch (logging) {
case SYSOUT:
System.setProperty("p6spy.config.appender", "com.p6spy.engine.spy.appender.StdoutLogger");
break;
case SLF4J:
System.setProperty("p6spy.config.appender", "com.p6spy.engine.spy.appender.Slf4JLogger");
break;
case FILE:
System.setProperty("p6spy.config.appender", "com.p6spy.engine.spy.appender.FileLogger");
break;
case CUSTOM:
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.p6spy.log-file", String.class, "spy.log");
System.setProperty("p6spy.config.logfile", logFile);
}
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);
}
// If factories were loaded before this method is initialized changing properties
// will not be applied
// Changes done in this method could not override anything user specified,
// therefore it is safe to call reload
P6ModuleManager.getInstance().reload();
}
private Boolean multiLine(ConfigurableEnvironment environment) {
return environment.getProperty("spring.sleuth.jdbc.p6spy.multiline", Boolean.class, true);
}
private String logFormat(ConfigurableEnvironment environment) {
return environment.getProperty("spring.sleuth.jdbc.p6spy.log-format", String.class);
}
@Override
public void close() throws IOException {
if (!initialP6SpyOptions.containsKey("modulelist")) {
System.clearProperty("p6spy.config.modulelist");
}
if (!initialP6SpyOptions.containsKey("logMessageFormat")) {
ConfigurableEnvironment environment = this.context.getEnvironment();
String logFormat = logFormat(environment);
boolean isMultiline = multiLine(environment);
if (logFormat != null) {
System.clearProperty("p6spy.config.logMessageFormat");
System.clearProperty("p6spy.config.customLogMessageFormat");
}
else if (isMultiline) {
System.clearProperty("p6spy.config.logMessageFormat");
}
}
if (!initialP6SpyOptions.containsKey("appender")) {
System.clearProperty("p6spy.config.appender");
}
if (!initialP6SpyOptions.containsKey("logfile")) {
System.clearProperty("p6spy.config.logfile");
}
P6ModuleManager.getInstance().reload();
}
private Map<String, String> findDefinedOptions() {
SpyDotProperties spyDotProperties = null;
try {
spyDotProperties = new SpyDotProperties();
}
catch (IOException ignored) {
}
return Stream.of(spyDotProperties, new EnvironmentVariables(), new SystemProperties()).filter(Objects::nonNull)
.map(P6OptionsSource::getOptions).filter(Objects::nonNull)
.flatMap(options -> options.entrySet().stream())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
// always using value from the first P6OptionsSource
(value1, value2) -> value1));
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
}
}

View File

@@ -0,0 +1,140 @@
/*
* 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.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceDecorator;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceNameResolver;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceWrapper;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.util.ClassUtils;
/**
* {@link BeanPostProcessor} that wraps all data source beans in {@link DataSource}
* proxies specified in property 'spring.datasource.type'.
*
* @author Arthur Gavlyukovskiy
* @since 3.1.0
*/
public class TraceDataSourceDecoratorBeanPostProcessor implements BeanPostProcessor, Ordered, ApplicationContextAware {
private static final Log log = LogFactory.getLog(TraceDataSourceDecoratorBeanPostProcessor.class);
private final static boolean HIKARI_AVAILABLE = ClassUtils.isPresent("com.zaxxer.hikari.HikariDataSource",
DataSourceNameResolver.class.getClassLoader());
private ApplicationContext applicationContext;
private DataSourceNameResolver dataSourceNameResolver;
private final Collection<String> excludedBeans;
public TraceDataSourceDecoratorBeanPostProcessor(Collection<String> excludedBeans) {
this.excludedBeans = excludedBeans;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof DataSource && !ScopedProxyUtils.isScopedTarget(beanName)
&& !this.excludedBeans.contains(beanName)) {
// TODO: This might be a problem - it can lead to eager bean init
Map<String, DataSourceDecorator> decorators = this.applicationContext
.getBeansOfType(DataSourceDecorator.class).entrySet().stream()
.sorted(Entry.comparingByValue(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (v1, v2) -> v2, LinkedHashMap::new));
return decorate((DataSource) bean, getDataSourceName(bean, beanName), decorators);
}
else {
return bean;
}
}
private String getDataSourceName(Object bean, String beanName) {
if (HIKARI_AVAILABLE && bean instanceof HikariDataSource) {
HikariDataSource hikariDataSource = (HikariDataSource) bean;
if (hikariDataSource.getPoolName() != null && !hikariDataSource.getPoolName().startsWith("HikariPool-")) {
return hikariDataSource.getPoolName();
}
}
return beanName;
}
private DataSource decorate(DataSource dataSource, String name, Map<String, DataSourceDecorator> decorators) {
getDataSourceNameResolver().addDataSource(name, dataSource);
DataSource decoratedDataSource = dataSource;
for (Entry<String, DataSourceDecorator> decoratorEntry : decorators.entrySet()) {
String decoratorBeanName = decoratorEntry.getKey();
DataSourceDecorator decorator = decoratorEntry.getValue();
DataSource dataSourceBeforeDecorating = decoratedDataSource;
decoratedDataSource = Objects.requireNonNull(decorator.decorate(name, decoratedDataSource),
"DataSourceDecorator (" + decoratorBeanName + ", " + decorator + ") should not return null");
if (dataSourceBeforeDecorating != decoratedDataSource) {
getDataSourceNameResolver().addDataSource(name, decoratedDataSource);
}
}
if (dataSource != decoratedDataSource) {
if (log.isDebugEnabled()) {
log.debug("The decorated data source [" + decoratedDataSource + "] will replace the original one ["
+ dataSource + "]");
}
decoratedDataSource = new DataSourceWrapper(dataSource, decoratedDataSource);
getDataSourceNameResolver().addDataSource(name, decoratedDataSource);
}
return decoratedDataSource;
}
private DataSourceNameResolver getDataSourceNameResolver() {
if (this.dataSourceNameResolver == null) {
this.dataSourceNameResolver = this.applicationContext.getBean(DataSourceNameResolver.class);
}
return this.dataSourceNameResolver;
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 10;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

View File

@@ -0,0 +1,72 @@
/*
* 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 javax.sql.DataSource;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
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.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceDecorator;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceNameResolver;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceHikariListenerStrategySpanCustomizer;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceListenerStrategySpanCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* {@link EnableAutoConfiguration Auto-configuration} for proxying DataSource.
*
* @author Arthur Gavlyukovskiy
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(TraceJdbcProperties.class)
@ConditionalOnProperty(name = "spring.sleuth.jdbc.enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnBean({ DataSource.class, Tracer.class })
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, BraveAutoConfiguration.class })
@Import({ P6SpyConfiguration.class, DataSourceProxyConfiguration.class })
public class TraceJdbcAutoConfiguration {
@Bean
@ConditionalOnBean(DataSourceDecorator.class)
static TraceDataSourceDecoratorBeanPostProcessor traceDataSourceDecoratorBeanPostProcessor(
TraceJdbcProperties dataSourceDecoratorProperties) {
return new TraceDataSourceDecoratorBeanPostProcessor(
dataSourceDecoratorProperties.getExcludedDataSourceBeanNames());
}
@Bean
@ConditionalOnMissingBean
DataSourceNameResolver traceDataSourceNameResolver() {
return new DataSourceNameResolver();
}
@Bean
@ConditionalOnClass(name = "com.zaxxer.hikari.HikariDataSource")
TraceListenerStrategySpanCustomizer hikariTraceListenerStrategySpanCustomizer() {
return new TraceHikariListenerStrategySpanCustomizer();
}
}

View File

@@ -0,0 +1,511 @@
/*
* 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.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceType;
/**
* Properties for JDBC instrumentation.
*
* @author Arthur Gavlyukovskiy
* @since 3.1.0
*/
@ConfigurationProperties(prefix = "spring.sleuth.jdbc")
public class TraceJdbcProperties {
/**
* Enables JDBC instrumentation.
*/
private boolean enabled = true;
/**
* List of DataSource bean names that will not be decorated.
*/
private Collection<String> excludedDataSourceBeanNames = Collections.emptyList();
/**
* Which types of tracing we would like to include.
*/
private List<TraceType> includes = Arrays.asList(TraceType.CONNECTION, TraceType.QUERY, TraceType.FETCH);
private DataSourceProxyProperties datasourceProxy = new DataSourceProxyProperties();
private P6SpyProperties p6spy = new P6SpyProperties();
public boolean isEnabled() {
return this.enabled;
}
public Collection<String> getExcludedDataSourceBeanNames() {
return this.excludedDataSourceBeanNames;
}
public List<TraceType> getIncludes() {
return this.includes;
}
public void setIncludes(List<TraceType> includes) {
this.includes = includes;
}
public DataSourceProxyProperties getDatasourceProxy() {
return datasourceProxy;
}
public P6SpyProperties getP6spy() {
return this.p6spy;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setExcludedDataSourceBeanNames(Collection<String> excludedDataSourceBeanNames) {
this.excludedDataSourceBeanNames = excludedDataSourceBeanNames;
}
public void setDatasourceProxy(DataSourceProxyProperties datasourceProxy) {
this.datasourceProxy = datasourceProxy;
}
public void setP6spy(P6SpyProperties p6spy) {
this.p6spy = p6spy;
}
/**
* Properties for datasource-proxy.
*/
public static class DataSourceProxyProperties {
/**
* Should the datasource-proxy tracing be enabled?
*/
private boolean enabled = true;
/**
* Logging to use for logging queries.
*/
private DataSourceProxyLogging logging = DataSourceProxyLogging.SLF4J;
/**
* Query configuration.
*/
private Query query = new Query();
/**
* Slow query configuration.
*/
private SlowQuery slowQuery = new SlowQuery();
/**
* Use multiline output for logging query.
*
* @see ProxyDataSourceBuilder#multiline()
*/
private boolean multiline = true;
/**
* Use json output for logging query.
*
* @see ProxyDataSourceBuilder#asJson()
*/
private boolean jsonFormat = false;
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public DataSourceProxyLogging getLogging() {
return this.logging;
}
public void setLogging(DataSourceProxyLogging logging) {
this.logging = logging;
}
public Query getQuery() {
return this.query;
}
public void setQuery(Query query) {
this.query = query;
}
public SlowQuery getSlowQuery() {
return this.slowQuery;
}
public void setSlowQuery(SlowQuery slowQuery) {
this.slowQuery = slowQuery;
}
public boolean isMultiline() {
return this.multiline;
}
public void setMultiline(boolean multiline) {
this.multiline = multiline;
}
public boolean isJsonFormat() {
return this.jsonFormat;
}
public void setJsonFormat(boolean jsonFormat) {
this.jsonFormat = jsonFormat;
}
/**
* Properties to configure query logging listener.
*/
public static class Query {
/**
* Enable logging all queries to the log.
*/
private boolean enableLogging = false;
/**
* Name of query logger.
*/
private String loggerName;
/**
* Severity of query logger.
*/
private String logLevel = "DEBUG";
public boolean isEnableLogging() {
return this.enableLogging;
}
public void setEnableLogging(boolean enableLogging) {
this.enableLogging = enableLogging;
}
public String getLoggerName() {
return this.loggerName;
}
public void setLoggerName(String loggerName) {
this.loggerName = loggerName;
}
public String getLogLevel() {
return this.logLevel;
}
public void setLogLevel(String logLevel) {
this.logLevel = logLevel;
}
}
/**
* Properties to configure slow query logging listener.
*/
public static class SlowQuery {
/**
* Enable logging slow queries to the log.
*/
private boolean enableLogging = false;
/**
* Name of slow query logger.
*/
private String loggerName;
/**
* Severity of slow query logger.
*/
private String logLevel = "WARN";
/**
* Number of seconds to consider query as slow.
*/
private long threshold = 300;
public boolean isEnableLogging() {
return enableLogging;
}
public void setEnableLogging(boolean enableLogging) {
this.enableLogging = enableLogging;
}
public String getLoggerName() {
return loggerName;
}
public void setLoggerName(String loggerName) {
this.loggerName = loggerName;
}
public String getLogLevel() {
return logLevel;
}
public void setLogLevel(String logLevel) {
this.logLevel = logLevel;
}
public long getThreshold() {
return threshold;
}
public void setThreshold(long threshold) {
this.threshold = threshold;
}
}
/**
* Query logging listener is the most used listener that logs executing query with
* actual parameters to. You can pick one of the following proxy logging
* mechanisms.
*/
public enum DataSourceProxyLogging {
/**
* Log using System.out.
*/
SYSOUT,
/**
* Log using SLF4J.
*/
SLF4J,
/**
* Log using Commons.
*/
COMMONS,
/**
* Log using Java Util Logging.
*/
JUL
}
}
/**
* Properties for configuring p6spy.
*/
public static class P6SpyProperties {
/**
* Should the p6spy tracing be enabled?
*/
private boolean enabled = true;
/**
* Enables logging JDBC events.
*/
private boolean enableLogging = false;
/**
* Enables multiline output.
*/
private boolean multiline = true;
/**
* Logging to use for logging queries.
*/
private P6SpyLogging logging = P6SpyLogging.SLF4J;
/**
* Name of log file to use (only with logging=file).
*/
private String logFile = "spy.log";
/**
* Custom log format.
*/
private String logFormat;
/**
* Tracing related properties.
*/
private P6SpyTracing tracing = new P6SpyTracing();
/**
* Class file to use (only with logging=custom). The class must implement
* {@link com.p6spy.engine.spy.appender.FormattedLogger}.
*/
private String customAppenderClass;
/**
* Log filtering related properties.
*/
private P6SpyLogFilter logFilter = new P6SpyLogFilter();
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isEnableLogging() {
return this.enableLogging;
}
public void setEnableLogging(boolean enableLogging) {
this.enableLogging = enableLogging;
}
public boolean isMultiline() {
return this.multiline;
}
public void setMultiline(boolean multiline) {
this.multiline = multiline;
}
public P6SpyLogging getLogging() {
return this.logging;
}
public void setLogging(P6SpyLogging logging) {
this.logging = logging;
}
public String getLogFile() {
return this.logFile;
}
public void setLogFile(String logFile) {
this.logFile = logFile;
}
public String getLogFormat() {
return this.logFormat;
}
public void setLogFormat(String logFormat) {
this.logFormat = logFormat;
}
public P6SpyTracing getTracing() {
return this.tracing;
}
public void setTracing(P6SpyTracing tracing) {
this.tracing = tracing;
}
public String getCustomAppenderClass() {
return this.customAppenderClass;
}
public void setCustomAppenderClass(String customAppenderClass) {
this.customAppenderClass = customAppenderClass;
}
public P6SpyLogFilter getLogFilter() {
return this.logFilter;
}
public void setLogFilter(P6SpyLogFilter logFilter) {
this.logFilter = logFilter;
}
/**
* P6Spy logging options.
*/
public enum P6SpyLogging {
/**
* Log using System.out.
*/
SYSOUT,
/**
* Log using SLF4J.
*/
SLF4J,
/**
* Log to file.
*/
FILE,
/**
* Custom logging.
*/
CUSTOM
}
public static class P6SpyTracing {
/**
* Report the effective sql string (with '?' replaced with real values) to
* tracing systems.
* <p>
* NOTE this setting does not affect the logging message.
*/
private boolean includeParameterValues = true;
public boolean isIncludeParameterValues() {
return this.includeParameterValues;
}
public void setIncludeParameterValues(boolean includeParameterValues) {
this.includeParameterValues = includeParameterValues;
}
}
public static class P6SpyLogFilter {
/**
* Use regex pattern to filter log messages. Only matched messages will be
* logged.
*/
private Pattern pattern;
public Pattern getPattern() {
return this.pattern;
}
public void setPattern(Pattern pattern) {
this.pattern = pattern;
}
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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.kafka;
import org.apache.kafka.clients.KafkaClient;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.kafka.TracingKafkaPropagatorGetter;
import org.springframework.cloud.sleuth.instrument.kafka.TracingKafkaPropagatorSetter;
import org.springframework.cloud.sleuth.propagation.Propagator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that registers instrumentation for Kafka.
*
* @author Anders Clausen
* @author Flaviu Muresan
* @since 3.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(KafkaClient.class)
@ConditionalOnBean(Tracer.class)
@AutoConfigureAfter(BraveAutoConfiguration.class)
@ConditionalOnProperty(value = "spring.sleuth.kafka.enabled", matchIfMissing = true)
public class TracingKafkaAutoConfiguration {
@Bean
@ConditionalOnMissingBean(value = ProducerRecord.class, parameterizedContainer = Propagator.Setter.class)
Propagator.Setter<ProducerRecord<?, ?>> tracingKafkaPropagationSetter() {
return new TracingKafkaPropagatorSetter();
}
@Bean
@ConditionalOnMissingBean(value = ConsumerRecord.class, parameterizedContainer = Propagator.Getter.class)
Propagator.Getter<ConsumerRecord<?, ?>> tracingKafkaPropagationGetter() {
return new TracingKafkaPropagatorGetter();
}
@Bean
static TracingKafkaProducerBeanPostProcessor tracingKafkaProducerBeanPostProcessor(BeanFactory beanFactory) {
return new TracingKafkaProducerBeanPostProcessor(beanFactory);
}
@Bean
static TracingKafkaConsumerBeanPostProcessor tracingKafkaConsumerBeanPostProcessor(BeanFactory beanFactory) {
return new TracingKafkaConsumerBeanPostProcessor(beanFactory);
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.kafka;
import org.apache.kafka.clients.consumer.Consumer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.sleuth.instrument.kafka.TracingKafkaConsumer;
/**
* Bean post processor for {@link org.apache.kafka.clients.consumer.Consumer}.
*
* @author Anders Clausen
* @author Flaviu Muresan
* @since 3.1.0
*/
public class TracingKafkaConsumerBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
public TracingKafkaConsumerBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Consumer && !(bean instanceof TracingKafkaConsumer)) {
return new TracingKafkaConsumer<>((Consumer) bean, beanFactory);
}
return bean;
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.kafka;
import org.apache.kafka.clients.producer.Producer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.sleuth.instrument.kafka.TracingKafkaProducer;
/**
* Bean post processor for {@link org.apache.kafka.clients.producer.Producer}.
*
* @author Anders Clausen
* @author Flaviu Muresan
* @since 3.1.0
*/
public class TracingKafkaProducerBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
public TracingKafkaProducerBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Producer && !(bean instanceof TracingKafkaProducer)) {
return new TracingKafkaProducer<>((Producer) bean, beanFactory);
}
return bean;
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.kafka;
import reactor.kafka.receiver.KafkaReceiver;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.kafka.TracingKafkaConsumerFactory;
import org.springframework.cloud.sleuth.instrument.kafka.TracingKafkaProducerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that registers instrumentation for Reactor Kafka.
*
* @author Anders Clausen
* @author Flaviu Muresan
* @since 3.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(KafkaReceiver.class)
@ConditionalOnBean(Tracer.class)
@AutoConfigureAfter(BraveAutoConfiguration.class)
@ConditionalOnProperty(value = "spring.sleuth.kafka.enabled", matchIfMissing = true)
public class TracingReactorKafkaAutoConfiguration {
@Bean
@ConditionalOnMissingBean
TracingKafkaProducerFactory tracingKafkaProducerFactory(BeanFactory beanFactory) {
return new TracingKafkaProducerFactory(beanFactory);
}
@Bean
@ConditionalOnMissingBean
TracingKafkaConsumerFactory tracingKafkaConsumerFactory(BeanFactory beanFactory) {
return new TracingKafkaConsumerFactory(beanFactory);
}
}

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.sleuth.autoconfig.instrument.messaging;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.messaging.handler.annotation.MessageMapping;
/**
* Properties for messaging.
@@ -32,6 +33,11 @@ public class SleuthMessagingProperties {
*/
private boolean enabled;
/**
* Aspect related properties.
*/
private Aspect aspect = new Aspect();
/**
* Rabbit related properties.
*/
@@ -55,6 +61,14 @@ public class SleuthMessagingProperties {
this.enabled = enabled;
}
public Aspect getAspect() {
return this.aspect;
}
public void setAspect(Aspect aspect) {
this.aspect = aspect;
}
public Rabbit getRabbit() {
return this.rabbit;
}
@@ -79,6 +93,26 @@ public class SleuthMessagingProperties {
this.jms = jms;
}
/**
* Aspect configuration.
*/
public static class Aspect {
/**
* Should {@link MessageMapping} wrapping be enabled.
*/
private boolean enabled;
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
/**
* RabbitMQ configuration.
*/

View File

@@ -16,12 +16,16 @@
package org.springframework.cloud.sleuth.autoconfig.instrument.messaging;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.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.instrument.messaging.MessageHeaderPropagatorGetter;
import org.springframework.cloud.sleuth.instrument.messaging.MessageHeaderPropagatorSetter;
import org.springframework.cloud.sleuth.instrument.messaging.TraceMessagingAspect;
import org.springframework.cloud.sleuth.propagation.Propagator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -29,18 +33,25 @@ import org.springframework.messaging.support.MessageHeaderAccessor;
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(MessageHeaderAccessor.class)
@ConditionalOnBean(Tracer.class)
@ConditionalOnProperty(value = "spring.sleuth.messaging.enabled", matchIfMissing = true)
@EnableConfigurationProperties({ SleuthIntegrationMessagingProperties.class, SleuthMessagingProperties.class })
class TraceSpringMessagingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.sleuth.messaging.aspect.enabled", matchIfMissing = true)
TraceMessagingAspect traceMessagingAspect(Tracer tracer, SpanNamer spanNamer) {
return new TraceMessagingAspect(tracer, spanNamer);
}
@Bean
@ConditionalOnMissingBean(value = MessageHeaderAccessor.class, parameterizedContainer = Propagator.Setter.class)
Propagator.Setter<MessageHeaderAccessor> traceMessagePropagationSetter() {
return new MessageHeaderPropagatorSetter();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnMissingBean(value = MessageHeaderAccessor.class, parameterizedContainer = Propagator.Getter.class)
Propagator.Getter<MessageHeaderAccessor> traceMessagePropagationGetter() {
return new MessageHeaderPropagatorGetter();
}

View File

@@ -0,0 +1,53 @@
/*
* 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.r2dbc;
import io.r2dbc.spi.ConnectionFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* Adds a tracing listener to the {@link ConnectionFactory}.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public class TraceConnectionFactoryBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
public TraceConnectionFactoryBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ConnectionFactory) {
return wrapConnectionFactory((ConnectionFactory) bean);
}
return bean;
}
ConnectionFactory wrapConnectionFactory(ConnectionFactory bean) {
TraceProxyConnectionFactoryWrapper proxyPostProcessor = new TraceProxyConnectionFactoryWrapper(
this.beanFactory);
return proxyPostProcessor.apply(bean);
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.r2dbc;
import java.util.function.Function;
import io.r2dbc.proxy.ProxyConnectionFactory;
import io.r2dbc.proxy.callback.ProxyConfig;
import io.r2dbc.spi.ConnectionFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.sleuth.instrument.r2dbc.TraceProxyExecutionListener;
class TraceProxyConnectionFactoryWrapper implements Function<ConnectionFactory, ConnectionFactory> {
private final BeanFactory beanFactory;
private ObjectProvider<ProxyConfig> proxyConfig;
TraceProxyConnectionFactoryWrapper(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public ConnectionFactory apply(ConnectionFactory connectionFactory) {
ProxyConnectionFactory.Builder builder = ProxyConnectionFactory.builder(connectionFactory);
proxyConfig().ifAvailable(builder::proxyConfig);
builder.listener(new TraceProxyExecutionListener(this.beanFactory, connectionFactory));
return builder.build();
}
private ObjectProvider<ProxyConfig> proxyConfig() {
if (this.proxyConfig == null) {
this.proxyConfig = this.beanFactory.getBeanProvider(ProxyConfig.class);
}
return this.proxyConfig;
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.r2dbc;
import io.r2dbc.proxy.callback.ProxyConfig;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} enables Quartz span information propagation.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ProxyConfig.class)
@ConditionalOnBean(Tracer.class)
@ConditionalOnProperty(value = "spring.sleuth.r2dbc.enabled", matchIfMissing = true)
@AutoConfigureAfter({ BraveAutoConfiguration.class, R2dbcAutoConfiguration.class })
public class TraceR2dbcAutoConfiguration {
@Bean
static TraceConnectionFactoryBeanPostProcessor traceConnectionFactoryBeanPostProcessor(BeanFactory beanFactory) {
return new TraceConnectionFactoryBeanPostProcessor(beanFactory);
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.rsocket;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Sleuth RSocket settings.
*
* @author Oleh Dokuka
* @since 3.1.0
*/
@ConfigurationProperties("spring.sleuth.rsocket")
public class SleuthRSocketProperties {
/**
* When true enables instrumentation for rsocket.
*/
private boolean enabled = true;
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}

View File

@@ -0,0 +1,88 @@
/*
* 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.rsocket;
import java.util.List;
import io.rsocket.RSocket;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
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.autoconfigure.rsocket.RSocketRequesterAutoConfiguration;
import org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.rsocket.server.RSocketServerCustomizer;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.rsocket.TracingRSocketConnectorConfigurer;
import org.springframework.cloud.sleuth.instrument.rsocket.TracingRSocketServerCustomizer;
import org.springframework.cloud.sleuth.propagation.Propagator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.messaging.rsocket.RSocketConnectorConfigurer;
import org.springframework.messaging.rsocket.RSocketRequester;
import org.springframework.messaging.rsocket.RSocketRequester.Builder;
import org.springframework.messaging.rsocket.RSocketStrategies;
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(Tracer.class)
@ConditionalOnProperty(value = "spring.sleuth.rsocket.enabled", matchIfMissing = true)
@ConditionalOnClass({ RSocket.class, RSocketStrategies.class })
@AutoConfigureAfter(BraveAutoConfiguration.class)
@AutoConfigureBefore({ RSocketRequesterAutoConfiguration.class, RSocketServerAutoConfiguration.class })
@EnableConfigurationProperties(SleuthRSocketProperties.class)
public class TraceRSocketAutoConfiguration {
// We're using text instead of objects cause we can have same properties from Brave /
// OTel
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
Builder rSocketRequesterBuilder(RSocketStrategies strategies,
ObjectProvider<RSocketConnectorConfigurer> connectorConfigurerProvider) {
// TODO: should be in spring boot
final Builder builder = RSocketRequester.builder().rsocketStrategies(strategies);
connectorConfigurerProvider.forEach(builder::rsocketConnector);
return builder;
}
private boolean containsZipkinPropagationType(List<String> types) {
return types.stream().anyMatch(s -> s.equalsIgnoreCase("b3"));
}
@Bean
RSocketConnectorConfigurer tracingRSocketConnectorConfigurer(Propagator propagator, Tracer tracer,
@Value("${spring.sleuth.propagation.type:B3}") List<String> types) {
return new TracingRSocketConnectorConfigurer(propagator, tracer, containsZipkinPropagationType(types));
}
// We're using text instead of objects cause we can have same properties from Brave /
// OTel
@Bean
RSocketServerCustomizer tracingRSocketServerCustomizer(Propagator propagator, Tracer tracer,
@Value("${spring.sleuth.propagation.type:B3}") List<String> types) {
return new TracingRSocketServerCustomizer(propagator, tracer, containsZipkinPropagationType(types));
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.session;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.CurrentTraceContext;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.session.TraceSessionRepositoryAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.SessionRepository;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that registers instrumentation for Spring Session.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(SessionRepository.class)
@ConditionalOnProperty(value = "spring.sleuth.session.enabled", matchIfMissing = true)
@ConditionalOnBean(Tracer.class)
@AutoConfigureAfter(BraveAutoConfiguration.class)
public class TraceSessionAutoConfiguration {
// Bean post processors don't work cause session-redis requires concrete classes
@Bean
TraceSessionRepositoryAspect traceSessionRepositoryAspect(Tracer tracer, CurrentTraceContext currentTraceContext) {
return new TraceSessionRepositoryAspect(tracer, currentTraceContext);
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.task;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.ApplicationRunner;
import org.springframework.cloud.sleuth.instrument.task.TraceApplicationRunner;
/**
* Registers beans related to task scheduling.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public class TraceApplicationRunnerBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
public TraceApplicationRunnerBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ApplicationRunner && !(bean instanceof TraceApplicationRunner)) {
return new TraceApplicationRunner(this.beanFactory, (ApplicationRunner) bean, beanName);
}
return bean;
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.task;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cloud.sleuth.instrument.task.TraceCommandLineRunner;
/**
* Registers beans related to task scheduling.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public class TraceCommandLineRunnerBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
public TraceCommandLineRunnerBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof CommandLineRunner && !(bean instanceof TraceCommandLineRunner)) {
return new TraceCommandLineRunner(this.beanFactory, (CommandLineRunner) bean, beanName);
}
return bean;
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.task;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.task.TraceTaskExecutionListener;
import org.springframework.cloud.task.listener.TaskExecutionListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that registers instrumentation for Spring Cloud Task.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(TaskExecutionListener.class)
@ConditionalOnProperty(value = "spring.sleuth.task.enabled", matchIfMissing = true)
@ConditionalOnBean(Tracer.class)
@AutoConfigureAfter(BraveAutoConfiguration.class)
public class TraceTaskAutoConfiguration {
@Bean
TraceTaskExecutionListener traceTaskExecutionListener(Tracer tracer,
@Value("${spring.application.name:default}") String appName) {
return new TraceTaskExecutionListener(tracer, appName);
}
@Bean
static TraceCommandLineRunnerBeanPostProcessor traceCommandLineRunnerBeanPostProcessor(BeanFactory beanFactory) {
return new TraceCommandLineRunnerBeanPostProcessor(beanFactory);
}
@Bean
static TraceApplicationRunnerBeanPostProcessor traceApplicationRunnerBeanPostProcessor(BeanFactory beanFactory) {
return new TraceApplicationRunnerBeanPostProcessor(beanFactory);
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.tx;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.sleuth.instrument.tx.TracePlatformTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
/**
* Post processor that wraps a {@link PlatformTransactionManager}.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public class TracePlatformTransactionManagerBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
public TracePlatformTransactionManagerBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof PlatformTransactionManager && !(bean instanceof TracePlatformTransactionManager)) {
return new TracePlatformTransactionManager((PlatformTransactionManager) bean, this.beanFactory);
}
return bean;
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.tx;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.sleuth.instrument.tx.TraceReactiveTransactionManager;
import org.springframework.transaction.ReactiveTransactionManager;
/**
* Post processor that wraps a {@link ReactiveTransactionManager}.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public class TraceReactiveTransactionManagerBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
public TraceReactiveTransactionManagerBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ReactiveTransactionManager && !(bean instanceof TraceReactiveTransactionManager)) {
return new TraceReactiveTransactionManager((ReactiveTransactionManager) bean, this.beanFactory);
}
return bean;
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.tx;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that registers instrumentation for Spring TX.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "spring.sleuth.tx.enabled", matchIfMissing = true)
@ConditionalOnBean(Tracer.class)
@AutoConfigureAfter(BraveAutoConfiguration.class)
public class TraceTxAutoConfiguration {
@Bean
@ConditionalOnClass(name = "org.springframework.transaction.PlatformTransactionManager")
static TracePlatformTransactionManagerBeanPostProcessor tracePlatformTransactionManagerBeanPostProcessor(
BeanFactory beanFactory) {
return new TracePlatformTransactionManagerBeanPostProcessor(beanFactory);
}
@Bean
@ConditionalOnClass(
name = { "org.springframework.transaction.ReactiveTransactionManager", "reactor.core.publisher.Mono" })
static TraceReactiveTransactionManagerBeanPostProcessor traceReactiveTransactionManagerBeanPostProcessor(
BeanFactory beanFactory) {
return new TraceReactiveTransactionManagerBeanPostProcessor(beanFactory);
}
}

View File

@@ -26,10 +26,14 @@ import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.catalina.Valve;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.web.embedded.tomcat.ConfigurableTomcatWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cloud.sleuth.CurrentTraceContext;
import org.springframework.cloud.sleuth.SpanNamer;
@@ -38,9 +42,12 @@ import org.springframework.cloud.sleuth.http.HttpServerHandler;
import org.springframework.cloud.sleuth.instrument.web.TraceWebAspect;
import org.springframework.cloud.sleuth.instrument.web.mvc.SpanCustomizingAsyncHandlerInterceptor;
import org.springframework.cloud.sleuth.instrument.web.servlet.TracingFilter;
import org.springframework.cloud.sleuth.instrument.web.tomcat.TraceValve;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
@@ -57,74 +64,84 @@ import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(HandlerInterceptorAdapter.class)
@Import(SpanCustomizingAsyncHandlerInterceptor.class)
@ConditionalOnProperty(value = "spring.sleuth.web.servlet.enabled", matchIfMissing = true)
class TraceWebServletConfiguration {
@Bean
TraceWebAspect traceWebAspect(Tracer tracer, CurrentTraceContext currentTraceContext, SpanNamer spanNamer) {
return new TraceWebAspect(tracer, currentTraceContext, spanNamer);
}
@Bean
FilterRegistrationBean traceWebFilter(BeanFactory beanFactory, SleuthWebProperties webProperties) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new LazyTracingFilter(beanFactory));
filterRegistrationBean.setDispatcherTypes(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.FORWARD,
DispatcherType.INCLUDE, DispatcherType.REQUEST);
filterRegistrationBean.setOrder(webProperties.getFilterOrder());
return filterRegistrationBean;
}
/**
* Nested config that configures Web MVC if it's present (without adding a runtime
* dependency to it).
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "spring.sleuth.web.servlet.enabled", matchIfMissing = true)
static class ServletConfiguration {
@ConditionalOnClass(WebMvcConfigurer.class)
@Import(TraceWebMvcConfigurer.class)
protected static class TraceWebMvcAutoConfiguration {
@Bean
TraceWebAspect traceWebAspect(Tracer tracer, CurrentTraceContext currentTraceContext, SpanNamer spanNamer) {
return new TraceWebAspect(tracer, currentTraceContext, spanNamer);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Valve.class, ConfigurableTomcatWebServerFactory.class })
@ConditionalOnProperty(value = "spring.sleuth.web.tomcat.enabled", matchIfMissing = true)
protected static class TraceTomcatConfiguration {
static final String CUSTOMIZER_NAME = "traceTomcatWebServerFactoryCustomizer";
@Bean(name = CUSTOMIZER_NAME)
@Order(Ordered.HIGHEST_PRECEDENCE)
WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory> traceTomcatWebServerFactoryCustomizer(
HttpServerHandler httpServerHandler, CurrentTraceContext currentTraceContext) {
return factory -> factory.addEngineValves(new TraceValve(httpServerHandler, currentTraceContext));
}
@Bean
FilterRegistrationBean traceWebFilter(BeanFactory beanFactory, SleuthWebProperties webProperties) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(
new LazyTracingFilter(beanFactory));
filterRegistrationBean.setDispatcherTypes(DispatcherType.ASYNC, DispatcherType.ERROR,
DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.REQUEST);
filterRegistrationBean.setOrder(webProperties.getFilterOrder());
return filterRegistrationBean;
}
static final class LazyTracingFilter implements Filter {
private final BeanFactory beanFactory;
private Filter tracingFilter;
LazyTracingFilter(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
/**
* Nested config that configures Web MVC if it's present (without adding a runtime
* dependency to it).
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(WebMvcConfigurer.class)
@Import(TraceWebMvcConfigurer.class)
protected static class TraceWebMvcAutoConfiguration {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
tracingFilter().init(filterConfig);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
tracingFilter().doFilter(request, response, chain);
}
@Override
public void destroy() {
tracingFilter().destroy();
}
private Filter tracingFilter() {
if (this.tracingFilter == null) {
this.tracingFilter = TracingFilter.create(this.beanFactory.getBean(CurrentTraceContext.class),
this.beanFactory.getBean(HttpServerHandler.class));
}
return this.tracingFilter;
}
}
}
final class LazyTracingFilter implements Filter {
private final BeanFactory beanFactory;
private Filter tracingFilter;
LazyTracingFilter(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
tracingFilter().init(filterConfig);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
tracingFilter().doFilter(request, response, chain);
}
@Override
public void destroy() {
tracingFilter().destroy();
}
private Filter tracingFilter() {
if (this.tracingFilter == null) {
this.tracingFilter = TracingFilter.create(this.beanFactory.getBean(CurrentTraceContext.class),
this.beanFactory.getBean(HttpServerHandler.class));
}
return this.tracingFilter;
}
}

View File

@@ -20,7 +20,6 @@ import reactor.netty.http.client.HttpClient;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -29,7 +28,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoRestTemplateCustomizer;
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;
import org.springframework.cloud.sleuth.CurrentTraceContext;
@@ -50,6 +48,7 @@ import org.springframework.cloud.sleuth.propagation.Propagator;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.web.client.RestTemplate;
@@ -81,22 +80,27 @@ class TraceWebClientAutoConfiguration {
httpClientHandler);
}
@Bean
@Order
TraceRestTemplateCustomizer traceRestTemplateCustomizer(BeanFactory beanFactory) {
return new TraceRestTemplateCustomizer(new LazyTraceClientHttpRequestInterceptor(beanFactory));
}
@Bean
static TraceRestTemplateBeanPostProcessor traceRestTemplateBeanPostProcessor(ListableBeanFactory beanFactory) {
return new TraceRestTemplateBeanPostProcessor(beanFactory);
}
@Configuration(proxyBeanMethods = false)
protected static class TraceInterceptorConfiguration {
@Autowired
private BeanFactory beanFactory;
@ConditionalOnClass(org.springframework.vault.client.RestTemplateCustomizer.class)
@ConditionalOnProperty(value = "spring.sleuth.vault.enabled", matchIfMissing = true)
static class VaultRestTemplateCustomizerConfiguration {
@Bean
static TraceRestTemplateBeanPostProcessor traceRestTemplateBeanPostProcessor(
ListableBeanFactory beanFactory) {
return new TraceRestTemplateBeanPostProcessor(beanFactory);
}
@Bean
@Order
RestTemplateCustomizer traceRestTemplateCustomizer() {
return new TraceRestTemplateCustomizer(new LazyTraceClientHttpRequestInterceptor(this.beanFactory));
@Order(Ordered.HIGHEST_PRECEDENCE)
org.springframework.vault.client.RestTemplateCustomizer traceVaultRestTemplateCustomizer(
TraceRestTemplateCustomizer restTemplateCustomizer) {
return restTemplateCustomizer::customize;
}
}
@@ -144,6 +148,21 @@ class TraceWebClientAutoConfiguration {
return new TraceWebClientBeanPostProcessor(springContext);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(org.springframework.vault.client.WebClientCustomizer.class)
@ConditionalOnProperty(value = "spring.sleuth.vault.enabled", matchIfMissing = true)
static class VaultWebClientCustomizerConfiguration {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
org.springframework.vault.client.WebClientCustomizer traceVaultWebClientCustomizer(
ConfigurableApplicationContext springContext) {
return webClientBuilder -> new TraceWebClientBeanPostProcessor(springContext)
.postProcessAfterInitialization(webClientBuilder, "");
}
}
}
@Configuration(proxyBeanMethods = false)

View File

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

View File

@@ -1,135 +1,207 @@
{
"properties": [
{
"name": "spring.zipkin.kafka.topic",
"type": "java.lang.String",
"description": "Name of the Kafka topic where spans should be sent to Zipkin.",
"defaultValue": "zipkin"
},
{
"name": "spring.zipkin.rabbitmq.queue",
"type": "java.lang.String",
"description": "Name of the RabbitMQ queue where spans should be sent to Zipkin.",
"defaultValue": "zipkin"
},
{
"name": "spring.zipkin.rabbitmq.addresses",
"type": "java.lang.String",
"description": "Addresses of the RabbitMQ brokers used to send spans to Zipkin"
},
{
"name": "spring.zipkin.activemq.queue",
"type": "java.lang.String",
"description": "Name of the ActiveMQ queue where spans should be sent to Zipkin.",
"defaultValue": "zipkin"
},
{
"name": "spring.zipkin.activemq.message-max-bytes",
"type": "java.lang.String",
"description": "Maximum number of bytes for a given message with spans sent to Zipkin over ActiveMQ.",
"defaultValue": 100000
},
{
"name": "spring.sleuth.function.enabled",
"type": "java.lang.Boolean",
"description": "Enable instrumenting of Spring Cloud Function and Spring Cloud Function based projects (e.g. Spring Cloud Stream).",
"defaultValue": true
},
{
"name": "spring.sleuth.integration.websockets.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing for WebSockets.",
"defaultValue": true
},
{
"name": "spring.sleuth.async.enabled",
"type": "java.lang.Boolean",
"description": "Enable instrumenting async related components so that the tracing information is passed between threads.",
"defaultValue": true
},
{
"name": "spring.sleuth.async.configurer.enabled",
"type": "java.lang.Boolean",
"description": "Enable default AsyncConfigurer.",
"defaultValue": true
},
{
"name": "spring.sleuth.feign.enabled",
"type": "java.lang.Boolean",
"description": "Enable span information propagation when using Feign.",
"defaultValue": true
},
{
"name": "spring.sleuth.feign.processor.enabled",
"type": "java.lang.Boolean",
"description": "Enable post processor that wraps Feign Context in its tracing representations.",
"defaultValue": true
},
{
"name": "spring.sleuth.grpc.enabled",
"type": "java.lang.Boolean",
"description": "Enable span information propagation when using GRPC.",
"defaultValue": true
},
{
"name": "spring.sleuth.messaging.jms.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing of JMS.",
"defaultValue": true
},
{
"name": "spring.sleuth.messaging.rabbit.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing of RabbitMQ.",
"defaultValue": true
},
{
"name": "spring.sleuth.messaging.kafka.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing of Kafka.",
"defaultValue": true
},
{
"name": "spring.sleuth.messaging.kafka.mapper.enabled",
"type": "java.lang.Boolean",
"description": "Enable DefaultKafkaHeaderMapper tracing for Kafka.",
"defaultValue": true
},
{
"name": "spring.sleuth.quartz.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing for Quartz.",
"defaultValue": true
},
{
"name": "spring.sleuth.mongodb.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing for MongoDb.",
"defaultValue": true
},
{
"name": "spring.sleuth.rpc.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing of RPC.",
"defaultValue": true
},
{
"name": "spring.sleuth.sampler.refresh.enabled",
"type": "java.lang.Boolean",
"description": "Enable refresh scope for sampler.",
"defaultValue": true
},
{
"name": "spring.sleuth.web.webclient.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing instrumentation for WebClient.",
"defaultValue": true
},
{
"name": "spring.sleuth.integration.enabled",
"type": "java.lang.Boolean",
"description": "Enable Spring Integration sleuth instrumentation.",
"defaultValue": true
}
]
}
{
"properties": [
{
"name": "spring.zipkin.kafka.topic",
"type": "java.lang.String",
"description": "Name of the Kafka topic where spans should be sent to Zipkin.",
"defaultValue": "zipkin"
},
{
"name": "spring.zipkin.rabbitmq.queue",
"type": "java.lang.String",
"description": "Name of the RabbitMQ queue where spans should be sent to Zipkin.",
"defaultValue": "zipkin"
},
{
"name": "spring.zipkin.rabbitmq.addresses",
"type": "java.lang.String",
"description": "Addresses of the RabbitMQ brokers used to send spans to Zipkin"
},
{
"name": "spring.zipkin.activemq.queue",
"type": "java.lang.String",
"description": "Name of the ActiveMQ queue where spans should be sent to Zipkin.",
"defaultValue": "zipkin"
},
{
"name": "spring.zipkin.activemq.message-max-bytes",
"type": "java.lang.String",
"description": "Maximum number of bytes for a given message with spans sent to Zipkin over ActiveMQ.",
"defaultValue": 100000
},
{
"name": "spring.sleuth.function.enabled",
"type": "java.lang.Boolean",
"description": "Enable instrumenting of Spring Cloud Function and Spring Cloud Function based projects (e.g. Spring Cloud Stream).",
"defaultValue": true
},
{
"name": "spring.sleuth.integration.websockets.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing for WebSockets.",
"defaultValue": true
},
{
"name": "spring.sleuth.kafka.enabled",
"type": "java.lang.Boolean",
"description": "Enable instrumenting of Apache Kafka clients.",
"defaultValue": true
},
{
"name": "spring.sleuth.async.enabled",
"type": "java.lang.Boolean",
"description": "Enable instrumenting async related components so that the tracing information is passed between threads.",
"defaultValue": true
},
{
"name": "spring.sleuth.async.configurer.enabled",
"type": "java.lang.Boolean",
"description": "Enable default AsyncConfigurer.",
"defaultValue": true
},
{
"name": "spring.sleuth.feign.enabled",
"type": "java.lang.Boolean",
"description": "Enable span information propagation when using Feign.",
"defaultValue": true
},
{
"name": "spring.sleuth.feign.processor.enabled",
"type": "java.lang.Boolean",
"description": "Enable post processor that wraps Feign Context in its tracing representations.",
"defaultValue": true
},
{
"name": "spring.sleuth.grpc.enabled",
"type": "java.lang.Boolean",
"description": "Enable span information propagation when using GRPC.",
"defaultValue": true
},
{
"name": "spring.sleuth.messaging.jms.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing of JMS.",
"defaultValue": true
},
{
"name": "spring.sleuth.messaging.rabbit.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing of RabbitMQ.",
"defaultValue": true
},
{
"name": "spring.sleuth.messaging.kafka.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing of Kafka.",
"defaultValue": true
},
{
"name": "spring.sleuth.messaging.kafka.mapper.enabled",
"type": "java.lang.Boolean",
"description": "Enable DefaultKafkaHeaderMapper tracing for Kafka.",
"defaultValue": true
},
{
"name": "spring.sleuth.quartz.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing for Quartz.",
"defaultValue": true
},
{
"name": "spring.sleuth.mongodb.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing for MongoDb.",
"defaultValue": true
},
{
"name": "spring.sleuth.rpc.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing of RPC.",
"defaultValue": true
},
{
"name": "spring.sleuth.sampler.refresh.enabled",
"type": "java.lang.Boolean",
"description": "Enable refresh scope for sampler.",
"defaultValue": true
},
{
"name": "spring.sleuth.web.webclient.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing instrumentation for WebClient.",
"defaultValue": true
},
{
"name": "spring.sleuth.web.tomcat.enabled",
"type": "java.lang.Boolean",
"description": "Enable tracing instrumentation for Tomcat.",
"defaultValue": true
},
{
"name": "spring.sleuth.integration.enabled",
"type": "java.lang.Boolean",
"description": "Enable Spring Integration instrumentation.",
"defaultValue": true
},
{
"name": "spring.sleuth.task.enabled",
"type": "java.lang.Boolean",
"description": "Enable Spring Cloud Task instrumentation.",
"defaultValue": true
},
{
"name": "spring.sleuth.deployer.enabled",
"type": "java.lang.Boolean",
"description": "Enable Spring Cloud Deployer instrumentation.",
"defaultValue": true
},
{
"name": "spring.sleuth.deployer.status-poll-delay",
"type": "java.lang.Long",
"description": "Default poll delay to retrieve the deployed application status.",
"defaultValue": 500
},
{
"name": "spring.sleuth.config.server.enabled",
"type": "java.lang.Boolean",
"description": "Enable Spring Cloud Config Server instrumentation.",
"defaultValue": true
},
{
"name": "spring.sleuth.tx.enabled",
"type": "java.lang.Boolean",
"description": "Enable Spring TX instrumentation.",
"defaultValue": true
},
{
"name": "spring.sleuth.batch.enabled",
"type": "java.lang.Boolean",
"description": "Enable Spring Batch instrumentation.",
"defaultValue": true
},
{
"name": "spring.sleuth.r2dbc.enabled",
"type": "java.lang.Boolean",
"description": "Enable R2dbc instrumentation.",
"defaultValue": true
},
{
"name": "spring.sleuth.session.enabled",
"type": "java.lang.Boolean",
"description": "Enable Spring Session instrumentation.",
"defaultValue": true
},
{
"name": "spring.sleuth.cassandra.enabled",
"type": "java.lang.Boolean",
"description": "Enable Cassandra instrumentation.",
"defaultValue": true
},
{
"name": "spring.sleuth.vault.enabled",
"type": "java.lang.Boolean",
"description": "Enable Spring Vault instrumentation.",
"defaultValue": true
}
]
}

View File

@@ -1,32 +1,46 @@
# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.sleuth.autoconfig.instrument.async.TraceAsyncAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.async.TraceAsyncCustomAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.async.TraceAsyncDefaultAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.circuitbreaker.TraceCircuitBreakerAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.rxjava.TraceRxJavaAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.quartz.TraceQuartzAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.web.TraceWebAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.web.client.TraceWebClientAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.web.client.feign.TraceFeignClientAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.web.client.TraceWebAsyncClientAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.scheduling.TraceSchedulingAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.reactor.TraceReactorAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.messaging.TraceFunctionAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.messaging.TraceSpringIntegrationAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.messaging.TraceSpringMessagingAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.messaging.TraceWebSocketAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.instrument.web.client.BraveWebClientAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.instrument.rpc.BraveRpcAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.instrument.grpc.BraveGrpcAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.instrument.messaging.BraveKafkaStreamsAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.instrument.messaging.BraveMessagingAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.instrument.opentracing.BraveOpentracingAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.instrument.redis.BraveRedisAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.instrument.mongodb.BraveMongoDbAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.zipkin2.ZipkinAutoConfiguration
# Environment Post Processor
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.sleuth.autoconfig.TraceEnvironmentPostProcessor,\
org.springframework.cloud.sleuth.autoconfig.instrument.web.client.TraceGatewayEnvironmentPostProcessor
# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.sleuth.autoconfig.actuate.TraceSleuthActuatorAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.kafka.TracingKafkaAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.kafka.TracingReactorKafkaAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.async.TraceAsyncAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.async.TraceAsyncCustomAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.async.TraceAsyncDefaultAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.batch.TraceBatchAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.cassandra.TraceCassandraAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.cassandra.TraceCassandraReactiveAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.config.TraceSpringCloudConfigAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.circuitbreaker.TraceCircuitBreakerAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.deployer.TraceDeployerAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.jdbc.TraceJdbcAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.rxjava.TraceRxJavaAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.quartz.TraceQuartzAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.task.TraceTaskAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.web.TraceWebAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.web.client.TraceWebClientAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.web.client.feign.TraceFeignClientAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.web.client.TraceWebAsyncClientAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.scheduling.TraceSchedulingAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.session.TraceSessionAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.reactor.TraceReactorAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.messaging.TraceFunctionAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.messaging.TraceSpringIntegrationAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.messaging.TraceSpringMessagingAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.messaging.TraceWebSocketAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.rsocket.TraceRSocketAutoConfiguration, \
org.springframework.cloud.sleuth.autoconfig.instrument.r2dbc.TraceR2dbcAutoConfiguration, \
org.springframework.cloud.sleuth.autoconfig.instrument.tx.TraceTxAutoConfiguration, \
org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.instrument.web.client.BraveWebClientAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.instrument.rpc.BraveRpcAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.instrument.grpc.BraveGrpcAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.instrument.messaging.BraveKafkaStreamsAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.instrument.messaging.BraveMessagingAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.instrument.opentracing.BraveOpentracingAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.instrument.redis.BraveRedisAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.instrument.mongodb.BraveMongoDbAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.zipkin2.ZipkinAutoConfiguration
# Environment Post Processor
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.sleuth.autoconfig.TraceEnvironmentPostProcessor,\
org.springframework.cloud.sleuth.autoconfig.instrument.web.client.TraceGatewayEnvironmentPostProcessor

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig;
import org.springframework.cloud.sleuth.TraceContext;
/**
* A noop implementation. Does nothing.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
class NoOpTraceContextBuilder implements TraceContext.Builder {
@Override
public TraceContext.Builder traceId(String traceId) {
return this;
}
@Override
public TraceContext.Builder parentId(String traceId) {
return this;
}
@Override
public TraceContext.Builder spanId(String spanId) {
return null;
}
@Override
public TraceContext.Builder sampled(Boolean sampled) {
return this;
}
@Override
public TraceContext build() {
return new NoOpTraceContext();
}
}

View File

@@ -69,6 +69,11 @@ class NoOpTracer implements Tracer {
return new NoOpSpanBuilder();
}
@Override
public TraceContext.Builder traceContextBuilder() {
return new NoOpTraceContextBuilder();
}
@Override
public Map<String, String> getAllBaggage() {
return new HashMap<>();

View File

@@ -16,6 +16,12 @@
package org.springframework.cloud.sleuth.autoconfig;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -42,6 +48,7 @@ import org.springframework.context.annotation.Import;
* @since 2.0.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnSleuth
@ConditionalOnProperty("spring.sleuth.noop.enabled")
@AutoConfigureBefore(BraveAutoConfiguration.class)
@Import(TraceConfiguration.class)
@@ -86,3 +93,11 @@ public class TraceNoOpAutoConfiguration {
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@ConditionalOnProperty(value = "spring.sleuth.enabled", matchIfMissing = true)
@interface ConditionalOnSleuth {
}

View File

@@ -0,0 +1,59 @@
/*
* 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.actuate;
import java.util.Collections;
import brave.handler.MutableSpan;
import brave.propagation.TraceContext;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.sleuth.brave.bridge.BraveFinishedSpan;
import org.springframework.cloud.sleuth.exporter.FinishedSpan;
import static org.assertj.core.api.BDDAssertions.then;
class BraveFinishedSpanWriterTests {
@Test
void should_convert_finished_spans_to_zipkin_json() {
FinishedSpan finishedSpan = new BraveFinishedSpan(
new MutableSpan(TraceContext.newBuilder().spanId(1L).traceId(2L).build(), null));
String json = new BraveFinishedSpanWriter().write(TextOutputFormat.CONTENT_TYPE_OPENZIPKIN_JSON_V2,
Collections.singletonList(finishedSpan));
then(json).isEqualTo("[{\"traceId\":\"0000000000000002\",\"id\":\"0000000000000001\"}]");
}
@Test
void should_not_support_any_other_format_than_openzipkin() {
FinishedSpan finishedSpan = new BraveFinishedSpan(
new MutableSpan(TraceContext.newBuilder().spanId(1L).traceId(2L).build(), null));
String json = new BraveFinishedSpanWriter().write(TextOutputFormat.CONTENT_TYPE_OTLP_PROTOBUF,
Collections.singletonList(finishedSpan));
then(json).isNull();
}
@Test
void should_return_null_when_format_not_supported() {
then(new BraveFinishedSpanWriter().write(null, Collections.emptyList())).isNull();
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.actuate;
import org.assertj.core.api.BDDAssertions;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.sleuth.exporter.FinishedSpan;
import static org.mockito.Mockito.mock;
class BufferingSpanReporterTests {
@Test
void should_overwrite_the_oldest_element_with_the_newest_when_queue_is_full() {
BufferingSpanReporter reporter = new BufferingSpanReporter(2);
FinishedSpan oldest = mock(FinishedSpan.class, "oldest");
FinishedSpan second = mock(FinishedSpan.class, "second");
FinishedSpan youngest = mock(FinishedSpan.class, "youngest");
reporter.report(oldest);
reporter.report(second);
reporter.report(youngest);
BDDAssertions.then(reporter.spans).containsExactly(second, youngest);
}
}

Some files were not shown because too many files have changed in this diff Show More