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.
diff --git a/docs/src/main/asciidoc/_spans.adoc b/docs/src/main/asciidoc/_spans.adoc
new file mode 100644
index 000000000..bca12b758
--- /dev/null
+++ b/docs/src/main/asciidoc/_spans.adoc
@@ -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.
+|===
+
diff --git a/docs/src/main/asciidoc/appendix.adoc b/docs/src/main/asciidoc/appendix.adoc
index b2d7034c2..4798f62e5 100644
--- a/docs/src/main/asciidoc/appendix.adoc
+++ b/docs/src/main/asciidoc/appendix.adoc
@@ -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[]
\ No newline at end of file
+: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[]
diff --git a/docs/src/main/asciidoc/documentation-overview.adoc b/docs/src/main/asciidoc/documentation-overview.adoc
index 9bae80a47..741e3bcca 100644
--- a/docs/src/main/asciidoc/documentation-overview.adoc
+++ b/docs/src/main/asciidoc/documentation-overview.adoc
@@ -77,6 +77,7 @@ Need more details about {project-full-name}'s core features?
Finally, we have topics related to instrumentation integrations:
* *Integrations:*
+<> |
<> |
<> |
<> |
@@ -91,4 +92,4 @@ Finally, we have topics related to instrumentation integrations:
<> |
<> |
<> |
-<>
\ No newline at end of file
+<>
diff --git a/docs/src/main/asciidoc/index.adoc b/docs/src/main/asciidoc/index.adoc
deleted file mode 120000
index 2ab5e96e8..000000000
--- a/docs/src/main/asciidoc/index.adoc
+++ /dev/null
@@ -1 +0,0 @@
-spring-cloud-sleuth.adoc
\ No newline at end of file
diff --git a/docs/src/main/asciidoc/index.adoc b/docs/src/main/asciidoc/index.adoc
new file mode 100644
index 000000000..dd1a0a718
--- /dev/null
+++ b/docs/src/main/asciidoc/index.adoc
@@ -0,0 +1 @@
+include::spring-cloud-sleuth.adoc[]
diff --git a/docs/src/main/asciidoc/index.htmladoc b/docs/src/main/asciidoc/index.htmladoc
deleted file mode 120000
index 2ab5e96e8..000000000
--- a/docs/src/main/asciidoc/index.htmladoc
+++ /dev/null
@@ -1 +0,0 @@
-spring-cloud-sleuth.adoc
\ No newline at end of file
diff --git a/docs/src/main/asciidoc/index.htmlsingleadoc b/docs/src/main/asciidoc/index.htmlsingleadoc
deleted file mode 120000
index f8d93d10b..000000000
--- a/docs/src/main/asciidoc/index.htmlsingleadoc
+++ /dev/null
@@ -1 +0,0 @@
-spring-cloud-sleuth.htmlsingleadoc
\ No newline at end of file
diff --git a/docs/src/main/asciidoc/index.pdfadoc b/docs/src/main/asciidoc/index.pdfadoc
deleted file mode 120000
index d4beca5b9..000000000
--- a/docs/src/main/asciidoc/index.pdfadoc
+++ /dev/null
@@ -1 +0,0 @@
-spring-cloud-sleuth.pdfadoc
\ No newline at end of file
diff --git a/docs/src/main/asciidoc/integrations.adoc b/docs/src/main/asciidoc/integrations.adoc
index 79c796170..21588a64c 100644
--- a/docs/src/main/asciidoc/integrations.adoc
+++ b/docs/src/main/asciidoc/integrations.adoc
@@ -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 <> 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 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`.
\ No newline at end of file
+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
+----
+
+ p6spy
+ p6spy
+ ${p6spy.version}
+ runtime
+
+----
+
+[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
+----
+
+ net.ttddyy
+ datasource-proxy
+ ${datasource-proxy.version}
+ runtime
+
+----
+
+[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
+.Datasource Proxy Gradle
+----
+runtimeOnly "net.ttddyy:datasource-proxy:${datasourceProxyVersion}"
+----
+====
+
+Please check the <> 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.
diff --git a/docs/src/main/asciidoc/project-features.adoc b/docs/src/main/asciidoc/project-features.adoc
index fb84d25c7..f871283cd 100644
--- a/docs/src/main/asciidoc/project-features.adoc
+++ b/docs/src/main/asciidoc/project-features.adoc
@@ -1,494 +1,506 @@
-[[features]]
-[[project-features]]
-= Spring Cloud Sleuth Features
-
-include::_attributes.adoc[]
-
-This section dives into the details of {project-full-name}.
-Here you can learn about the key features that you may want to use and customize.
-If you have not already done so, you might want to read the "<>" and "<>" sections, so that you have a good grounding in the basics.
-
-[[features-context-propagation]]
-== Context Propagation
-
-Traces connect from service to service using header propagation.
-The default format is https://github.com/openzipkin/b3-propagation[B3].
-Similar to data formats, you can configure alternate header formats also, provided trace and span IDs are compatible with B3. Most notably, this means the trace ID and span IDs are lower-case hex, not UUIDs.
-Besides trace identifiers, other properties (Baggage) can also be passed along with the request.
-Remote Baggage must be predefined, but is flexible otherwise.
-
-To use the provided defaults you can set the `spring.sleuth.propagation.type` property.
-The value can be a list in which case you will propagate more tracing headers.
-
-For Brave we support `AWS`, `B3`, `W3C` propagation types.
-
-You can read more about how to provide custom context propagation in this "<>".
-
-[[features-sampling]]
-== Sampling
-
-Spring Cloud Sleuth pushes the sampling decision down to the tracer implementation.
-However, there are cases where you can change the sampling decision at runtime.
-
-One of such cases is skip reporting of certain client spans.
-To achieve that you can set the `spring.sleuth.web.client.skip-pattern` with the path patterns to be skipped.
-Another option is to provide your own custom `org.springframework.cloud.sleuth.SamplerFunction<`org.springframework.cloud.sleuth.http.HttpRequest>` implementation and define when a given `HttpRequest` should not be sampled.
-
-[[features-baggage]]
-== Baggage
-
-Distributed tracing works by propagating fields inside and across services that connect the trace together: traceId and spanId notably.
-The context that holds these fields can optionally push other fields that need to be consistent regardless of many services are touched.
-The simple name for these extra fields is "Baggage".
-
-Sleuth allows you to define which baggage are permitted to exist in the trace context, including what header names are used.
-
-The following example shows setting baggage values using Spring Cloud Sleuth's API:
-
-[source,java,indent=0]
-----
-include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/baggage/multiple/MultipleHopsIntegrationTests.java[tags=baggage,indent=0]
-----
-
-IMPORTANT: There is currently no limitation of the count or size of baggage items.
-Keep in mind that too many can decrease system throughput or increase RPC latency.
-In extreme cases, too much baggage can crash the application, due to exceeding transport-level message or header capacity.
-
-You can use properties to define fields that have no special configuration such as name mapping:
-
-* `spring.sleuth.baggage.remote-fields` is a list of header names to accept and propagate to remote services.
-* `spring.sleuth.baggage.local-fields` is a list of names to propagate locally
-
-No prefixing applies with these keys.
-What you set is literally what is used.
-
-A name set in either of these properties will result in a `Baggage` of the same name.
-
-In order to automatically set the baggage values to Slf4j's MDC, you have to set the `spring.sleuth.baggage.correlation-fields` property with a list of allowed local or remote keys. E.g. `spring.sleuth.baggage.correlation-fields=country-code` will set the value of the `country-code` baggage into MDC.
-
-Note that the extra field is propagated and added to MDC starting with the next downstream trace context.
-To immediately add the extra field to MDC in the current trace context, configure the field to flush on update:
-
-```
-// configuration
-@Bean
-BaggageField countryCodeField() {
- return BaggageField.create("country-code");
-}
-
-@Bean
-ScopeDecorator mdcScopeDecorator() {
- return MDCScopeDecorator.newBuilder()
- .clear()
- .add(SingleCorrelationField.newBuilder(countryCodeField())
- .flushOnUpdate()
- .build())
- .build();
-}
-
-// service
-@Autowired
-BaggageField countryCodeField;
-
-countryCodeField.updateValue("new-value");
-```
-
-IMPORTANT: Remember that adding entries to MDC can drastically decrease the performance of your application!
-
-If you want to add the baggage entries as tags, to make it possible to search for spans via the baggage entries, you can set the value of
-`spring.sleuth.baggage.tag-fields` with a list of allowed baggage keys.
-To disable the feature you have to pass the `spring.sleuth.propagation.tag.enabled=false` property.
-
-[[features-baggage-vs-tags]]
-=== Baggage versus Tags
-
-Like trace IDs, Baggage is attached to messages or requests, usually as headers.
-Tags are key value pairs sent in a Span to Zipkin.
-Baggage values are not added spans by default, which means you can't search based on Baggage unless you opt-in.
-
-To make baggage also tags, use the property `spring.sleuth.baggage.tag-fields`
-like so:
-
-[source,yml]
-----
-include::{brave_path}/src/test/resources/application-baggage.yml[indent=0]
-----
-
-[[features-brave]]
-== OpenZipkin Brave Tracer Integration
-
-Spring Cloud Sleuth integrates with the OpenZipkin Brave tracer via the bridge that is available in the `spring-cloud-sleuth-brave` module.
-In this section you can read about specific Brave integrations.
-
-You can choose to use either Sleuth's API or the Brave API directly in your code (e.g. either Sleuth's `Tracer` or Brave's `Tracer`).
-If you want to use this tracer implementation's API directly please read https://github.com/openzipkin/brave[their documentation to learn more about it].
-
-[[features-brave-basics]]
-=== Brave Basics
-
-Here are the most core types you might use:
-
-* `brave.SpanCustomizer` - to change the span currently in progress
-* `brave.Tracer` - to get a start new spans ad-hoc
-
-Here are the most relevant links from the OpenZipkin Brave project:
-
-* https://github.com/openzipkin/brave/tree/master/brave[Brave's core library]
-* https://github.com/openzipkin/brave/tree/master/brave#baggage[Baggage (propagated fields)]
-* https://github.com/openzipkin/brave/tree/master/instrumentation/http[HTTP tracing]
-
-[[features-brave-sampling]]
-=== Brave Sampling
-
-Sampling only applies to tracing backends, such as Zipkin.
-Trace IDs appear in logs regardless of sample rate.
-Sampling is a way to prevent overloading the system, by consistently tracing some, but not all requests.
-
-The default rate of 10 traces per second is controlled by the `spring.sleuth.sampler.rate`
-property and applies when we know Sleuth is used for reasons besides logging.
-Use a rate above 100 traces per second with extreme caution as it can overload your tracing system.
-
-The sampler can be set by Java Config also, as shown in the following example:
-
-[source,java,indent=0]
-----
-include::{brave_path}/src/test/java/org/springframework/cloud/sleuth/brave/SpringCloudSleuthDocTests.java[tags=always_sampler,indent=0]
-----
-
-TIP: You can set the HTTP header `b3` to `1`, or, when doing messaging, you can set the `spanFlags` header to `1`.
-Doing so forces the current request to be sampled regardless of configuration.
-
-By default samplers will work with the refresh scope mechanism.
-That means that you can change the sampling properties at runtime, refresh the application and the changes will be reflected.
-However, sometimes the fact of creating a proxy around samplers and calling it from too early (from `@PostConstruct` annotated method) may lead to dead locks.
-In such a case either create a sampler bean explicitly, or set the property `spring.sleuth.sampler.refresh.enabled` to `false` to disable the refresh scope support.
-
-[[features-brave-baggage]]
-=== Brave Baggage Java configuration
-
-If you need to do anything more advanced than above, do not define properties and instead use a
-`@Bean` config for the baggage fields you use.
-
-* `BaggagePropagationCustomizer` sets up baggage fields
-* Add a `SingleBaggageField` to control header names for a `Baggage`.
-* `CorrelationScopeCustomizer` sets up MDC fields
-* Add a `SingleCorrelationField` to change the MDC name of a `Baggage` or if updates flush.
-
-[[features-brave-customizations]]
-=== Brave Customizations
-
-The `brave.Tracer` object is fully managed by sleuth, so you rarely need to affect it.
-That said, Sleuth supports a number of `Customizer` types, that allow you to configure anything not already done by Sleuth with auto-configuration or properties.
-
-If you define one of the following as a `Bean`, Sleuth will invoke it to customize behaviour:
-
-* `RpcTracingCustomizer` - for RPC tagging and sampling policy
-* `HttpTracingCustomizer` - for HTTP tagging and sampling policy
-* `MessagingTracingCustomizer` - for messaging tagging and sampling policy
-* `CurrentTraceContextCustomizer` - to integrate decorators such as correlation.
-* `BaggagePropagationCustomizer` - for propagating baggage fields in process and over headers
-* `CorrelationScopeDecoratorCustomizer` - for scope decorations such as MDC (logging) field correlation
-
-[[features-brave-sampling-customizations]]
-==== Brave Sampling Customizations
-
-If client /server sampling is required, just register a bean of type
-`brave.sampler.SamplerFunction` and name the bean
-`sleuthHttpClientSampler` for client sampler and `sleuthHttpServerSampler`
-for server sampler.
-
-For your convenience the `@HttpClientSampler` and `@HttpServerSampler`
-annotations can be used to inject the proper beans or to reference the bean names via their static String `NAME` fields.
-
-Check out Brave's code to see an example of how to make a path-based sampler
-https://github.com/openzipkin/brave/tree/master/instrumentation/http#sampling-policy
-
-If you want to completely rewrite the `HttpTracing` bean you can use the `SkipPatternProvider`
-interface to retrieve the URL `Pattern` for spans that should be not sampled.
-Below you can see an example of usage of `SkipPatternProvider` inside a server side, `Sampler`.
-
-[source,java,indent=0]
-----
-@Configuration(proxyBeanMethods = false)
- class Config {
-include::{tests_path}/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceFilterWebIntegrationTests.java[tags=custom_server_sampler,indent=2]
-}
-----
-
-[[features-brave-messaging]]
-=== Brave Messaging
-
-Sleuth automatically configures the `MessagingTracing` bean which serves as a foundation for Messaging instrumentation such as Kafka or JMS.
-
-If a customization of producer / consumer sampling of messaging traces is required, just register a bean of type `brave.sampler.SamplerFunction` and name the bean `sleuthProducerSampler` for producer sampler and `sleuthConsumerSampler`
-for consumer sampler.
-
-For your convenience the `@ProducerSampler` and `@ConsumerSampler`
-annotations can be used to inject the proper beans or to reference the bean names via their static String `NAME` fields.
-
-Ex.
-Here's a sampler that traces 100 consumer requests per second, except for the "alerts" channel.
-Other requests will use a global rate provided by the
-`Tracing` component.
-
-[source,java,indent=0]
-----
-@Configuration(proxyBeanMethods = false)
- class Config {
-include::{autoconfig_path}/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/messaging/BraveMessagingAutoConfigurationIntegrationTests.java[tags=custom_messaging_consumer_sampler,indent=2]
-}
-----
-
-For more, see https://github.com/openzipkin/brave/tree/master/instrumentation/messaging#sampling-policy
-
-[[features-brave-opentracing]]
-=== Brave Opentracing
-
-You can integrate with Brave and https://opentracing.io/[OpenTracing] via the
-`io.opentracing.brave:brave-opentracing` bridge.
-Just add it to the classpath and the OpenTracing `Tracer` will be set up automatically.
-
-[[features-zipkin]]
-== Sending Spans to Zipkin
-
-Spring Cloud Sleuth provides various integrations with the https://zipkin.io[OpenZipkin] distributed tracing system.
-Regardless of the chosen tracer implementation it's enough to add `spring-cloud-sleuth-zipkin` to the classpath to start sending spans to Zipkin.
-You can choose whether to do that via HTTP or messaging.
-You can read more about how to do that in "<>".
-
-When the span is closed, it is sent to Zipkin over HTTP. The communication is asynchronous.
-You can configure the URL by setting the `spring.zipkin.baseUrl` property, as follows:
-
-[source,yaml]
-----
-spring.zipkin.baseUrl: https://192.168.99.100:9411/
-----
-
-If you want to find Zipkin through service discovery, you can pass the Zipkin's service ID inside the URL, as shown in the following example for `zipkinserver` service ID:
-
-[source,yaml]
-----
-spring.zipkin.baseUrl: https://zipkinserver/
-----
-
-To disable this feature just set `spring.zipkin.discovery-client-enabled` to `false`.
-
-When the Discovery Client feature is enabled, Sleuth uses
-`LoadBalancerClient` to find the URL of the Zipkin Server.
-It means that you can set up the load balancing configuration.
-
-If you have `web`, `rabbit`, `activemq` or `kafka` together on the classpath, you might need to pick the means by which you would like to send spans to zipkin.
-To do so, set `web`, `rabbit`, `activemq` or `kafka` to the `spring.zipkin.sender.type` property.
-The following example shows setting the sender type for `web`:
-
-[source,yaml]
-----
-spring.zipkin.sender.type: web
-----
-
-To customize the `RestTemplate` that sends spans to Zipkin via HTTP, you can register the `ZipkinRestTemplateCustomizer` bean.
-
-[source,java,indent=0]
-----
-@Configuration(proxyBeanMethods = false)
- class MyConfig {
- @Bean ZipkinRestTemplateCustomizer myCustomizer() {
- return new ZipkinRestTemplateCustomizer() {
- @Override
- void customize(RestTemplate restTemplate) {
- // customize the RestTemplate
- }
- };
- }
-}
-----
-
-If, however, you would like to control the full process of creating the `RestTemplate`
-object, you will have to create a bean of `zipkin2.reporter.Sender` type.
-
-[source,java,indent=0]
-----
- @Bean Sender myRestTemplateSender(ZipkinProperties zipkin,
- ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer) {
- RestTemplate restTemplate = mySuperCustomRestTemplate();
- zipkinRestTemplateCustomizer.customize(restTemplate);
- return myCustomSender(zipkin, restTemplate);
- }
-----
-
-By default, api path will be set to `api/v2/spans` or `api/v1/spans` depending on the encoder version. If you want to use a custom api path, you can configure it using the following property (empty case, set ""):
-
-[source,yaml]
-----
-spring.zipkin.api-path: v2/path2
-----
-
-[[features-zipkin-custom-service-name]]
-=== Custom service name
-
-By default, Sleuth assumes that, when you send a span to Zipkin, you want the span's service name to be equal to the value of the `spring.application.name` property.
-That is not always the case, though.
-There are situations in which you want to explicitly provide a different service name for all spans coming from your application.
-To achieve that, you can pass the following property to your application to override that value (the example is for a service named `myService`):
-
-[source,yaml]
-----
-spring.zipkin.service.name: myService
-----
-
-[[features-zipkin-host-locator]]
-=== Host Locator
-
-IMPORTANT: This section is about defining *host* from service discovery.
-It is *NOT* about finding Zipkin through service discovery.
-
-To define the host that corresponds to a particular span, we need to resolve the host name and port.
-The default approach is to take these values from server properties.
-If those are not set, we try to retrieve the host name from the network interfaces.
-
-If you have the discovery client enabled and prefer to retrieve the host address from the registered instance in a service registry, you have to set the `spring.zipkin.locator.discovery.enabled` property (it is applicable for both HTTP-based and Stream-based span reporting), as follows:
-
-[source,yaml]
-----
-spring.zipkin.locator.discovery.enabled: true
-----
-
-[[features-zipkin-custom-reported-spans]]
-=== Customization of Reported Spans
-
-In Sleuth, we generate spans with a fixed name.
-Some users want to modify the name depending on values of tags.
-
-Sleuth registers a `SpanFilter` bean that can automatically skip reporting spans of given name patterns.
-The property `spring.sleuth.span-filter.span-name-patterns-to-skip` contains the default skip patterns for span names.
-The property `spring.sleuth.span-filter.additional-span-name-patterns-to-skip` will append the provided span name patterns to the existing ones.
-In order to disable this functionality just set `spring.sleuth.span-filter.enabled` to `false`.
-
-[[features-zipkin-custom-reported-spans-brave]]
-==== Brave Customization of Reported Spans
-
-IMPORTANT: This section is applicable for Brave tracer only.
-
-Before reporting spans (for example, to Zipkin) you may want to modify that span in some way.
-You can do so by implementing a `SpanHandler`.
-
-The following example shows how to register two beans that implement `SpanHandler`:
-
-[source,java,indent=0]
-----
-include::{autoconfig_path}/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/SpanHandlerTests.java[tags=spanHandler,indent=0]
-----
-
-The preceding example results in changing the name of the reported span to `foo bar`, just before it gets reported (for example, to Zipkin).
-
-=== Overriding the auto-configuration of Zipkin
-
-Spring Cloud Sleuth supports sending traces to multiple tracing systems as of version 2.1.0. In order to get this to work, every tracing system needs to have a `Reporter` and `Sender`.
-If you want to override the provided beans you need to give them a specific name.
-To do this you can use respectively `ZipkinAutoConfiguration.REPORTER_BEAN_NAME` and `ZipkinAutoConfiguration.SENDER_BEAN_NAME`.
-
-[source,java,indent=0]
-----
-include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/autoconfig/zipkin2/ZipkinAutoConfigurationTests.java[tags=override_default_beans,indent=0]
-----
-
-[[features-log-integration]]
-== Log integration
-
-Sleuth configures the logging context with variables including the service name (`%{spring.zipkin.service.name}` or `%{spring.application.name}` if the previous one was not set), span ID (`%{spanId}`) and the trace ID (`%{traceId}`).
-These help you connect logs with distributed traces and allow you choice in what tools you use to troubleshoot your services.
-
-Once you find any log with an error, you can look for the trace ID in the message.
-Paste that into your distributed tracing system to visualize the entire trace, regardless of how many services the first request ended up hitting.
-
-[source]
-----
-backend.log: 2020-04-09 17:45:40.516 ERROR [backend,5e8eeec48b08e26882aba313eb08f0a4,dcc1df555b5777b3] 97203 --- [nio-9000-exec-1] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown
-frontend.log:2020-04-09 17:45:40.574 ERROR [frontend,5e8eeec48b08e26882aba313eb08f0a4,82aba313eb08f0a4] 97192 --- [nio-8081-exec-2] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown
-----
-
-Above, you'll notice the trace ID is `5e8eeec48b08e26882aba313eb08f0a4`, for example.
-This log configuration was automatically setup by Sleuth.
-You can disable it by disabling Sleuth via `spring.sleuth.enabled=false` property or putting your own `logging.pattern.level` property.
-
-If you use a log aggregating tool (such as https://www.elastic.co/products/kibana[Kibana], https://www.splunk.com/[Splunk], and others), you can order the events that took place.
-An example from Kibana would resemble the following image:
-
-image::{github-raw}/docs/src/main/asciidoc/images/kibana.png[Log correlation with Kibana]
-
-If you want to use https://www.elastic.co/guide/en/logstash/current/index.html[Logstash], the following listing shows the Grok pattern for Logstash:
-
-[source]
-----
-filter {
- # pattern matching logback pattern
- grok {
- match => { "message" => "%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
- }
- date {
- match => ["timestamp", "ISO8601"]
- }
- mutate {
- remove_field => ["timestamp"]
- }
-}
-----
-
-NOTE: If you want to use Grok together with the logs from Cloud Foundry, you have to use the following pattern:
-
-[source]
-----
-filter {
- # pattern matching logback pattern
- grok {
- match => { "message" => "(?m)OUT\s+%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
- }
- date {
- match => ["timestamp", "ISO8601"]
- }
- mutate {
- remove_field => ["timestamp"]
- }
-}
-----
-
-[[features-log-integration-json-logback]]
-=== JSON Logback with Logstash
-
-Often, you do not want to store your logs in a text file but in a JSON file that Logstash can immediately pick.
-To do so, you have to do the following (for readability, we pass the dependencies in the `groupId:artifactId:version` notation).
-
-*Dependencies Setup*
-
-. Ensure that Logback is on the classpath (`ch.qos.logback:logback-core`).
-. Add Logstash Logback encode.
-For example, to use version `4.6`, add `net.logstash.logback:logstash-logback-encoder:4.6`.
-
-*Logback Setup*
-
-Consider the following example of a Logback configuration file (logback-spring.xml).
-
-[source,xml]
------
-include::{project-root}/docs/src/main/asciidoc/logback-spring.xml[]
------
-
-That Logback configuration file:
-
-* Logs information from the application in a JSON format to a `build/${spring.application.name}.json` file.
-* Has commented out two additional appenders: console and standard log file.
-* Has the same logging pattern as the one presented in the previous section.
-
-NOTE: If you use a custom `logback-spring.xml`, you must pass the `spring.application.name` in the `bootstrap` rather than the `application` property file.
-Otherwise, your custom logback file does not properly read the property.
-
-[[features-whats-next]]
-== What to Read Next
-
-If you want to learn more about any of the classes discussed in this section, you can browse the
-{github-code}[source code directly].
-If you have specific questions, see the
-<> section.
-
-If you are comfortable with {project-full-name}'s core features, you can continue on and read about
-<>.
+[[features]]
+[[project-features]]
+= Spring Cloud Sleuth Features
+
+include::_attributes.adoc[]
+
+This section dives into the details of {project-full-name}.
+Here you can learn about the key features that you may want to use and customize.
+If you have not already done so, you might want to read the "<>" and "<>" sections, so that you have a good grounding in the basics.
+
+[[features-context-propagation]]
+== Context Propagation
+
+Traces connect from service to service using header propagation.
+The default format is https://github.com/openzipkin/b3-propagation[B3].
+Similar to data formats, you can configure alternate header formats also, provided trace and span IDs are compatible with B3. Most notably, this means the trace ID and span IDs are lower-case hex, not UUIDs.
+Besides trace identifiers, other properties (Baggage) can also be passed along with the request.
+Remote Baggage must be predefined, but is flexible otherwise.
+
+To use the provided defaults you can set the `spring.sleuth.propagation.type` property.
+The value can be a list in which case you will propagate more tracing headers.
+
+For Brave we support `AWS`, `B3`, `W3C` propagation types.
+
+You can read more about how to provide custom context propagation in this "<>".
+
+[[features-sampling]]
+== Sampling
+
+Spring Cloud Sleuth pushes the sampling decision down to the tracer implementation.
+However, there are cases where you can change the sampling decision at runtime.
+
+One of such cases is skip reporting of certain client spans.
+To achieve that you can set the `spring.sleuth.web.client.skip-pattern` with the path patterns to be skipped.
+Another option is to provide your own custom `org.springframework.cloud.sleuth.SamplerFunction<`org.springframework.cloud.sleuth.http.HttpRequest>` implementation and define when a given `HttpRequest` should not be sampled.
+
+[[features-baggage]]
+== Baggage
+
+Distributed tracing works by propagating fields inside and across services that connect the trace together: traceId and spanId notably.
+The context that holds these fields can optionally push other fields that need to be consistent regardless of many services are touched.
+The simple name for these extra fields is "Baggage".
+
+Sleuth allows you to define which baggage are permitted to exist in the trace context, including what header names are used.
+
+The following example shows setting baggage values using Spring Cloud Sleuth's API:
+
+[source,java,indent=0]
+----
+include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/baggage/multiple/MultipleHopsIntegrationTests.java[tags=baggage,indent=0]
+----
+
+IMPORTANT: There is currently no limitation of the count or size of baggage items.
+Keep in mind that too many can decrease system throughput or increase RPC latency.
+In extreme cases, too much baggage can crash the application, due to exceeding transport-level message or header capacity.
+
+You can use properties to define fields that have no special configuration such as name mapping:
+
+* `spring.sleuth.baggage.remote-fields` is a list of header names to accept and propagate to remote services.
+* `spring.sleuth.baggage.local-fields` is a list of names to propagate locally
+
+No prefixing applies with these keys.
+What you set is literally what is used.
+
+A name set in either of these properties will result in a `Baggage` of the same name.
+
+In order to automatically set the baggage values to Slf4j's MDC, you have to set the `spring.sleuth.baggage.correlation-fields` property with a list of allowed local or remote keys. E.g. `spring.sleuth.baggage.correlation-fields=country-code` will set the value of the `country-code` baggage into MDC.
+
+Note that the extra field is propagated and added to MDC starting with the next downstream trace context.
+To immediately add the extra field to MDC in the current trace context, configure the field to flush on update:
+
+```
+// configuration
+@Bean
+BaggageField countryCodeField() {
+ return BaggageField.create("country-code");
+}
+
+@Bean
+ScopeDecorator mdcScopeDecorator() {
+ return MDCScopeDecorator.newBuilder()
+ .clear()
+ .add(SingleCorrelationField.newBuilder(countryCodeField())
+ .flushOnUpdate()
+ .build())
+ .build();
+}
+
+// service
+@Autowired
+BaggageField countryCodeField;
+
+countryCodeField.updateValue("new-value");
+```
+
+IMPORTANT: Remember that adding entries to MDC can drastically decrease the performance of your application!
+
+If you want to add the baggage entries as tags, to make it possible to search for spans via the baggage entries, you can set the value of
+`spring.sleuth.baggage.tag-fields` with a list of allowed baggage keys.
+To disable the feature you have to pass the `spring.sleuth.propagation.tag.enabled=false` property.
+
+[[features-baggage-vs-tags]]
+=== Baggage versus Tags
+
+Like trace IDs, Baggage is attached to messages or requests, usually as headers.
+Tags are key value pairs sent in a Span to Zipkin.
+Baggage values are not added spans by default, which means you can't search based on Baggage unless you opt-in.
+
+To make baggage also tags, use the property `spring.sleuth.baggage.tag-fields`
+like so:
+
+[source,yml]
+----
+include::{brave_path}/src/test/resources/application-baggage.yml[indent=0]
+----
+
+[[features-brave]]
+== OpenZipkin Brave Tracer Integration
+
+Spring Cloud Sleuth integrates with the OpenZipkin Brave tracer via the bridge that is available in the `spring-cloud-sleuth-brave` module.
+In this section you can read about specific Brave integrations.
+
+You can choose to use either Sleuth's API or the Brave API directly in your code (e.g. either Sleuth's `Tracer` or Brave's `Tracer`).
+If you want to use this tracer implementation's API directly please read https://github.com/openzipkin/brave[their documentation to learn more about it].
+
+[[features-brave-basics]]
+=== Brave Basics
+
+Here are the most core types you might use:
+
+* `brave.SpanCustomizer` - to change the span currently in progress
+* `brave.Tracer` - to get a start new spans ad-hoc
+
+Here are the most relevant links from the OpenZipkin Brave project:
+
+* https://github.com/openzipkin/brave/tree/master/brave[Brave's core library]
+* https://github.com/openzipkin/brave/tree/master/brave#baggage[Baggage (propagated fields)]
+* https://github.com/openzipkin/brave/tree/master/instrumentation/http[HTTP tracing]
+
+[[features-brave-sampling]]
+=== Brave Sampling
+
+Sampling only applies to tracing backends, such as Zipkin.
+Trace IDs appear in logs regardless of sample rate.
+Sampling is a way to prevent overloading the system, by consistently tracing some, but not all requests.
+
+The default rate of 10 traces per second is controlled by the `spring.sleuth.sampler.rate`
+property and applies when we know Sleuth is used for reasons besides logging.
+Use a rate above 100 traces per second with extreme caution as it can overload your tracing system.
+
+The sampler can be set by Java Config also, as shown in the following example:
+
+[source,java,indent=0]
+----
+include::{brave_path}/src/test/java/org/springframework/cloud/sleuth/brave/SpringCloudSleuthDocTests.java[tags=always_sampler,indent=0]
+----
+
+TIP: You can set the HTTP header `b3` to `1`, or, when doing messaging, you can set the `spanFlags` header to `1`.
+Doing so forces the current request to be sampled regardless of configuration.
+
+By default samplers will work with the refresh scope mechanism.
+That means that you can change the sampling properties at runtime, refresh the application and the changes will be reflected.
+However, sometimes the fact of creating a proxy around samplers and calling it from too early (from `@PostConstruct` annotated method) may lead to dead locks.
+In such a case either create a sampler bean explicitly, or set the property `spring.sleuth.sampler.refresh.enabled` to `false` to disable the refresh scope support.
+
+[[features-brave-baggage]]
+=== Brave Baggage Java configuration
+
+If you need to do anything more advanced than above, do not define properties and instead use a
+`@Bean` config for the baggage fields you use.
+
+* `BaggagePropagationCustomizer` sets up baggage fields
+* Add a `SingleBaggageField` to control header names for a `Baggage`.
+* `CorrelationScopeCustomizer` sets up MDC fields
+* Add a `SingleCorrelationField` to change the MDC name of a `Baggage` or if updates flush.
+
+[[features-brave-customizations]]
+=== Brave Customizations
+
+The `brave.Tracer` object is fully managed by sleuth, so you rarely need to affect it.
+That said, Sleuth supports a number of `Customizer` types, that allow you to configure anything not already done by Sleuth with auto-configuration or properties.
+
+If you define one of the following as a `Bean`, Sleuth will invoke it to customize behaviour:
+
+* `RpcTracingCustomizer` - for RPC tagging and sampling policy
+* `HttpTracingCustomizer` - for HTTP tagging and sampling policy
+* `MessagingTracingCustomizer` - for messaging tagging and sampling policy
+* `CurrentTraceContextCustomizer` - to integrate decorators such as correlation.
+* `BaggagePropagationCustomizer` - for propagating baggage fields in process and over headers
+* `CorrelationScopeDecoratorCustomizer` - for scope decorations such as MDC (logging) field correlation
+
+[[features-brave-sampling-customizations]]
+==== Brave Sampling Customizations
+
+If client /server sampling is required, just register a bean of type
+`brave.sampler.SamplerFunction` and name the bean
+`sleuthHttpClientSampler` for client sampler and `sleuthHttpServerSampler`
+for server sampler.
+
+For your convenience the `@HttpClientSampler` and `@HttpServerSampler`
+annotations can be used to inject the proper beans or to reference the bean names via their static String `NAME` fields.
+
+Check out Brave's code to see an example of how to make a path-based sampler
+https://github.com/openzipkin/brave/tree/master/instrumentation/http#sampling-policy
+
+If you want to completely rewrite the `HttpTracing` bean you can use the `SkipPatternProvider`
+interface to retrieve the URL `Pattern` for spans that should be not sampled.
+Below you can see an example of usage of `SkipPatternProvider` inside a server side, `Sampler`.
+
+[source,java,indent=0]
+----
+@Configuration(proxyBeanMethods = false)
+ class Config {
+include::{tests_path}/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceFilterWebIntegrationTests.java[tags=custom_server_sampler,indent=2]
+}
+----
+
+[[features-brave-messaging]]
+=== Brave Messaging
+
+Sleuth automatically configures the `MessagingTracing` bean which serves as a foundation for Messaging instrumentation such as Kafka or JMS.
+
+If a customization of producer / consumer sampling of messaging traces is required, just register a bean of type `brave.sampler.SamplerFunction` and name the bean `sleuthProducerSampler` for producer sampler and `sleuthConsumerSampler`
+for consumer sampler.
+
+For your convenience the `@ProducerSampler` and `@ConsumerSampler`
+annotations can be used to inject the proper beans or to reference the bean names via their static String `NAME` fields.
+
+Ex.
+Here's a sampler that traces 100 consumer requests per second, except for the "alerts" channel.
+Other requests will use a global rate provided by the
+`Tracing` component.
+
+[source,java,indent=0]
+----
+@Configuration(proxyBeanMethods = false)
+ class Config {
+include::{autoconfig_path}/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/messaging/BraveMessagingAutoConfigurationIntegrationTests.java[tags=custom_messaging_consumer_sampler,indent=2]
+}
+----
+
+For more, see https://github.com/openzipkin/brave/tree/master/instrumentation/messaging#sampling-policy
+
+[[features-brave-opentracing]]
+=== Brave Opentracing
+
+You can integrate with Brave and https://opentracing.io/[OpenTracing] via the
+`io.opentracing.brave:brave-opentracing` bridge.
+Just add it to the classpath and the OpenTracing `Tracer` will be set up automatically.
+
+[[features-zipkin]]
+== Sending Spans to Zipkin
+
+Spring Cloud Sleuth provides various integrations with the https://zipkin.io[OpenZipkin] distributed tracing system.
+Regardless of the chosen tracer implementation it's enough to add `spring-cloud-sleuth-zipkin` to the classpath to start sending spans to Zipkin.
+You can choose whether to do that via HTTP or messaging.
+You can read more about how to do that in "<>".
+
+When the span is closed, it is sent to Zipkin over HTTP. The communication is asynchronous.
+You can configure the URL by setting the `spring.zipkin.baseUrl` property, as follows:
+
+[source,yaml]
+----
+spring.zipkin.baseUrl: https://192.168.99.100:9411/
+----
+
+If you want to find Zipkin through service discovery, you can pass the Zipkin's service ID inside the URL, as shown in the following example for `zipkinserver` service ID:
+
+[source,yaml]
+----
+spring.zipkin.baseUrl: https://zipkinserver/
+----
+
+To disable this feature just set `spring.zipkin.discovery-client-enabled` to `false`.
+
+When the Discovery Client feature is enabled, Sleuth uses
+`LoadBalancerClient` to find the URL of the Zipkin Server.
+It means that you can set up the load balancing configuration.
+
+If you have `web`, `rabbit`, `activemq` or `kafka` together on the classpath, you might need to pick the means by which you would like to send spans to zipkin.
+To do so, set `web`, `rabbit`, `activemq` or `kafka` to the `spring.zipkin.sender.type` property.
+The following example shows setting the sender type for `web`:
+
+[source,yaml]
+----
+spring.zipkin.sender.type: web
+----
+
+To customize the `RestTemplate` that sends spans to Zipkin via HTTP, you can register the `ZipkinRestTemplateCustomizer` bean.
+
+[source,java,indent=0]
+----
+@Configuration(proxyBeanMethods = false)
+ class MyConfig {
+ @Bean ZipkinRestTemplateCustomizer myCustomizer() {
+ return new ZipkinRestTemplateCustomizer() {
+ @Override
+ void customize(RestTemplate restTemplate) {
+ // customize the RestTemplate
+ }
+ };
+ }
+}
+----
+
+If, however, you would like to control the full process of creating the `RestTemplate`
+object, you will have to create a bean of `ZipkinRestTemplateProvider` type.
+
+[source,java,indent=0]
+----
+ @Bean
+ ZipkinRestTemplateProvider myZipkinRestTemplateProvider() {
+ return MyRestTemplate::new;
+ }
+----
+
+By default, api path will be set to `api/v2/spans` or `api/v1/spans` depending on the encoder version. If you want to use a custom api path, you can configure it using the following property (empty case, set ""):
+
+[source,yaml]
+----
+spring.zipkin.api-path: v2/path2
+----
+
+[[features-zipkin-custom-service-name]]
+=== Custom service name
+
+By default, Sleuth assumes that, when you send a span to Zipkin, you want the span's service name to be equal to the value of the `spring.application.name` property.
+That is not always the case, though.
+There are situations in which you want to explicitly provide a different service name for all spans coming from your application.
+To achieve that, you can pass the following property to your application to override that value (the example is for a service named `myService`):
+
+[source,yaml]
+----
+spring.zipkin.service.name: myService
+----
+
+[[features-zipkin-host-locator]]
+=== Host Locator
+
+IMPORTANT: This section is about defining *host* from service discovery.
+It is *NOT* about finding Zipkin through service discovery.
+
+To define the host that corresponds to a particular span, we need to resolve the host name and port.
+The default approach is to take these values from server properties.
+If those are not set, we try to retrieve the host name from the network interfaces.
+
+If you have the discovery client enabled and prefer to retrieve the host address from the registered instance in a service registry, you have to set the `spring.zipkin.locator.discovery.enabled` property (it is applicable for both HTTP-based and Stream-based span reporting), as follows:
+
+[source,yaml]
+----
+spring.zipkin.locator.discovery.enabled: true
+----
+
+[[features-zipkin-custom-reported-spans]]
+=== Customization of Reported Spans
+
+In Sleuth, we generate spans with a fixed name.
+Some users want to modify the name depending on values of tags.
+
+Sleuth registers a `SpanFilter` bean that can automatically skip reporting spans of given name patterns.
+The property `spring.sleuth.span-filter.span-name-patterns-to-skip` contains the default skip patterns for span names.
+The property `spring.sleuth.span-filter.additional-span-name-patterns-to-skip` will append the provided span name patterns to the existing ones.
+In order to disable this functionality just set `spring.sleuth.span-filter.enabled` to `false`.
+
+[[features-zipkin-custom-reported-spans-brave]]
+==== Brave Customization of Reported Spans
+
+IMPORTANT: This section is applicable for Brave tracer only.
+
+Before reporting spans (for example, to Zipkin) you may want to modify that span in some way.
+You can do so by implementing a `SpanHandler`.
+
+The following example shows how to register two beans that implement `SpanHandler`:
+
+[source,java,indent=0]
+----
+include::{autoconfig_path}/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/SpanHandlerTests.java[tags=spanHandler,indent=0]
+----
+
+The preceding example results in changing the name of the reported span to `foo bar`, just before it gets reported (for example, to Zipkin).
+
+=== Overriding the auto-configuration of Zipkin
+
+Spring Cloud Sleuth supports sending traces to multiple tracing systems as of version 2.1.0. In order to get this to work, every tracing system needs to have a `Reporter` and `Sender`.
+If you want to override the provided beans you need to give them a specific name.
+To do this you can use respectively `ZipkinAutoConfiguration.REPORTER_BEAN_NAME` and `ZipkinAutoConfiguration.SENDER_BEAN_NAME`.
+
+[source,java,indent=0]
+----
+include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/autoconfig/zipkin2/ZipkinAutoConfigurationTests.java[tags=override_default_beans,indent=0]
+----
+
+[[features-log-integration]]
+== Log integration
+
+Sleuth configures the logging context with variables including the service name (`%{spring.zipkin.service.name}` or `%{spring.application.name}` if the previous one was not set), span ID (`%{spanId}`) and the trace ID (`%{traceId}`).
+These help you connect logs with distributed traces and allow you choice in what tools you use to troubleshoot your services.
+
+Once you find any log with an error, you can look for the trace ID in the message.
+Paste that into your distributed tracing system to visualize the entire trace, regardless of how many services the first request ended up hitting.
+
+[source]
+----
+backend.log: 2020-04-09 17:45:40.516 ERROR [backend,5e8eeec48b08e26882aba313eb08f0a4,dcc1df555b5777b3] 97203 --- [nio-9000-exec-1] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown
+frontend.log:2020-04-09 17:45:40.574 ERROR [frontend,5e8eeec48b08e26882aba313eb08f0a4,82aba313eb08f0a4] 97192 --- [nio-8081-exec-2] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown
+----
+
+Above, you'll notice the trace ID is `5e8eeec48b08e26882aba313eb08f0a4`, for example.
+This log configuration was automatically setup by Sleuth.
+You can disable it by disabling Sleuth via `spring.sleuth.enabled=false` property or putting your own `logging.pattern.level` property.
+
+If you use a log aggregating tool (such as https://www.elastic.co/products/kibana[Kibana], https://www.splunk.com/[Splunk], and others), you can order the events that took place.
+An example from Kibana would resemble the following image:
+
+image::{github-raw}/docs/src/main/asciidoc/images/kibana.png[Log correlation with Kibana]
+
+If you want to use https://www.elastic.co/guide/en/logstash/current/index.html[Logstash], the following listing shows the Grok pattern for Logstash:
+
+[source]
+----
+filter {
+ # pattern matching logback pattern
+ grok {
+ match => { "message" => "%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
+ }
+ date {
+ match => ["timestamp", "ISO8601"]
+ }
+ mutate {
+ remove_field => ["timestamp"]
+ }
+}
+----
+
+NOTE: If you want to use Grok together with the logs from Cloud Foundry, you have to use the following pattern:
+
+[source]
+----
+filter {
+ # pattern matching logback pattern
+ grok {
+ match => { "message" => "(?m)OUT\s+%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
+ }
+ date {
+ match => ["timestamp", "ISO8601"]
+ }
+ mutate {
+ remove_field => ["timestamp"]
+ }
+}
+----
+
+[[features-log-integration-json-logback]]
+=== JSON Logback with Logstash
+
+Often, you do not want to store your logs in a text file but in a JSON file that Logstash can immediately pick.
+To do so, you have to do the following (for readability, we pass the dependencies in the `groupId:artifactId:version` notation).
+
+*Dependencies Setup*
+
+. Ensure that Logback is on the classpath (`ch.qos.logback:logback-core`).
+. Add Logstash Logback encode.
+For example, to use version `4.6`, add `net.logstash.logback:logstash-logback-encoder:4.6`.
+
+*Logback Setup*
+
+Consider the following example of a Logback configuration file (logback-spring.xml).
+
+[source,xml]
+-----
+include::{project-root}/docs/src/main/asciidoc/logback-spring.xml[]
+-----
+
+That Logback configuration file:
+
+* Logs information from the application in a JSON format to a `build/${spring.application.name}.json` file.
+* Has commented out two additional appenders: console and standard log file.
+* Has the same logging pattern as the one presented in the previous section.
+
+NOTE: If you use a custom `logback-spring.xml`, you must pass the `spring.application.name` in the `bootstrap` rather than the `application` property file.
+Otherwise, your custom logback file does not properly read the property.
+
+[[features-self-documenting-spans]]
+== Self Documenting Spans
+
+A declarative format of representing span configuration was introduced via the `DocumentedSpan` abstraction. By analyzing Sleuth's source code an appendix with all span characteristics is created (including allowed tag keys and event names). You can check the <> for more information.
+
+[[features-traces-actuator]]
+== Traces Actuator Endpoint
+
+Spring Cloud Sleuth comes with a `traces` Actuator endpoint that can store finished spans. The endpoint can be queried either via an HTTP Get method to simply retrieve the list of stored spans or via HTTP Post method to retrieve the list and clear it.
+
+The size of the queue where the spans are stored can be configured via the `management.endpoint.traces.queue-size` property.
+
+Please read the https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#actuator[Spring Boot Actuator: Production-ready Features] section of the documentation to read more about the Actuator endpoints configuration options.
+
+[[features-whats-next]]
+== What to Read Next
+
+If you want to learn more about any of the classes discussed in this section, you can browse the
+{github-code}[source code directly].
+If you have specific questions, see the
+<> section.
+
+If you are comfortable with {project-full-name}'s core features, you can continue on and read about
+<>.
diff --git a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc
index d0a241950..d34261b40 100644
--- a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc
+++ b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc
@@ -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 information.
-<> :: About the Documentation, Getting Help, First Steps, and more.
-<> :: Introducing {project-full-name}, Developing Your First {project-full-name}-based Application
-<> :: {project-full-name} usage examples and workflows.
-<> :: Span creation, context propagation, and more.
-<> :: Add sampling, propagate remote tags, and more.
-<> :: Instrumentation configuration, context propagation, and more.
-<> :: 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 information.
+<> :: About the Documentation, Getting Help, First Steps, and more.
+<> :: Introducing {project-full-name}, Developing Your First {project-full-name}-based Application
+<> :: {project-full-name} usage examples and workflows.
+<> :: Span creation, context propagation, and more.
+<> :: Add sampling, propagate remote tags, and more.
+<> :: Instrumentation configuration, context propagation, and more.
+<> :: Span definitions and configuration properties.
diff --git a/docs/src/main/java/org/springframework/cloud/sleuth/documentation/DocsFromSources.java b/docs/src/main/java/org/springframework/cloud/sleuth/documentation/DocsFromSources.java
new file mode 100644
index 000000000..868f2a13d
--- /dev/null
+++ b/docs/src/main/java/org/springframework/cloud/sleuth/documentation/DocsFromSources.java
@@ -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 spanEntries = new TreeSet<>();
+ FileVisitor 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);
+ }
+ }
+
+}
diff --git a/docs/src/main/java/org/springframework/cloud/sleuth/documentation/KeyValueEntry.java b/docs/src/main/java/org/springframework/cloud/sleuth/documentation/KeyValueEntry.java
new file mode 100644
index 000000000..7ac690bd6
--- /dev/null
+++ b/docs/src/main/java/org/springframework/cloud/sleuth/documentation/KeyValueEntry.java
@@ -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 {
+
+ 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;
+ }
+
+}
diff --git a/docs/src/main/java/org/springframework/cloud/sleuth/documentation/SpanEntry.java b/docs/src/main/java/org/springframework/cloud/sleuth/documentation/SpanEntry.java
new file mode 100644
index 000000000..b11eb31eb
--- /dev/null
+++ b/docs/src/main/java/org/springframework/cloud/sleuth/documentation/SpanEntry.java
@@ -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 {
+
+ final String name;
+
+ final String enclosingClass;
+
+ final String enumName;
+
+ final String description;
+
+ final String prefix;
+
+ final Collection tagKeys;
+
+ final Collection events;
+
+ SpanEntry(String name, String enclosingClass, String enumName, String description, String prefix,
+ Collection tagKeys, Collection 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
+
+}
diff --git a/docs/src/main/java/org/springframework/cloud/sleuth/documentation/SpanSearchingFileVisitor.java b/docs/src/main/java/org/springframework/cloud/sleuth/documentation/SpanSearchingFileVisitor.java
new file mode 100644
index 000000000..66f17122a
--- /dev/null
+++ b/docs/src/main/java/org/springframework/cloud/sleuth/documentation/SpanSearchingFileVisitor.java
@@ -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 {
+
+ private final Pattern pattern;
+
+ private final Collection spanEntries;
+
+ SpanSearchingFileVisitor(Pattern pattern, Collection 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> members = enumConstant.getBody().getMembers();
+ if (members.isEmpty()) {
+ return null;
+ }
+ String name = "";
+ String description = enumConstant.getJavaDoc().getText();
+ String prefix = "";
+ Collection tags = new TreeSet<>();
+ Collection events = new TreeSet<>();
+ for (MemberSource 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 keyValueEntries(JavaEnumImpl myEnum, MethodDeclaration methodDeclaration,
+ Class requiredClass) {
+ Collection enumNames = readClassValue(methodDeclaration);
+ Collection keyValues = new TreeSet<>();
+ enumNames.forEach(enumName -> {
+ List> 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 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> 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 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 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());
+ }
+
+}
diff --git a/docs/src/test/java/org/springframework/cloud/sleuth/documentation/DocsFromSourcesTests.java b/docs/src/test/java/org/springframework/cloud/sleuth/documentation/DocsFromSourcesTests.java
new file mode 100644
index 000000000..258736614
--- /dev/null
+++ b/docs/src/test/java/org/springframework/cloud/sleuth/documentation/DocsFromSourcesTests.java
@@ -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");
+ }
+
+}
diff --git a/docs/src/test/java/org/springframework/cloud/sleuth/documentation/SleuthAnnotationSpan.java b/docs/src/test/java/org/springframework/cloud/sleuth/documentation/SleuthAnnotationSpan.java
new file mode 100644
index 000000000..710768b16
--- /dev/null
+++ b/docs/src/test/java/org/springframework/cloud/sleuth/documentation/SleuthAnnotationSpan.java
@@ -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";
+ }
+ }
+
+ }
+
+}
diff --git a/docs/src/test/java/org/springframework/cloud/sleuth/documentation/SleuthAsyncSpan.java b/docs/src/test/java/org/springframework/cloud/sleuth/documentation/SleuthAsyncSpan.java
new file mode 100644
index 000000000..b879b7fed
--- /dev/null
+++ b/docs/src/test/java/org/springframework/cloud/sleuth/documentation/SleuthAsyncSpan.java
@@ -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";
+ }
+ }
+
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 5393834aa..14846526b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,428 +1,494 @@
-
-
-
-
- 4.0.0
-
- spring-cloud-sleuth
- 3.0.4-SNAPSHOT
- pom
- Spring Cloud Sleuth
- Spring Cloud Sleuth
-
-
- org.springframework.cloud
- spring-cloud-build
- 3.0.4-SNAPSHOT
-
-
-
-
-
- https://github.com/spring-cloud/spring-cloud-sleuth
- scm:git:git://github.com/spring-cloud/spring-cloud-sleuth.git
-
-
- scm:git:ssh://git@github.com/spring-cloud/spring-cloud-sleuth.git
-
- HEAD
-
-
-
- spring-cloud-sleuth-dependencies
- spring-cloud-sleuth-api
- spring-cloud-sleuth-instrumentation
- spring-cloud-sleuth-brave
- spring-cloud-sleuth-autoconfigure
- tests
- spring-cloud-sleuth-zipkin
- spring-cloud-starter-sleuth
- spring-cloud-sleuth-samples
- docs
-
-
-
- 1.8
- 1.8
- 1.8
- 1.8
- 3.0.4-SNAPSHOT
- 3.0.4-SNAPSHOT
- 3.0.4-SNAPSHOT
- 2.0.3-SNAPSHOT
- 3.1.4-SNAPSHOT
- 3.1.4-SNAPSHOT
- 3.0.4-SNAPSHOT
- 3.0.4-SNAPSHOT
- 5.13.2
- 0.32.0
- 2.3.4.RELEASE
- false
- 4.9.0
- 4.8.0
- 20.0
- 1.7.1
- 3.3.0
- 3.0.1
- 2.2.0.RELEASE
-
-
- true
- false
- 3.8.1
- 2.2
- 4.0.3
- 0.21.3
- 0.14.1
-
-
-
-
-
-
- maven-compiler-plugin
- 3.8.1
-
-
- default-compile
-
- true
- true
-
- ${maven.compiler.source}
- ${maven.compiler.target}
-
-
-
-
- default-testCompile
-
- true
- true
-
- ${maven.compiler.testSource}
- ${maven.compiler.testTarget}
-
-
-
-
-
-
- maven-enforcer-plugin
- 1.3.1
-
-
- enforce-java
-
- enforce
-
-
-
-
- ${maven.compiler.testTarget}
-
-
-
-
-
-
-
- maven-deploy-plugin
- 2.8.2
-
-
-
-
-
- io.spring.javaformat
- spring-javaformat-maven-plugin
-
-
- maven-checkstyle-plugin
-
-
-
-
-
-
-
- maven-checkstyle-plugin
-
-
- maven-surefire-report-plugin
-
-
-
-
-
-
-
- org.springframework.cloud
- spring-cloud-sleuth-dependencies
- ${project.version}
- pom
- import
-
-
- org.springframework.cloud
- spring-cloud-netflix-dependencies
- ${spring-cloud-netflix.version}
- pom
- import
-
-
- org.springframework.cloud
- spring-cloud-commons-dependencies
- ${spring-cloud-commons.version}
- pom
- import
-
-
- org.springframework.cloud
- spring-cloud-gateway-dependencies
- ${spring-cloud-gateway.version}
- pom
- import
-
-
- org.springframework.cloud
- spring-cloud-circuitbreaker-dependencies
- ${spring-cloud-circuitbreaker.version}
- pom
- import
-
-
- org.springframework.cloud
- spring-cloud-stream-dependencies
- ${spring-cloud-stream.version}
- pom
- import
-
-
- org.springframework.cloud
- spring-cloud-function-dependencies
- ${spring-cloud-function.version}
- pom
- import
-
-
- org.springframework.cloud
- spring-cloud-openfeign-dependencies
- ${spring-cloud-openfeign.version}
- pom
- import
-
-
- org.springframework.security.oauth.boot
- spring-security-oauth2-autoconfigure
- ${spring-security-boot-autoconfigure.version}
- true
-
-
- cglib
- cglib-nodep
- ${cglib-nodep.version}
-
-
- org.objenesis
- objenesis
- ${objenesis.version}
-
-
-
- com.squareup.okhttp3
- mockwebserver
- ${mockwebserver.version}
-
-
- org.springframework.security.oauth
- spring-security-oauth2
- ${spring-security-oauth2.version}
-
-
- io.zipkin.aws
- brave-propagation-aws
- ${brave-propagation-aws.version}
-
-
- org.hamcrest
- hamcrest-core
- ${hamcrest-core.version}
- test
-
-
- org.awaitility
- awaitility
- ${awaitility.version}
- test
-
-
- com.tngtech.archunit
- archunit-junit5
- ${archunit-junit5.version}
-
-
-
-
-
-
- spring
-
-
- spring-snapshots
- Spring Snapshots
- https://repo.spring.io/snapshot
-
- true
-
-
- false
-
-
-
-
- jfrog-snapshots
- JFrog Snapshots
- https://oss.jfrog.org/oss-snapshot-local/
-
- true
-
-
- false
-
-
-
- spring-milestones
- Spring Milestones
- https://repo.spring.io/milestone
-
- false
-
-
-
- spring-releases
- Spring Releases
- https://repo.spring.io/release
-
- false
-
-
-
-
-
- spring-snapshots
- Spring Snapshots
- https://repo.spring.io/snapshot
-
- true
-
-
- false
-
-
-
- spring-milestones
- Spring Milestones
- https://repo.spring.io/milestone
-
- false
-
-
-
- spring-releases
- Spring Releases
- https://repo.spring.io/release
-
- false
-
-
-
-
-
- ide
-
- false
-
-
-
-
- maven-compiler-plugin
-
- ${maven.compiler.testSource}
- ${maven.compiler.testTarget}
-
-
-
-
-
-
- benchmarks
-
- false
-
-
- benchmarks
-
-
-
- sonar
-
-
-
- org.jacoco
- jacoco-maven-plugin
-
-
- pre-unit-test
-
- prepare-agent
-
-
- surefireArgLine
- ${project.build.directory}/jacoco.exec
-
-
-
-
- post-unit-test
- test
-
- report
-
-
-
- ${project.build.directory}/jacoco.exec
-
-
-
-
-
-
- maven-surefire-plugin
-
-
- ${surefireArgLine}
-
-
-
-
-
-
-
-
+
+
+
+
+ 4.0.0
+
+ spring-cloud-sleuth
+ 3.1.0-SNAPSHOT
+ pom
+ Spring Cloud Sleuth
+ Spring Cloud Sleuth
+
+
+ org.springframework.cloud
+ spring-cloud-build
+ 3.1.0-SNAPSHOT
+
+
+
+
+
+ https://github.com/spring-cloud/spring-cloud-sleuth
+ scm:git:git://github.com/spring-cloud/spring-cloud-sleuth.git
+
+
+ scm:git:ssh://git@github.com/spring-cloud/spring-cloud-sleuth.git
+
+ HEAD
+
+
+
+ spring-cloud-sleuth-dependencies
+ spring-cloud-sleuth-api
+ spring-cloud-sleuth-instrumentation
+ spring-cloud-sleuth-brave
+ spring-cloud-sleuth-autoconfigure
+ tests
+ spring-cloud-sleuth-zipkin
+ spring-cloud-starter-sleuth
+ spring-cloud-sleuth-samples
+ docs
+
+
+
+ 1.8
+ 1.8
+ 1.8
+ 1.8
+ 3.1.0-SNAPSHOT
+ 3.0.4-SNAPSHOT
+ 3.0.4-SNAPSHOT
+ 3.0.4-SNAPSHOT
+ 2.0.3-SNAPSHOT
+ 3.1.4-SNAPSHOT
+ 3.1.4-SNAPSHOT
+ 3.0.4-SNAPSHOT
+ 3.0.4-SNAPSHOT
+ 2.3.2-SNAPSHOT
+ 2.5.1
+ 5.13.2
+ 0.32.0
+ 2.3.4.RELEASE
+ false
+ 4.9.0
+ 4.8.0
+ 20.0
+ 1.7.1
+ 3.3.0
+ 3.0.1
+ 2.2.0.RELEASE
+ 3.9.1
+ 1.7
+ 10.0.6
+ 2.8.0
+ 1.5.10
+
+
+ true
+ false
+ 3.8.1
+ 2.2
+ 4.0.3
+ 0.21.3
+ 0.14.1
+ 1.15.3
+ 3.2.0
+
+
+
+
+
+
+ maven-compiler-plugin
+ 3.8.1
+
+
+ default-compile
+
+ true
+ true
+
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+
+
+
+
+ default-testCompile
+
+ true
+ true
+
+ ${maven.compiler.testSource}
+ ${maven.compiler.testTarget}
+
+
+
+
+
+
+ maven-enforcer-plugin
+ 1.3.1
+
+
+ enforce-java
+
+ enforce
+
+
+
+
+ ${maven.compiler.testTarget}
+
+
+
+
+
+
+
+ maven-deploy-plugin
+ 2.8.2
+
+
+ maven-surefire-plugin
+
+
+ true
+
+
+
+
+
+
+
+ io.spring.javaformat
+ spring-javaformat-maven-plugin
+
+
+ maven-checkstyle-plugin
+
+
+
+
+
+
+
+ maven-checkstyle-plugin
+
+
+ maven-surefire-report-plugin
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-sleuth-dependencies
+ ${project.version}
+ pom
+ import
+
+
+ org.springframework.cloud
+ spring-cloud-netflix-dependencies
+ ${spring-cloud-netflix.version}
+ pom
+ import
+
+
+ org.springframework.cloud
+ spring-cloud-commons-dependencies
+ ${spring-cloud-commons.version}
+ pom
+ import
+
+
+ org.springframework.cloud
+ spring-cloud-gateway-dependencies
+ ${spring-cloud-gateway.version}
+ pom
+ import
+
+
+ org.springframework.cloud
+ spring-cloud-circuitbreaker-dependencies
+ ${spring-cloud-circuitbreaker.version}
+ pom
+ import
+
+
+ org.springframework.cloud
+ spring-cloud-stream-dependencies
+ ${spring-cloud-stream.version}
+ pom
+ import
+
+
+ org.springframework.cloud
+ spring-cloud-function-dependencies
+ ${spring-cloud-function.version}
+ pom
+ import
+
+
+ org.springframework.cloud
+ spring-cloud-openfeign-dependencies
+ ${spring-cloud-openfeign.version}
+ pom
+ import
+
+
+ org.springframework.cloud
+ spring-cloud-config-dependencies
+ ${spring-cloud-config.version}
+ pom
+ import
+
+
+ org.springframework.cloud
+ spring-cloud-task-dependencies
+ ${spring-cloud-task.version}
+ pom
+ import
+
+
+ org.springframework.cloud
+ spring-cloud-deployer-dependencies
+ ${spring-cloud-deployer.version}
+ import
+ pom
+
+
+ org.springframework.security.oauth.boot
+ spring-security-oauth2-autoconfigure
+ ${spring-security-boot-autoconfigure.version}
+ true
+
+
+ cglib
+ cglib-nodep
+ ${cglib-nodep.version}
+
+
+ org.objenesis
+ objenesis
+ ${objenesis.version}
+
+
+
+ com.squareup.okhttp3
+ mockwebserver
+ ${mockwebserver.version}
+
+
+ org.springframework.security.oauth
+ spring-security-oauth2
+ ${spring-security-oauth2.version}
+
+
+ io.zipkin.aws
+ brave-propagation-aws
+ ${brave-propagation-aws.version}
+
+
+ p6spy
+ p6spy
+ ${p6spy.version}
+
+
+ net.ttddyy
+ datasource-proxy
+ ${datasource-proxy.version}
+
+
+ org.apache.commons
+ commons-dbcp2
+ ${commons-dbcp2.version}
+
+
+ org.apache.tomcat
+ tomcat-jdbc
+ ${tomcat-jdbc.version}
+
+
+ org.hamcrest
+ hamcrest-core
+ ${hamcrest-core.version}
+ test
+
+
+ org.awaitility
+ awaitility
+ ${awaitility.version}
+ test
+
+
+ com.tngtech.archunit
+ archunit-junit5
+ ${archunit-junit5.version}
+
+
+ org.testcontainers
+ testcontainers-bom
+ ${testcontainers.version}
+ pom
+ import
+
+
+
+
+
+
+ spring
+
+
+ spring-snapshots
+ Spring Snapshots
+ https://repo.spring.io/snapshot
+
+ true
+
+
+ false
+
+
+
+
+ jfrog-snapshots
+ JFrog Snapshots
+ https://oss.jfrog.org/oss-snapshot-local/
+
+ true
+
+
+ false
+
+
+
+ spring-milestones
+ Spring Milestones
+ https://repo.spring.io/milestone
+
+ false
+
+
+
+ spring-releases
+ Spring Releases
+ https://repo.spring.io/release
+
+ false
+
+
+
+
+
+ spring-snapshots
+ Spring Snapshots
+ https://repo.spring.io/snapshot
+
+ true
+
+
+ false
+
+
+
+ spring-milestones
+ Spring Milestones
+ https://repo.spring.io/milestone
+
+ false
+
+
+
+ spring-releases
+ Spring Releases
+ https://repo.spring.io/release
+
+ false
+
+
+
+
+
+ ide
+
+ false
+
+
+
+
+ maven-compiler-plugin
+
+ ${maven.compiler.testSource}
+ ${maven.compiler.testTarget}
+
+
+
+
+
+
+ benchmarks
+
+ false
+
+
+ benchmarks
+
+
+
+ sonar
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ pre-unit-test
+
+ prepare-agent
+
+
+ surefireArgLine
+ ${project.build.directory}/jacoco.exec
+
+
+
+
+ post-unit-test
+ test
+
+ report
+
+
+
+ ${project.build.directory}/jacoco.exec
+
+
+
+
+
+
+ maven-surefire-plugin
+
+
+ ${surefireArgLine}
+
+
+
+
+
+
+
+
diff --git a/spring-cloud-sleuth-api/pom.xml b/spring-cloud-sleuth-api/pom.xml
index 90c393d69..59766a458 100644
--- a/spring-cloud-sleuth-api/pom.xml
+++ b/spring-cloud-sleuth-api/pom.xml
@@ -28,7 +28,7 @@
org.springframework.cloudspring-cloud-sleuth
- 3.0.4-SNAPSHOT
+ 3.1.0-SNAPSHOT..
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/Span.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/Span.java
index 74373c54d..e0bf641d3 100644
--- a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/Span.java
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/Span.java
@@ -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
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/SpanAndScope.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/SpanAndScope.java
new file mode 100644
index 000000000..d981cba7f
--- /dev/null
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/SpanAndScope.java
@@ -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();
+ }
+
+}
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/Taggable.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/Taggable.java
new file mode 100644
index 000000000..79c92512a
--- /dev/null
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/Taggable.java
@@ -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);
+
+}
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/ThreadLocalSpan.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/ThreadLocalSpan.java
new file mode 100644
index 000000000..aa2c0226b
--- /dev/null
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/ThreadLocalSpan.java
@@ -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 threadLocalSpan = new ThreadLocal<>();
+
+ private final Deque 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);
+ }
+ }
+ }
+
+}
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/TraceContext.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/TraceContext.java
index d9e564bbd..15cb7231e 100644
--- a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/TraceContext.java
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/TraceContext.java
@@ -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();
+
+ }
+
}
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/Tracer.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/Tracer.java
index bf05dea69..b5a06b5b9 100644
--- a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/Tracer.java
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/Tracer.java
@@ -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
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/AssertingSpan.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/AssertingSpan.java
new file mode 100644
index 000000000..0f9f47f23
--- /dev/null
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/AssertingSpan.java
@@ -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 type extending a span
+ * @return unwrapped object
+ */
+ static T unwrap(Span span) {
+ if (span == null) {
+ return null;
+ }
+ else if (span instanceof AssertingSpan) {
+ return (T) ((AssertingSpan) span).getDelegate();
+ }
+ return (T) span;
+ }
+
+}
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/AssertingSpanBuilder.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/AssertingSpanBuilder.java
new file mode 100644
index 000000000..de951bdc4
--- /dev/null
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/AssertingSpanBuilder.java
@@ -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);
+ }
+
+}
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/AssertingSpanCustomizer.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/AssertingSpanCustomizer.java
new file mode 100644
index 000000000..d1267bcaf
--- /dev/null
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/AssertingSpanCustomizer.java
@@ -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 type extending a span
+ * @return unwrapped object
+ */
+ static T unwrap(SpanCustomizer span) {
+ if (span == null) {
+ return null;
+ }
+ else if (span instanceof AssertingSpanCustomizer) {
+ return (T) ((AssertingSpanCustomizer) span).getDelegate();
+ }
+ return (T) span;
+ }
+
+}
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/DocumentedSpan.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/DocumentedSpan.java
new file mode 100644
index 000000000..463b1b377
--- /dev/null
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/DocumentedSpan.java
@@ -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);
+ }
+
+}
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/DocumentedSpanAssertions.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/DocumentedSpanAssertions.java
new file mode 100644
index 000000000..1e87eaa4a
--- /dev/null
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/DocumentedSpanAssertions.java
@@ -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 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");
+ }
+
+}
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/EventValue.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/EventValue.java
new file mode 100644
index 000000000..fd5ea542a
--- /dev/null
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/EventValue.java
@@ -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();
+
+}
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/ImmutableAssertingSpan.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/ImmutableAssertingSpan.java
new file mode 100644
index 000000000..2de29248e
--- /dev/null
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/ImmutableAssertingSpan.java
@@ -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;
+ }
+
+}
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/ImmutableAssertingSpanBuilder.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/ImmutableAssertingSpanBuilder.java
new file mode 100644
index 000000000..61a6eb6bb
--- /dev/null
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/ImmutableAssertingSpanBuilder.java
@@ -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;
+ }
+
+}
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/ImmutableAssertingSpanCustomizer.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/ImmutableAssertingSpanCustomizer.java
new file mode 100644
index 000000000..257cf0407
--- /dev/null
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/ImmutableAssertingSpanCustomizer.java
@@ -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;
+ }
+
+}
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/TagKey.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/TagKey.java
new file mode 100644
index 000000000..b3916f384
--- /dev/null
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/docs/TagKey.java
@@ -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);
+ }
+
+}
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/exporter/SpanReporter.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/exporter/SpanReporter.java
new file mode 100644
index 000000000..0c0b20cc5
--- /dev/null
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/exporter/SpanReporter.java
@@ -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);
+
+}
diff --git a/spring-cloud-sleuth-api/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-sleuth-api/src/main/resources/META-INF/additional-spring-configuration-metadata.json
deleted file mode 100644
index 828f8064d..000000000
--- a/spring-cloud-sleuth-api/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ /dev/null
@@ -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
- }
- ]
-}
diff --git a/spring-cloud-sleuth-api/src/test/java/org/springframework/cloud/sleuth/docs/DocumentedSpanAssertionsTests.java b/spring-cloud-sleuth-api/src/test/java/org/springframework/cloud/sleuth/docs/DocumentedSpanAssertionsTests.java
new file mode 100644
index 000000000..2b8d16dcf
--- /dev/null
+++ b/spring-cloud-sleuth-api/src/test/java/org/springframework/cloud/sleuth/docs/DocumentedSpanAssertionsTests.java
@@ -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";
+ }
+ }
+
+ }
+
+}
diff --git a/spring-cloud-sleuth-autoconfigure/pom.xml b/spring-cloud-sleuth-autoconfigure/pom.xml
index 497c9aa02..aedbd9bf8 100644
--- a/spring-cloud-sleuth-autoconfigure/pom.xml
+++ b/spring-cloud-sleuth-autoconfigure/pom.xml
@@ -1,435 +1,523 @@
-
-
-
-
- 4.0.0
-
- spring-cloud-sleuth-autoconfigure
- jar
- Spring Cloud Sleuth AutoConfigure
- Spring Cloud Sleuth AutoConfigure
-
-
- org.springframework.cloud
- spring-cloud-sleuth
- 3.0.4-SNAPSHOT
- ..
-
-
-
-
-
- org.springframework.cloud
- spring-cloud-sleuth-instrumentation
-
-
- org.springframework.boot
- spring-boot-starter-web
- true
-
-
- io.micrometer
- micrometer-core
- true
-
-
- io.projectreactor
- reactor-core
- true
-
-
- org.reactivestreams
- reactive-streams
- true
-
-
- org.springframework.boot
- spring-boot-configuration-processor
- true
-
-
- org.springframework.boot
- spring-boot-starter-actuator
- true
-
-
- org.springframework.integration
- spring-integration-core
- true
-
-
- org.springframework.cloud
- spring-cloud-function-context
- true
-
-
- org.springframework.boot
- spring-boot-starter-websocket
- true
-
-
- org.springframework.cloud
- spring-cloud-stream
-
- ${spring-cloud-stream.version}
- true
-
-
- org.springframework.cloud
- spring-cloud-commons
-
-
- org.springframework
- spring-context
-
-
- org.springframework.cloud
- spring-cloud-context
- true
-
-
- io.reactivex
- rxjava
- true
-
-
- io.github.openfeign
- feign-okhttp
- true
-
-
- org.springframework.cloud
- spring-cloud-starter-openfeign
- true
-
-
- org.springframework.cloud
- spring-cloud-starter-loadbalancer
- true
-
-
- org.springframework.cloud
- spring-cloud-starter-gateway
- true
-
-
- org.aspectj
- aspectjrt
-
-
-
- org.springframework.boot
- spring-boot-starter-quartz
- true
-
-
- org.springframework.boot
- spring-boot-autoconfigure-processor
- true
-
-
- org.springframework.security.oauth
- spring-security-oauth2
- true
-
-
- org.springframework.security.oauth.boot
- spring-security-oauth2-autoconfigure
- true
-
-
-
-
- org.springframework.cloud
- spring-cloud-sleuth-brave
- true
-
-
- io.zipkin.brave
- brave
-
-
- io.zipkin.reporter2
- *
-
-
- io.zipkin.zipkin2
- *
-
-
- true
-
-
- io.zipkin.brave
- brave-context-slf4j
- true
-
-
- io.zipkin.brave
- brave-instrumentation-messaging
- true
-
-
- io.zipkin.brave
- brave-instrumentation-rpc
- true
-
-
- io.zipkin.brave
- brave-instrumentation-spring-rabbit
- true
-
-
- io.zipkin.brave
- brave-instrumentation-kafka-clients
- true
-
-
- io.zipkin.brave
- brave-instrumentation-kafka-streams
- true
-
-
- io.zipkin.brave
- brave-instrumentation-httpclient
- true
-
-
- io.zipkin.brave
- brave-instrumentation-httpasyncclient
- true
-
-
- io.zipkin.brave
- brave-instrumentation-jms
- true
-
-
- io.zipkin.brave
- brave-instrumentation-mongodb
- true
-
-
- io.zipkin.aws
- brave-propagation-aws
- true
-
-
- javax.jms
- javax.jms-api
- true
-
-
- io.opentracing.brave
- brave-opentracing
- true
-
-
- org.apache.httpcomponents
- httpasyncclient
- true
-
-
- org.springframework
- spring-jms
- true
-
-
-
- io.github.lognet
- grpc-spring-boot-starter
- true
-
-
- org.springframework.boot
- spring-boot-starter
-
-
-
-
- io.zipkin.brave
- brave-instrumentation-grpc
- true
-
-
- io.zipkin.reporter2
- zipkin-reporter-metrics-micrometer
-
-
- io.micrometer
- micrometer-core
-
-
- true
-
-
-
- io.lettuce
- lettuce-core
- true
-
-
- org.springframework.kafka
- spring-kafka
- true
-
-
- org.apache.kafka
- kafka-streams
- true
-
-
- org.springframework.amqp
- spring-rabbit
- true
-
-
- org.springframework.boot
- spring-boot-starter-data-mongodb
- true
-
-
-
-
- org.springframework.cloud
- spring-cloud-sleuth-zipkin
- true
-
-
- io.zipkin.zipkin2
- zipkin
- true
-
-
- io.zipkin.reporter2
- zipkin-reporter
- true
-
-
- io.zipkin.reporter2
- zipkin-reporter-brave
- true
-
-
- io.zipkin.reporter2
- zipkin-sender-kafka
- true
-
-
-
- org.apache.kafka
- kafka-clients
-
-
-
-
- io.zipkin.reporter2
- zipkin-sender-activemq-client
- true
-
-
- org.apache.activemq
- activemq-client
-
-
-
-
- org.apache.activemq
- activemq-client
- true
-
-
- io.zipkin.reporter2
- zipkin-sender-amqp-client
- true
-
-
-
- com.rabbitmq
- amqp-client
-
-
-
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
- org.awaitility
- awaitility
- test
-
-
- org.mongodb
- mongodb-driver-reactivestreams
- test
-
-
-
-
- io.zipkin.brave
- brave-instrumentation-http-tests
- test
-
-
- com.squareup.okhttp3
- mockwebserver
- test
-
-
-
-
- com.squareup.okhttp3
- okhttp
-
- 4.8.0
- test
-
-
- com.tngtech.archunit
- archunit-junit5
- test
-
-
-
-
-
-
- fast
-
- false
-
-
-
-
- maven-surefire-plugin
-
- 4
- true
- -Xmx1024m -XX:MaxPermSize=256m
-
-
-
-
-
-
-
-
+
+
+
+
+ 4.0.0
+
+ spring-cloud-sleuth-autoconfigure
+ jar
+ Spring Cloud Sleuth AutoConfigure
+ Spring Cloud Sleuth AutoConfigure
+
+
+ org.springframework.cloud
+ spring-cloud-sleuth
+ 3.1.0-SNAPSHOT
+ ..
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-sleuth-instrumentation
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ true
+
+
+ io.micrometer
+ micrometer-core
+ true
+
+
+ io.projectreactor
+ reactor-core
+ true
+
+
+ org.reactivestreams
+ reactive-streams
+ true
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+ true
+
+
+ org.springframework.integration
+ spring-integration-core
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-config-server
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-starter-config
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-function-context
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-batch
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-stream
+
+ ${spring-cloud-stream.version}
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-commons
+
+
+ org.springframework
+ spring-context
+
+
+ org.springframework.cloud
+ spring-cloud-context
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-starter-task
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-data-cassandra
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-data-cassandra-reactive
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-deployer-spi
+ true
+
+
+ io.reactivex
+ rxjava
+ true
+
+
+ io.r2dbc
+ r2dbc-proxy
+ true
+
+
+ io.github.openfeign
+ feign-okhttp
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-starter-loadbalancer
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-starter-gateway
+ true
+
+
+ org.aspectj
+ aspectjrt
+
+
+ org.springframework.boot
+ spring-boot-starter-quartz
+ true
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure-processor
+ true
+
+
+ org.springframework.security.oauth
+ spring-security-oauth2
+ true
+
+
+ org.springframework.security.oauth.boot
+ spring-security-oauth2-autoconfigure
+ true
+
+
+ org.springframework.vault
+ spring-vault-core
+ true
+
+
+ p6spy
+ p6spy
+ true
+
+
+ net.ttddyy
+ datasource-proxy
+ true
+
+
+ org.apache.commons
+ commons-dbcp2
+ true
+
+
+ org.apache.tomcat
+ tomcat-jdbc
+ true
+
+
+ com.zaxxer
+ HikariCP
+ true
+
+
+ org.springframework.session
+ spring-session-data-redis
+ true
+
+
+
+ org.springframework.cloud
+ spring-cloud-sleuth-brave
+ true
+
+
+ io.zipkin.brave
+ brave
+
+
+ io.zipkin.reporter2
+ *
+
+
+ io.zipkin.zipkin2
+ *
+
+
+ true
+
+
+ io.zipkin.brave
+ brave-context-slf4j
+ true
+
+
+ io.zipkin.brave
+ brave-instrumentation-messaging
+ true
+
+
+ io.zipkin.brave
+ brave-instrumentation-rpc
+ true
+
+
+ io.zipkin.brave
+ brave-instrumentation-spring-rabbit
+ true
+
+
+ io.zipkin.brave
+ brave-instrumentation-kafka-clients
+ true
+
+
+ io.zipkin.brave
+ brave-instrumentation-kafka-streams
+ true
+
+
+ io.zipkin.brave
+ brave-instrumentation-httpclient
+ true
+
+
+ io.zipkin.brave
+ brave-instrumentation-httpasyncclient
+ true
+
+
+ io.zipkin.brave
+ brave-instrumentation-jms
+ true
+
+
+ io.zipkin.brave
+ brave-instrumentation-mongodb
+ true
+
+
+ io.zipkin.aws
+ brave-propagation-aws
+ true
+
+
+ javax.jms
+ javax.jms-api
+ true
+
+
+ io.opentracing.brave
+ brave-opentracing
+ true
+
+
+ org.apache.httpcomponents
+ httpasyncclient
+ true
+
+
+ org.springframework
+ spring-jms
+ true
+
+
+ io.projectreactor.kafka
+ reactor-kafka
+ true
+
+
+
+ io.github.lognet
+ grpc-spring-boot-starter
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+
+ io.zipkin.brave
+ brave-instrumentation-grpc
+ true
+
+
+ io.zipkin.reporter2
+ zipkin-reporter-metrics-micrometer
+
+
+ io.micrometer
+ micrometer-core
+
+
+ true
+
+
+
+ io.lettuce
+ lettuce-core
+ true
+
+
+ org.springframework.kafka
+ spring-kafka
+ true
+
+
+ org.apache.kafka
+ kafka-streams
+ true
+
+
+ org.springframework.amqp
+ spring-rabbit
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-data-mongodb
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-rsocket
+ true
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-sleuth-zipkin
+ true
+
+
+ io.zipkin.zipkin2
+ zipkin
+ true
+
+
+ io.zipkin.reporter2
+ zipkin-reporter
+ true
+
+
+ io.zipkin.reporter2
+ zipkin-reporter-brave
+ true
+
+
+ io.zipkin.reporter2
+ zipkin-sender-kafka
+ true
+
+
+
+ org.apache.kafka
+ kafka-clients
+
+
+
+
+ io.zipkin.reporter2
+ zipkin-sender-activemq-client
+ true
+
+
+ org.apache.activemq
+ activemq-client
+
+
+
+
+ org.apache.activemq
+ activemq-client
+ true
+
+
+ io.zipkin.reporter2
+ zipkin-sender-amqp-client
+ true
+
+
+
+ com.rabbitmq
+ amqp-client
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.awaitility
+ awaitility
+ test
+
+
+ org.mongodb
+ mongodb-driver-reactivestreams
+ test
+
+
+
+
+ io.zipkin.brave
+ brave-instrumentation-http-tests
+ test
+
+
+ com.squareup.okhttp3
+ mockwebserver
+ test
+
+
+
+
+ com.squareup.okhttp3
+ okhttp
+
+ 4.8.0
+ test
+
+
+ com.tngtech.archunit
+ archunit-junit5
+ test
+
+
+ com.h2database
+ h2
+ test
+
+
+
+
+
+
+ fast
+
+ false
+
+
+
+
+ maven-surefire-plugin
+
+ 4
+ true
+ -Xmx1024m -XX:MaxPermSize=256m
+
+
+
+
+
+
+
+
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/SleuthBaggageProperties.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/SleuthBaggageProperties.java
index 9f629e987..64f5985df 100644
--- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/SleuthBaggageProperties.java
+++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/SleuthBaggageProperties.java
@@ -36,19 +36,24 @@ public class SleuthBaggageProperties {
private boolean correlationEnabled = true;
/**
+ * List of fields that should be propagated over the wire.
*/
private List correlationFields = new ArrayList<>();
+ /**
+ * List of fields that should be accessible within the JVM process but not propagated
+ * over the wire.
+ */
private List 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 remoteFields = new ArrayList<>();
/**
+ * List of fields that should automatically become tags.
*/
private List tagFields = new ArrayList<>();
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/BraveFinishedSpanWriter.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/BraveFinishedSpanWriter.java
new file mode 100644
index 000000000..0448dc7ab
--- /dev/null
+++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/BraveFinishedSpanWriter.java
@@ -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 {
+
+ @Override
+ public String write(TextOutputFormat format, List 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;
+ }
+
+}
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/BufferingSpanReporter.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/BufferingSpanReporter.java
new file mode 100644
index 000000000..331db905c
--- /dev/null
+++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/BufferingSpanReporter.java
@@ -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 spans = new ConcurrentLinkedQueue<>();
+
+ public BufferingSpanReporter(int capacity) {
+ this.capacity = capacity;
+ }
+
+ /**
+ * Return a snapshot of currently buffered spans.
+ *
+ * This will not remove spans from the buffer, see {@link #drainFinishedSpans()} ()}
+ * for its counterpart.
+ * @return a snapshot of currently buffered spans.
+ */
+ public List getFinishedSpans() {
+ return new ArrayList<>(this.spans);
+ }
+
+ /**
+ * Return the {@link StartupTimeline timeline} by pulling spans from the buffer.
+ *
+ * This removes steps from the buffer, see {@link #getFinishedSpans()} for its
+ * read-only counterpart.
+ * @return buffered steps drained from the buffer.
+ */
+ public List drainFinishedSpans() {
+ List events = new ArrayList<>();
+ Iterator 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);
+ }
+ }
+
+}
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/FinishedSpanWriter.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/FinishedSpanWriter.java
new file mode 100644
index 000000000..c174467cc
--- /dev/null
+++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/FinishedSpanWriter.java
@@ -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 {
+
+ /**
+ * 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 spans);
+
+}
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/SleuthActuatorProperties.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/SleuthActuatorProperties.java
new file mode 100644
index 000000000..80d15b205
--- /dev/null
+++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/SleuthActuatorProperties.java
@@ -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;
+ }
+
+}
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/TextOutputFormat.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/TextOutputFormat.java
new file mode 100644
index 000000000..4e87ec6cb
--- /dev/null
+++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/TextOutputFormat.java
@@ -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 {
+
+ /**
+ * 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;
+ }
+
+}
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/TraceSleuthActuatorAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/TraceSleuthActuatorAutoConfiguration.java
new file mode 100644
index 000000000..03add3b2d
--- /dev/null
+++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/TraceSleuthActuatorAutoConfiguration.java
@@ -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() {
+
+ };
+ }
+
+ }
+
+}
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/TracesScrapeEndpoint.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/TracesScrapeEndpoint.java
new file mode 100644
index 000000000..36eaa262c
--- /dev/null
+++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/actuate/TracesScrapeEndpoint.java
@@ -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