Automatic tag table generation
Without this change we don't even really know how many tags are created and what are their values. With this change we want this information to be automatically rendered.
This commit is contained in:
59
docs/pom.xml
59
docs/pom.xml
@@ -32,6 +32,10 @@
|
||||
<main.basedir>${basedir}/..</main.basedir>
|
||||
<configprops.inclusionPattern>spring.sleuth.*|spring.zipkin.*</configprops.inclusionPattern>
|
||||
<upload-docs-zip.phase>deploy</upload-docs-zip.phase>
|
||||
|
||||
<!-- Tag generation script -->
|
||||
<roaster.version>2.22.3.Final</roaster.version>
|
||||
<module.inclusionPattern>.*/spring-cloud-sleuth-instrumentation/.*|.*/spring-cloud-sleuth-brave/.*</module.inclusionPattern>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@@ -42,9 +46,43 @@
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.forge.roaster</groupId>
|
||||
<artifactId>roaster-api</artifactId>
|
||||
<version>${roaster.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.forge.roaster</groupId>
|
||||
<artifactId>roaster-jdt</artifactId>
|
||||
<version>${roaster.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<sourceDirectory>src/main/asciidoc</sourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>add-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>src/main/asciidoc</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
@@ -64,6 +102,25 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>build-tags-and-spans</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>java</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<mainClass>org.springframework.cloud.sleuth.documentation.DocsFromSources</mainClass>
|
||||
<arguments>
|
||||
<argument>${maven.multiModuleProjectDirectory}</argument>
|
||||
<argument>${module.inclusionPattern}</argument>
|
||||
<argument>${maven.multiModuleProjectDirectory}/docs/src/main/asciidoc/</argument>
|
||||
</arguments>
|
||||
<includeProjectDependencies>true</includeProjectDependencies>
|
||||
<includePluginDependencies>true</includePluginDependencies>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
|
||||
@@ -9,10 +9,11 @@
|
||||
|spring.sleuth.baggage.local-fields | |
|
||||
|spring.sleuth.baggage.remote-fields | | List of fields that are referenced the same in-process as it is on the wire. For example, the field "x-vcap-request-id" would be set as-is including the prefix.
|
||||
|spring.sleuth.baggage.tag-fields | |
|
||||
|spring.sleuth.batch.enabled | `true` | Enable Spring Batch instrumentation.
|
||||
|spring.sleuth.circuitbreaker.enabled | `true` | Enable Spring Cloud CircuitBreaker instrumentation.
|
||||
|spring.sleuth.config.server.enabled | `true` | Enable Spring Cloud Config Server instrumentation.
|
||||
|spring.sleuth.deployer.enabled | `true` | Enable Spring Cloud Deployer instrumentation.
|
||||
|spring.sleuth.deployer.status-poll-delay | `1000` | Default poll delay to retrieve the deployed application status.
|
||||
|spring.sleuth.deployer.status-poll-delay | `500` | Default poll delay to retrieve the deployed application status.
|
||||
|spring.sleuth.enabled | `true` |
|
||||
|spring.sleuth.feign.enabled | `true` | Enable span information propagation when using Feign.
|
||||
|spring.sleuth.feign.processor.enabled | `true` | Enable post processor that wraps Feign Context in its tracing representations.
|
||||
@@ -22,6 +23,8 @@
|
||||
|spring.sleuth.integration.enabled | `true` | Enable Spring Integration instrumentation.
|
||||
|spring.sleuth.integration.patterns | `[!hystrixStreamOutput*, *, !channel*]` | An array of patterns against which channel names will be matched. @see org.springframework.integration.config.GlobalChannelInterceptor#patterns() Defaults to any channel name not matching the Hystrix Stream and functional Stream channel names.
|
||||
|spring.sleuth.integration.websockets.enabled | `true` | Enable tracing for WebSockets.
|
||||
|spring.sleuth.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.
|
||||
@@ -35,12 +38,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).
|
||||
@@ -55,6 +60,7 @@
|
||||
|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.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.
|
||||
|
||||
407
docs/src/main/asciidoc/_spans.adoc
Normal file
407
docs/src/main/asciidoc/_spans.adoc
Normal file
@@ -0,0 +1,407 @@
|
||||
=== 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.
|
||||
|===
|
||||
|
||||
=== 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.
|
||||
|===
|
||||
|
||||
=== 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.
|
||||
|===
|
||||
|
||||
=== 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.
|
||||
|===
|
||||
|
||||
=== 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`
|
||||
|
||||
=== 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.
|
||||
|===
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
:numbered!:
|
||||
|
||||
[[appendix]]
|
||||
[[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[]
|
||||
|
||||
[[common-application-properties]]
|
||||
== Common application properties
|
||||
|
||||
|
||||
@@ -480,6 +480,11 @@ That Logback configuration file:
|
||||
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 <<appendix.adoc#sleuth-spans, Sleuth Spans appendix>> for more information.
|
||||
|
||||
[[features-whats-next]]
|
||||
== What to Read Next
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.documentation;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitor;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.TreeSet;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DocsFromSources {
|
||||
|
||||
private final File projectRoot;
|
||||
|
||||
private final Pattern inclusionPattern;
|
||||
|
||||
private final File outputDir;
|
||||
|
||||
public DocsFromSources(File projectRoot, Pattern inclusionPattern, File outputDir) {
|
||||
this.projectRoot = projectRoot;
|
||||
this.inclusionPattern = inclusionPattern;
|
||||
this.outputDir = outputDir;
|
||||
}
|
||||
|
||||
public static void main(String... args) {
|
||||
String projectRoot = args[0];
|
||||
String inclusionPattern = args[1];
|
||||
inclusionPattern = inclusionPattern.replace("/", File.separator);
|
||||
String output = args[2];
|
||||
new DocsFromSources(new File(projectRoot), Pattern.compile(inclusionPattern), new File(output)).generate();
|
||||
}
|
||||
|
||||
public void generate() {
|
||||
Path path = this.projectRoot.toPath();
|
||||
System.out.println("Inclusion pattern is [" + this.inclusionPattern + "]");
|
||||
Collection<SpanEntry> spanEntries = new TreeSet<>();
|
||||
FileVisitor<Path> fv = new SpanSearchingFileVisitor(this.inclusionPattern, spanEntries);
|
||||
try {
|
||||
Files.walkFileTree(path, fv);
|
||||
Path output = new File(this.outputDir, "_spans.adoc").toPath();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
System.out.println("======================================");
|
||||
System.out.println("Summary of sources analysis");
|
||||
System.out.println("Found [" + spanEntries.size() + "] spans");
|
||||
System.out.println(
|
||||
"Found [" + spanEntries.stream().flatMap(e -> e.tagKeys.stream()).distinct().count() + "] tags");
|
||||
System.out.println(
|
||||
"Found [" + spanEntries.stream().flatMap(e -> e.events.stream()).distinct().count() + "] events");
|
||||
spanEntries.forEach(spanEntry -> stringBuilder.append(spanEntry.toString()).append("\n\n"));
|
||||
Files.write(output, stringBuilder.toString().getBytes());
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.documentation;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
class KeyValueEntry implements Comparable<KeyValueEntry> {
|
||||
|
||||
final String name;
|
||||
|
||||
final String description;
|
||||
|
||||
KeyValueEntry(String name, String description) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
KeyValueEntry tag = (KeyValueEntry) o;
|
||||
return Objects.equals(name, tag.name) && Objects.equals(description, tag.description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(KeyValueEntry o) {
|
||||
return name.compareTo(o.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "|" + name + "|" + description();
|
||||
}
|
||||
|
||||
private String description() {
|
||||
String suffix = "";
|
||||
if (this.name.contains("%s")) {
|
||||
suffix = " (since the name contains `%s` the final value will be resolved at runtime)";
|
||||
}
|
||||
return description + suffix;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.documentation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
class SpanEntry implements Comparable<SpanEntry> {
|
||||
|
||||
final String name;
|
||||
|
||||
final String enclosingClass;
|
||||
|
||||
final String enumName;
|
||||
|
||||
final String description;
|
||||
|
||||
final Collection<KeyValueEntry> tagKeys;
|
||||
|
||||
final Collection<KeyValueEntry> events;
|
||||
|
||||
SpanEntry(String name, String enclosingClass, String enumName, String description,
|
||||
Collection<KeyValueEntry> tagKeys, Collection<KeyValueEntry> events) {
|
||||
Assert.isTrue(StringUtils.hasText(name), "Span name must not be empty");
|
||||
Assert.isTrue(StringUtils.hasText(description), "Span description must not be empty");
|
||||
this.name = name;
|
||||
this.enclosingClass = enclosingClass;
|
||||
this.enumName = enumName;
|
||||
this.description = description;
|
||||
this.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 (!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
|
||||
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.documentation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.jboss.forge.roaster.Roaster;
|
||||
import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Expression;
|
||||
import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.MethodInvocation;
|
||||
import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ReturnStatement;
|
||||
import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.jboss.forge.roaster.model.JavaType;
|
||||
import org.jboss.forge.roaster.model.JavaUnit;
|
||||
import org.jboss.forge.roaster.model.impl.JavaEnumImpl;
|
||||
import org.jboss.forge.roaster.model.source.EnumConstantSource;
|
||||
import org.jboss.forge.roaster.model.source.JavaSource;
|
||||
import org.jboss.forge.roaster.model.source.MemberSource;
|
||||
|
||||
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
|
||||
import org.springframework.cloud.sleuth.docs.EventValue;
|
||||
import org.springframework.cloud.sleuth.docs.TagKey;
|
||||
|
||||
class SpanSearchingFileVisitor extends SimpleFileVisitor<Path> {
|
||||
|
||||
private final Pattern pattern;
|
||||
|
||||
private final Collection<SpanEntry> spanEntries;
|
||||
|
||||
SpanSearchingFileVisitor(Pattern pattern, Collection<SpanEntry> spanEntries) {
|
||||
this.pattern = pattern;
|
||||
this.spanEntries = spanEntries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
if (!pattern.matcher(file.toString()).matches()) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
else if (!file.toString().endsWith(".java")) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
try (InputStream stream = Files.newInputStream(file)) {
|
||||
JavaUnit unit = Roaster.parseUnit(stream);
|
||||
JavaType myClass = unit.getGoverningType();
|
||||
if (!(myClass instanceof JavaEnumImpl)) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
JavaEnumImpl myEnum = (JavaEnumImpl) myClass;
|
||||
if (!myEnum.getInterfaces().contains(DocumentedSpan.class.getCanonicalName())) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
System.out.println("Checking [" + myEnum.getName() + "]");
|
||||
if (myEnum.getEnumConstants().size() == 0) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
for (EnumConstantSource enumConstant : myEnum.getEnumConstants()) {
|
||||
SpanEntry entry = parseSpan(enumConstant, myEnum);
|
||||
if (entry != null) {
|
||||
spanEntries.add(entry);
|
||||
System.out.println(
|
||||
"Found [" + entry.tagKeys.size() + "] tags and [" + entry.events.size() + "] events");
|
||||
}
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
private SpanEntry parseSpan(EnumConstantSource enumConstant, JavaEnumImpl myEnum) {
|
||||
List<MemberSource<EnumConstantSource.Body, ?>> members = enumConstant.getBody().getMembers();
|
||||
if (members.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String name = "";
|
||||
String description = enumConstant.getJavaDoc().getText();
|
||||
Collection<KeyValueEntry> tags = new TreeSet<>();
|
||||
Collection<KeyValueEntry> events = new TreeSet<>();
|
||||
for (MemberSource<EnumConstantSource.Body, ?> member : members) {
|
||||
Object internal = member.getInternal();
|
||||
if (!(internal instanceof MethodDeclaration)) {
|
||||
return null;
|
||||
}
|
||||
MethodDeclaration methodDeclaration = (MethodDeclaration) internal;
|
||||
String methodName = methodDeclaration.getName().getIdentifier();
|
||||
if ("getName".equals(methodName)) {
|
||||
name = readStringReturnValue(methodDeclaration);
|
||||
}
|
||||
else if ("getTagKeys".equals(methodName)) {
|
||||
tags.addAll(keyValueEntries(myEnum, methodDeclaration, TagKey.class));
|
||||
}
|
||||
else if ("getEvents".equals(methodName)) {
|
||||
events.addAll(keyValueEntries(myEnum, methodDeclaration, EventValue.class));
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return new SpanEntry(name, myEnum.getCanonicalName(), enumConstant.getName(), description, tags, events);
|
||||
}
|
||||
|
||||
private Collection<KeyValueEntry> keyValueEntries(JavaEnumImpl myEnum, MethodDeclaration methodDeclaration,
|
||||
Class requiredClass) {
|
||||
Collection<String> enumNames = readClassValue(methodDeclaration);
|
||||
Collection<KeyValueEntry> keyValues = new TreeSet<>();
|
||||
enumNames.forEach(enumName -> {
|
||||
List<JavaSource<?>> nestedTypes = myEnum.getNestedTypes();
|
||||
JavaSource<?> nestedSource = nestedTypes.stream()
|
||||
.filter(javaSource -> javaSource.getName().equals(enumName)).findFirst().orElseThrow(
|
||||
() -> new IllegalStateException("There's no nested type with name [" + enumName + "]"));
|
||||
updateKeyValuesFromEnum(myEnum, nestedSource, requiredClass, keyValues);
|
||||
});
|
||||
return keyValues;
|
||||
}
|
||||
|
||||
private void updateKeyValuesFromEnum(JavaEnumImpl parentEnum, JavaSource<?> source, Class requiredClass,
|
||||
Collection<KeyValueEntry> keyValues) {
|
||||
if (!(source instanceof JavaEnumImpl)) {
|
||||
return;
|
||||
}
|
||||
JavaEnumImpl myEnum = (JavaEnumImpl) source;
|
||||
if (!myEnum.getInterfaces().contains(requiredClass.getCanonicalName())) {
|
||||
return;
|
||||
}
|
||||
System.out.println("Checking [" + parentEnum.getName() + "." + myEnum.getName() + "]");
|
||||
if (myEnum.getEnumConstants().size() == 0) {
|
||||
return;
|
||||
}
|
||||
for (EnumConstantSource enumConstant : myEnum.getEnumConstants()) {
|
||||
String keyValue = enumKeyValue(enumConstant);
|
||||
keyValues.add(new KeyValueEntry(keyValue, enumConstant.getJavaDoc().getText()));
|
||||
}
|
||||
}
|
||||
|
||||
private String enumKeyValue(EnumConstantSource enumConstant) {
|
||||
List<MemberSource<EnumConstantSource.Body, ?>> members = enumConstant.getBody().getMembers();
|
||||
if (members.isEmpty()) {
|
||||
System.err.println("No method declarations in the enum.");
|
||||
return "";
|
||||
}
|
||||
Object internal = members.get(0).getInternal();
|
||||
if (!(internal instanceof MethodDeclaration)) {
|
||||
System.err.println("Can't read the member [" + internal.getClass() + "] as a method declaration.");
|
||||
return "";
|
||||
}
|
||||
MethodDeclaration methodDeclaration = (MethodDeclaration) internal;
|
||||
if (methodDeclaration.getBody().statements().isEmpty()) {
|
||||
System.err.println("Body was empty. Continuing...");
|
||||
return "";
|
||||
}
|
||||
return stringFromReturnMethodDeclaration(methodDeclaration);
|
||||
}
|
||||
|
||||
private String stringFromReturnMethodDeclaration(MethodDeclaration methodDeclaration) {
|
||||
Object statement = methodDeclaration.getBody().statements().get(0);
|
||||
if (!(statement instanceof ReturnStatement)) {
|
||||
System.err.println("Statement [" + statement.getClass() + "] is not a return statement.");
|
||||
return "";
|
||||
}
|
||||
ReturnStatement returnStatement = (ReturnStatement) statement;
|
||||
Expression expression = returnStatement.getExpression();
|
||||
if (!(expression instanceof StringLiteral)) {
|
||||
System.err.println("Statement [" + statement.getClass() + "] is not a string literal statement.");
|
||||
return "";
|
||||
}
|
||||
return ((StringLiteral) expression).getLiteralValue();
|
||||
}
|
||||
|
||||
private String readStringReturnValue(MethodDeclaration methodDeclaration) {
|
||||
return stringFromReturnMethodDeclaration(methodDeclaration);
|
||||
}
|
||||
|
||||
private Collection<String> readClassValue(MethodDeclaration methodDeclaration) {
|
||||
Object statement = methodDeclaration.getBody().statements().get(0);
|
||||
if (!(statement instanceof ReturnStatement)) {
|
||||
System.err.println("Statement [" + statement.getClass() + "] is not a return statement.");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
ReturnStatement returnStatement = (ReturnStatement) statement;
|
||||
Expression expression = returnStatement.getExpression();
|
||||
if (!(expression instanceof MethodInvocation)) {
|
||||
System.err.println("Statement [" + statement.getClass() + "] is not a method invocation.");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
MethodInvocation methodInvocation = (MethodInvocation) expression;
|
||||
if ("merge".equals(methodInvocation.getName().getIdentifier())) {
|
||||
// TODO: There must be a better way to do this...
|
||||
// TagKey.merge(TestSpanTags.values(),AsyncSpanTags.values())
|
||||
String invocationString = methodInvocation.toString();
|
||||
Matcher matcher = Pattern.compile("([a-zA-Z]+.values)").matcher(invocationString);
|
||||
Collection<String> classNames = new TreeSet<>();
|
||||
while (matcher.find()) {
|
||||
String className = matcher.group(1).split("\\.")[0];
|
||||
classNames.add(className);
|
||||
}
|
||||
return classNames;
|
||||
}
|
||||
else if (!methodInvocation.toString().endsWith(".values()")) {
|
||||
throw new IllegalStateException("You have to use the static .values() method on the enum that implements "
|
||||
+ TagKey.class + " or " + EventValue.class
|
||||
+ " interface or use [TagKey.merge(...)] method to merge multiple values from tags");
|
||||
}
|
||||
// will return Tags
|
||||
return Collections.singletonList(methodInvocation.getExpression().toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
8
pom.xml
8
pom.xml
@@ -150,6 +150,14 @@
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.8.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<spring.cloud.sleuth.assertions.enabled>true</spring.cloud.sleuth.assertions.enabled>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
|
||||
@@ -1,222 +1,222 @@
|
||||
/*
|
||||
* 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 org.springframework.cloud.sleuth.propagation.Propagator;
|
||||
|
||||
/**
|
||||
*
|
||||
* This API was heavily influenced by Brave. Parts of its documentation were taken
|
||||
* directly from Brave.
|
||||
*
|
||||
* Span is a single unit of work that needs to be started and stopped. Contains timing
|
||||
* information and events and tags.
|
||||
*
|
||||
* @author OpenZipkin Brave Authors
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public interface Span extends SpanCustomizer {
|
||||
|
||||
/**
|
||||
* @return {@code true} when no recording is done and nothing is reported to an
|
||||
* external system. However, this span should still be injected into outgoing
|
||||
* requests. Use this flag to avoid performing expensive computation.
|
||||
*/
|
||||
boolean isNoop();
|
||||
|
||||
/**
|
||||
* @return {@link TraceContext} corresponding to this span.
|
||||
*/
|
||||
TraceContext context();
|
||||
|
||||
/**
|
||||
* Starts this span.
|
||||
* @return this span
|
||||
*/
|
||||
Span start();
|
||||
|
||||
/**
|
||||
* Sets a name on this span.
|
||||
* @param name name to set on the span
|
||||
* @return this span
|
||||
*/
|
||||
Span name(String name);
|
||||
|
||||
/**
|
||||
* Sets an event on this span.
|
||||
* @param value event name to set on the span
|
||||
* @return this span
|
||||
*/
|
||||
Span event(String value);
|
||||
|
||||
/**
|
||||
* Sets a tag on this span.
|
||||
* @param key tag key
|
||||
* @param value tag value
|
||||
* @return this span
|
||||
*/
|
||||
Span tag(String key, String value);
|
||||
|
||||
/**
|
||||
* Records an exception for this span.
|
||||
* @param throwable to record
|
||||
* @return this span
|
||||
*/
|
||||
Span error(Throwable throwable);
|
||||
|
||||
/**
|
||||
* Ends the span. The span gets stopped and recorded if not noop.
|
||||
*/
|
||||
void end();
|
||||
|
||||
/**
|
||||
* Ends the span. The span gets stopped but does not get recorded.
|
||||
*/
|
||||
void abandon();
|
||||
|
||||
/**
|
||||
* Sets the remote service name for the span.
|
||||
* @param remoteServiceName remote service name
|
||||
* @return this span
|
||||
* @since 3.0.3
|
||||
*/
|
||||
default Span remoteServiceName(String remoteServiceName) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of span. Can be used to specify additional relationships between spans in
|
||||
* addition to a parent/child relationship.
|
||||
*
|
||||
* Documentation of the enum taken from OpenTelemetry.
|
||||
*/
|
||||
enum Kind {
|
||||
|
||||
/**
|
||||
* Indicates that the span covers server-side handling of an RPC or other remote
|
||||
* request.
|
||||
*/
|
||||
SERVER,
|
||||
|
||||
/**
|
||||
* Indicates that the span covers the client-side wrapper around an RPC or other
|
||||
* remote request.
|
||||
*/
|
||||
CLIENT,
|
||||
|
||||
/**
|
||||
* Indicates that the span describes producer sending a message to a broker.
|
||||
* Unlike client and server, there is no direct critical path latency relationship
|
||||
* between producer and consumer spans.
|
||||
*/
|
||||
PRODUCER,
|
||||
|
||||
/**
|
||||
* Indicates that the span describes consumer receiving a message from a broker.
|
||||
* Unlike client and server, there is no direct critical path latency relationship
|
||||
* between producer and consumer spans.
|
||||
*/
|
||||
CONSUMER
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* In some cases (e.g. when dealing with
|
||||
* {@link Propagator#extract(Object, Propagator.Getter)}'s we want to create a span
|
||||
* that has not yet been started, yet it's heavily configurable (some options are not
|
||||
* possible to be set when a span has already been started). We can achieve that by
|
||||
* using a builder.
|
||||
*
|
||||
* Inspired by OpenZipkin Brave and OpenTelemetry API.
|
||||
*/
|
||||
interface Builder {
|
||||
|
||||
/**
|
||||
* Sets the parent of the built span.
|
||||
* @param context parent's context
|
||||
* @return this
|
||||
*/
|
||||
Builder setParent(TraceContext context);
|
||||
|
||||
/**
|
||||
* Sets no parent of the built span.
|
||||
* @return this
|
||||
*/
|
||||
Builder setNoParent();
|
||||
|
||||
/**
|
||||
* Sets the name of the span.
|
||||
* @param name span name
|
||||
* @return this
|
||||
*/
|
||||
Builder name(String name);
|
||||
|
||||
/**
|
||||
* Sets an event on the span.
|
||||
* @param value event value
|
||||
* @return this
|
||||
*/
|
||||
Builder event(String value);
|
||||
|
||||
/**
|
||||
* Sets a tag on the span.
|
||||
* @param key tag key
|
||||
* @param value tag value
|
||||
* @return this
|
||||
*/
|
||||
Builder tag(String key, String value);
|
||||
|
||||
/**
|
||||
* Sets an error on the span.
|
||||
* @param throwable error to set
|
||||
* @return this
|
||||
*/
|
||||
Builder error(Throwable throwable);
|
||||
|
||||
/**
|
||||
* Sets the kind on the span.
|
||||
* @param spanKind kind of the span
|
||||
* @return this
|
||||
*/
|
||||
Builder kind(Span.Kind spanKind);
|
||||
|
||||
/**
|
||||
* Sets the remote service name for the span.
|
||||
* @param remoteServiceName remote service name
|
||||
* @return this
|
||||
*/
|
||||
Builder remoteServiceName(String remoteServiceName);
|
||||
|
||||
/**
|
||||
* Sets the remote URL for the span.
|
||||
* @param remoteUrl remote service name
|
||||
* @return this
|
||||
*/
|
||||
default Builder remoteUrl(String remoteUrl) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and starts the span.
|
||||
* @return started span
|
||||
*/
|
||||
Span start();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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 org.springframework.cloud.sleuth.propagation.Propagator;
|
||||
|
||||
/**
|
||||
*
|
||||
* This API was heavily influenced by Brave. Parts of its documentation were taken
|
||||
* directly from Brave.
|
||||
*
|
||||
* Span is a single unit of work that needs to be started and stopped. Contains timing
|
||||
* information and events and tags.
|
||||
*
|
||||
* @author OpenZipkin Brave Authors
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public interface Span extends SpanCustomizer {
|
||||
|
||||
/**
|
||||
* @return {@code true} when no recording is done and nothing is reported to an
|
||||
* external system. However, this span should still be injected into outgoing
|
||||
* requests. Use this flag to avoid performing expensive computation.
|
||||
*/
|
||||
boolean isNoop();
|
||||
|
||||
/**
|
||||
* @return {@link TraceContext} corresponding to this span.
|
||||
*/
|
||||
TraceContext context();
|
||||
|
||||
/**
|
||||
* Starts this span.
|
||||
* @return this span
|
||||
*/
|
||||
Span start();
|
||||
|
||||
/**
|
||||
* Sets a name on this span.
|
||||
* @param name name to set on the span
|
||||
* @return this span
|
||||
*/
|
||||
Span name(String name);
|
||||
|
||||
/**
|
||||
* Sets an event on this span.
|
||||
* @param value event name to set on the span
|
||||
* @return this span
|
||||
*/
|
||||
Span event(String value);
|
||||
|
||||
/**
|
||||
* Sets a tag on this span.
|
||||
* @param key tag key
|
||||
* @param value tag value
|
||||
* @return this span
|
||||
*/
|
||||
Span tag(String key, String value);
|
||||
|
||||
/**
|
||||
* Records an exception for this span.
|
||||
* @param throwable to record
|
||||
* @return this span
|
||||
*/
|
||||
Span error(Throwable throwable);
|
||||
|
||||
/**
|
||||
* Ends the span. The span gets stopped and recorded if not noop.
|
||||
*/
|
||||
void end();
|
||||
|
||||
/**
|
||||
* Ends the span. The span gets stopped but does not get recorded.
|
||||
*/
|
||||
void abandon();
|
||||
|
||||
/**
|
||||
* Sets the remote service name for the span.
|
||||
* @param remoteServiceName remote service name
|
||||
* @return this span
|
||||
* @since 3.0.3
|
||||
*/
|
||||
default Span remoteServiceName(String remoteServiceName) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of span. Can be used to specify additional relationships between spans in
|
||||
* addition to a parent/child relationship.
|
||||
*
|
||||
* Documentation of the enum taken from OpenTelemetry.
|
||||
*/
|
||||
enum Kind {
|
||||
|
||||
/**
|
||||
* Indicates that the span covers server-side handling of an RPC or other remote
|
||||
* request.
|
||||
*/
|
||||
SERVER,
|
||||
|
||||
/**
|
||||
* Indicates that the span covers the client-side wrapper around an RPC or other
|
||||
* remote request.
|
||||
*/
|
||||
CLIENT,
|
||||
|
||||
/**
|
||||
* Indicates that the span describes producer sending a message to a broker.
|
||||
* Unlike client and server, there is no direct critical path latency relationship
|
||||
* between producer and consumer spans.
|
||||
*/
|
||||
PRODUCER,
|
||||
|
||||
/**
|
||||
* Indicates that the span describes consumer receiving a message from a broker.
|
||||
* Unlike client and server, there is no direct critical path latency relationship
|
||||
* between producer and consumer spans.
|
||||
*/
|
||||
CONSUMER
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* In some cases (e.g. when dealing with
|
||||
* {@link Propagator#extract(Object, Propagator.Getter)}'s we want to create a span
|
||||
* that has not yet been started, yet it's heavily configurable (some options are not
|
||||
* possible to be set when a span has already been started). We can achieve that by
|
||||
* using a builder.
|
||||
*
|
||||
* Inspired by OpenZipkin Brave and OpenTelemetry API.
|
||||
*/
|
||||
interface Builder {
|
||||
|
||||
/**
|
||||
* Sets the parent of the built span.
|
||||
* @param context parent's context
|
||||
* @return this
|
||||
*/
|
||||
Builder setParent(TraceContext context);
|
||||
|
||||
/**
|
||||
* Sets no parent of the built span.
|
||||
* @return this
|
||||
*/
|
||||
Builder setNoParent();
|
||||
|
||||
/**
|
||||
* Sets the name of the span.
|
||||
* @param name span name
|
||||
* @return this
|
||||
*/
|
||||
Builder name(String name);
|
||||
|
||||
/**
|
||||
* Sets an event on the span.
|
||||
* @param value event value
|
||||
* @return this
|
||||
*/
|
||||
Builder event(String value);
|
||||
|
||||
/**
|
||||
* Sets a tag on the span.
|
||||
* @param key tag key
|
||||
* @param value tag value
|
||||
* @return this
|
||||
*/
|
||||
Builder tag(String key, String value);
|
||||
|
||||
/**
|
||||
* Sets an error on the span.
|
||||
* @param throwable error to set
|
||||
* @return this
|
||||
*/
|
||||
Builder error(Throwable throwable);
|
||||
|
||||
/**
|
||||
* Sets the kind on the span.
|
||||
* @param spanKind kind of the span
|
||||
* @return this
|
||||
*/
|
||||
Builder kind(Span.Kind spanKind);
|
||||
|
||||
/**
|
||||
* Sets the remote service name for the span.
|
||||
* @param remoteServiceName remote service name
|
||||
* @return this
|
||||
*/
|
||||
Builder remoteServiceName(String remoteServiceName);
|
||||
|
||||
/**
|
||||
* Sets the remote URL for the span.
|
||||
* @param remoteUrl remote service name
|
||||
* @return this
|
||||
*/
|
||||
default Builder remoteUrl(String remoteUrl) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and starts the span.
|
||||
* @return started span
|
||||
*/
|
||||
Span start();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param documentedSpan span configuration
|
||||
* @param span span to wrap in assertions
|
||||
* @return asserting span
|
||||
*/
|
||||
static AssertingSpan of(DocumentedSpan documentedSpan, Span span) {
|
||||
if (span == null) {
|
||||
return null;
|
||||
}
|
||||
else if (span instanceof AssertingSpan) {
|
||||
return (AssertingSpan) span;
|
||||
}
|
||||
return new ImmutableAssertingSpan(documentedSpan, span);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying delegate. Used when casting is necessary.
|
||||
* @param span span to check for wrapping
|
||||
* @param <T> type extending a span
|
||||
* @return unwrapped object
|
||||
*/
|
||||
static <T extends Span> T unwrap(Span span) {
|
||||
if (span == null) {
|
||||
return null;
|
||||
}
|
||||
else if (span instanceof AssertingSpan) {
|
||||
return (T) ((AssertingSpan) span).getDelegate();
|
||||
}
|
||||
return (T) span;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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 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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.docs;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanCustomizer;
|
||||
|
||||
/**
|
||||
* A {@link SpanCustomizer} that can perform assertions on itself.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface AssertingSpanCustomizer extends SpanCustomizer {
|
||||
|
||||
/**
|
||||
* @return a {@link DocumentedSpan} with span configuration
|
||||
*/
|
||||
DocumentedSpan getDocumentedSpan();
|
||||
|
||||
/**
|
||||
* @return wrapped {@link SpanCustomizer}
|
||||
*/
|
||||
SpanCustomizer getDelegate();
|
||||
|
||||
@Override
|
||||
default AssertingSpanCustomizer tag(String key, String value) {
|
||||
DocumentedSpanAssertions.assertThatKeyIsValid(key, getDocumentedSpan());
|
||||
getDelegate().tag(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a tag on a span.
|
||||
* @param key tag key
|
||||
* @param value tag
|
||||
* @return this, for chaining
|
||||
*/
|
||||
default AssertingSpanCustomizer tag(TagKey key, String value) {
|
||||
DocumentedSpanAssertions.assertThatKeyIsValid(key, getDocumentedSpan());
|
||||
getDelegate().tag(key.getKey(), value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
default AssertingSpanCustomizer event(String value) {
|
||||
DocumentedSpanAssertions.assertThatEventIsValid(value, getDocumentedSpan());
|
||||
getDelegate().event(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an event on a span.
|
||||
* @param value event
|
||||
* @return this, for chaining
|
||||
*/
|
||||
default AssertingSpanCustomizer event(EventValue value) {
|
||||
DocumentedSpanAssertions.assertThatEventIsValid(value, getDocumentedSpan());
|
||||
getDelegate().event(value.getValue());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
default AssertingSpanCustomizer name(String name) {
|
||||
DocumentedSpanAssertions.assertThatNameIsValid(name, getDocumentedSpan());
|
||||
getDelegate().name(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param documentedSpan span configuration
|
||||
* @param span span to wrap in assertions
|
||||
* @return asserting span customizer
|
||||
*/
|
||||
static AssertingSpanCustomizer of(DocumentedSpan documentedSpan, SpanCustomizer span) {
|
||||
if (span instanceof AssertingSpanCustomizer) {
|
||||
return (AssertingSpanCustomizer) span;
|
||||
}
|
||||
return new ImmutableAssertingSpanCustomizer(documentedSpan, span);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying delegate. Used when casting is necessary.
|
||||
* @param span span to check for wrapping
|
||||
* @param <T> type extending a span
|
||||
* @return unwrapped object
|
||||
*/
|
||||
static <T extends SpanCustomizer> T unwrap(SpanCustomizer span) {
|
||||
if (span == null) {
|
||||
return null;
|
||||
}
|
||||
else if (span instanceof AssertingSpanCustomizer) {
|
||||
return (T) ((AssertingSpanCustomizer) span).getDelegate();
|
||||
}
|
||||
return (T) span;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.docs;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* In order to turn on the assertions you need to either turn on the
|
||||
* {@code spring.cloud.sleuth.assertions.enabled} system property or
|
||||
* {@code SPRING_CLOUD_SLEUTH_ASSERTIONS_ENABLED} environment variable.
|
||||
*/
|
||||
final class DocumentedSpanAssertions {
|
||||
|
||||
static boolean SLEUTH_SPAN_ASSERTIONS_ON = Boolean.parseBoolean(System.getProperty(
|
||||
"spring.cloud.sleuth.assertions.enabled", System.getenv("SPRING_CLOUD_SLEUTH_ASSERTIONS_ENABLED") != null
|
||||
? System.getenv("SPRING_CLOUD_SLEUTH_ASSERTIONS_ENABLED") : "false"));
|
||||
|
||||
private static final Map<String, Pattern> PATTERN_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
private static final Pattern SPECIAL_REGEX_CHARS = Pattern.compile("[{}()\\[\\].+*?^$\\\\|]");
|
||||
|
||||
private DocumentedSpanAssertions() {
|
||||
throw new IllegalStateException("Can't instantiate utility class");
|
||||
}
|
||||
|
||||
static void assertThatKeyIsValid(String key, DocumentedSpan documentedSpan) {
|
||||
if (SLEUTH_SPAN_ASSERTIONS_ON) {
|
||||
TagKey[] allowedKeys = documentedSpan.getTagKeys();
|
||||
if (allowedKeys.length == 0) {
|
||||
return;
|
||||
}
|
||||
boolean validTagKey = Arrays.stream(allowedKeys)
|
||||
.anyMatch(tagKey -> patternOrValueMatches(key, tagKey.getKey())
|
||||
&& hasRequiredPrefix(key, documentedSpan.prefix()));
|
||||
if (!validTagKey) {
|
||||
throw new AssertionError("The key [" + key + "] is invalid. You can use only one matching "
|
||||
+ Arrays.stream(allowedKeys).map(TagKey::getKey).collect(Collectors.toList())
|
||||
+ prefixWarningIfPresent(documentedSpan));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String prefixWarningIfPresent(DocumentedSpan documentedSpan) {
|
||||
return StringUtils.hasText(documentedSpan.prefix())
|
||||
? ". Also it has start with [" + documentedSpan.prefix() + "] prefix" : "";
|
||||
}
|
||||
|
||||
static void assertThatKeyIsValid(TagKey key, DocumentedSpan documentedSpan) {
|
||||
if (SLEUTH_SPAN_ASSERTIONS_ON) {
|
||||
TagKey[] allowedKeys = documentedSpan.getTagKeys();
|
||||
if (allowedKeys.length == 0) {
|
||||
return;
|
||||
}
|
||||
if (Arrays.stream(allowedKeys).noneMatch(tagKey -> patternOrValueMatches(key.getKey(), tagKey.getKey())
|
||||
&& hasRequiredPrefix(key.getKey(), documentedSpan.prefix()))) {
|
||||
throw new AssertionError("The key [" + key.getKey() + "] is invalid. You can use only one matching "
|
||||
+ Arrays.stream(allowedKeys).map(TagKey::getKey).collect(Collectors.toList())
|
||||
+ prefixWarningIfPresent(documentedSpan));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void assertThatNameIsValid(String name, DocumentedSpan documentedSpan) {
|
||||
String allowedName = documentedSpan.getName();
|
||||
if (SLEUTH_SPAN_ASSERTIONS_ON && !patternOrValueMatches(name, allowedName)) {
|
||||
throw new AssertionError(
|
||||
"The name [" + name + "] is invalid. You can use only one matching [" + allowedName + "]");
|
||||
}
|
||||
}
|
||||
|
||||
static void assertThatEventIsValid(String eventValue, DocumentedSpan documentedSpan) {
|
||||
if (SLEUTH_SPAN_ASSERTIONS_ON) {
|
||||
EventValue[] allowed = documentedSpan.getEvents();
|
||||
if (allowed.length == 0) {
|
||||
return;
|
||||
}
|
||||
boolean valid = Arrays.stream(allowed).anyMatch(value -> patternOrValueMatches(eventValue, value.getValue())
|
||||
&& hasRequiredPrefix(eventValue, documentedSpan.prefix()));
|
||||
if (!valid) {
|
||||
throw new AssertionError("The event [" + eventValue + "] is invalid. You can use only one matching "
|
||||
+ Arrays.stream(allowed).map(EventValue::getValue).collect(Collectors.toList())
|
||||
+ prefixWarningIfPresent(documentedSpan));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void assertThatEventIsValid(EventValue eventValue, DocumentedSpan documentedSpan) {
|
||||
if (SLEUTH_SPAN_ASSERTIONS_ON) {
|
||||
EventValue[] allowed = documentedSpan.getEvents();
|
||||
if (allowed.length == 0) {
|
||||
return;
|
||||
}
|
||||
if (Arrays.stream(allowed).noneMatch(value -> patternOrValueMatches(eventValue.getValue(), value.getValue())
|
||||
&& hasRequiredPrefix(eventValue.getValue(), documentedSpan.prefix()))) {
|
||||
throw new AssertionError(
|
||||
"The event [" + eventValue.getValue() + "] is invalid. You can use only one matching "
|
||||
+ Arrays.stream(allowed).map(EventValue::getValue).collect(Collectors.toList())
|
||||
+ prefixWarningIfPresent(documentedSpan));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void assertThatSpanStartedBeforeEnd(AssertingSpan span) {
|
||||
if (SLEUTH_SPAN_ASSERTIONS_ON && !span.isStarted()) {
|
||||
throw new AssertionError("The span was not started, however you're trying to end it");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean patternOrValueMatches(String pickedValue, String allowedValue) {
|
||||
if (allowedValue.contains("%s")) {
|
||||
String stringPattern = escapeSpecialRegexWithSingleEscape(allowedValue).replaceAll("%s", ".*?");
|
||||
Pattern pattern = PATTERN_CACHE.computeIfAbsent(stringPattern, Pattern::compile);
|
||||
return pattern.matcher(pickedValue).matches();
|
||||
}
|
||||
return allowedValue.equals(pickedValue);
|
||||
}
|
||||
|
||||
private static boolean hasRequiredPrefix(String value, String prefix) {
|
||||
if (StringUtils.hasText(prefix)) {
|
||||
return value.startsWith(prefix);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String escapeSpecialRegexWithSingleEscape(String str) {
|
||||
return SPECIAL_REGEX_CHARS.matcher(str).replaceAll("\\\\$0");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -47,22 +47,26 @@ class BraveScopedSpan implements ScopedSpan {
|
||||
|
||||
@Override
|
||||
public ScopedSpan name(String name) {
|
||||
return new BraveScopedSpan(this.span.name(name));
|
||||
this.span.name(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScopedSpan tag(String key, String value) {
|
||||
return new BraveScopedSpan(this.span.tag(key, value));
|
||||
this.span.tag(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScopedSpan event(String value) {
|
||||
return new BraveScopedSpan(this.span.annotate(value));
|
||||
this.span.annotate(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScopedSpan error(Throwable throwable) {
|
||||
return new BraveScopedSpan(this.span.error(throwable));
|
||||
this.span.error(throwable);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.Objects;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceContext;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpan;
|
||||
|
||||
/**
|
||||
* Brave implementation of a {@link Span}.
|
||||
@@ -50,22 +51,26 @@ public class BraveSpan implements Span {
|
||||
|
||||
@Override
|
||||
public Span start() {
|
||||
return new BraveSpan(this.delegate.start());
|
||||
this.delegate.start();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span name(String name) {
|
||||
return new BraveSpan(this.delegate.name(name));
|
||||
this.delegate.name(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span event(String value) {
|
||||
return new BraveSpan(this.delegate.annotate(value));
|
||||
this.delegate.annotate(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span tag(String key, String value) {
|
||||
return new BraveSpan(this.delegate.tag(key, value));
|
||||
this.delegate.tag(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,7 +78,7 @@ public class BraveSpan implements Span {
|
||||
String message = throwable.getMessage() == null ? throwable.getClass().getSimpleName() : throwable.getMessage();
|
||||
this.delegate.tag("error", message);
|
||||
this.delegate.error(throwable);
|
||||
return new BraveSpan(this.delegate);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -98,7 +103,7 @@ public class BraveSpan implements Span {
|
||||
}
|
||||
|
||||
public static brave.Span toBrave(Span span) {
|
||||
return ((BraveSpan) span).delegate;
|
||||
return ((BraveSpan) AssertingSpan.unwrap(span)).delegate;
|
||||
}
|
||||
|
||||
public static Span fromBrave(brave.Span span) {
|
||||
|
||||
@@ -1,144 +1,145 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.brave.bridge;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import brave.Tracer;
|
||||
import brave.propagation.TraceContextOrSamplingFlags;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceContext;
|
||||
|
||||
/**
|
||||
* Brave implementation of a {@link Span.Builder}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.0
|
||||
*/
|
||||
class BraveSpanBuilder implements Span.Builder {
|
||||
|
||||
private static final Log log = LogFactory.getLog(BraveSpanBuilder.class);
|
||||
|
||||
brave.Span delegate;
|
||||
|
||||
TraceContextOrSamplingFlags parentContext;
|
||||
|
||||
private final Tracer tracer;
|
||||
|
||||
private long startTimestamp;
|
||||
|
||||
BraveSpanBuilder(Tracer tracer) {
|
||||
this.tracer = tracer;
|
||||
}
|
||||
|
||||
BraveSpanBuilder(Tracer tracer, TraceContextOrSamplingFlags parentContext) {
|
||||
this.tracer = tracer;
|
||||
this.parentContext = parentContext;
|
||||
}
|
||||
|
||||
brave.Span span() {
|
||||
if (this.delegate != null) {
|
||||
return this.delegate;
|
||||
}
|
||||
else if (this.parentContext != null) {
|
||||
this.delegate = this.tracer.nextSpan(this.parentContext);
|
||||
}
|
||||
else {
|
||||
this.delegate = this.tracer.nextSpan();
|
||||
}
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder setParent(TraceContext context) {
|
||||
this.parentContext = TraceContextOrSamplingFlags.create(BraveTraceContext.toBrave(context));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder setNoParent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder name(String name) {
|
||||
span().name(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder event(String value) {
|
||||
span().annotate(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder tag(String key, String value) {
|
||||
span().tag(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder error(Throwable throwable) {
|
||||
span().error(throwable);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder kind(Span.Kind kind) {
|
||||
span().kind(kind != null ? brave.Span.Kind.valueOf(kind.toString()) : null);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder remoteServiceName(String remoteServiceName) {
|
||||
span().remoteServiceName(remoteServiceName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder remoteUrl(String remoteUrl) {
|
||||
try {
|
||||
URI uri = URI.create(remoteUrl);
|
||||
span().remoteIpAndPort(uri.getHost(), uri.getPort());
|
||||
} catch (Exception e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Failed to parse url [" + remoteUrl + "]. Will not set the value", e);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span start() {
|
||||
if (this.startTimestamp > 0) {
|
||||
span().start(this.startTimestamp);
|
||||
}
|
||||
else {
|
||||
span().start();
|
||||
}
|
||||
return BraveSpan.fromBrave(this.delegate);
|
||||
}
|
||||
|
||||
static Span.Builder toBuilder(Tracer tracer, TraceContextOrSamplingFlags context) {
|
||||
return new BraveSpanBuilder(tracer, context);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.brave.bridge;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import brave.Tracer;
|
||||
import brave.propagation.TraceContextOrSamplingFlags;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceContext;
|
||||
|
||||
/**
|
||||
* Brave implementation of a {@link Span.Builder}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.0
|
||||
*/
|
||||
class BraveSpanBuilder implements Span.Builder {
|
||||
|
||||
private static final Log log = LogFactory.getLog(BraveSpanBuilder.class);
|
||||
|
||||
brave.Span delegate;
|
||||
|
||||
TraceContextOrSamplingFlags parentContext;
|
||||
|
||||
private final Tracer tracer;
|
||||
|
||||
private long startTimestamp;
|
||||
|
||||
BraveSpanBuilder(Tracer tracer) {
|
||||
this.tracer = tracer;
|
||||
}
|
||||
|
||||
BraveSpanBuilder(Tracer tracer, TraceContextOrSamplingFlags parentContext) {
|
||||
this.tracer = tracer;
|
||||
this.parentContext = parentContext;
|
||||
}
|
||||
|
||||
brave.Span span() {
|
||||
if (this.delegate != null) {
|
||||
return this.delegate;
|
||||
}
|
||||
else if (this.parentContext != null) {
|
||||
this.delegate = this.tracer.nextSpan(this.parentContext);
|
||||
}
|
||||
else {
|
||||
this.delegate = this.tracer.nextSpan();
|
||||
}
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder setParent(TraceContext context) {
|
||||
this.parentContext = TraceContextOrSamplingFlags.create(BraveTraceContext.toBrave(context));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder setNoParent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder name(String name) {
|
||||
span().name(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder event(String value) {
|
||||
span().annotate(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder tag(String key, String value) {
|
||||
span().tag(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder error(Throwable throwable) {
|
||||
span().error(throwable);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder kind(Span.Kind kind) {
|
||||
span().kind(kind != null ? brave.Span.Kind.valueOf(kind.toString()) : null);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder remoteServiceName(String remoteServiceName) {
|
||||
span().remoteServiceName(remoteServiceName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder remoteUrl(String remoteUrl) {
|
||||
try {
|
||||
URI uri = URI.create(remoteUrl);
|
||||
span().remoteIpAndPort(uri.getHost(), uri.getPort());
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Failed to parse url [" + remoteUrl + "]. Will not set the value", e);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span start() {
|
||||
if (this.startTimestamp > 0) {
|
||||
span().start(this.startTimestamp);
|
||||
}
|
||||
else {
|
||||
span().start();
|
||||
}
|
||||
return BraveSpan.fromBrave(this.delegate);
|
||||
}
|
||||
|
||||
static Span.Builder toBuilder(Tracer tracer, TraceContextOrSamplingFlags context) {
|
||||
return new BraveSpanBuilder(tracer, context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.cloud.sleuth.brave.bridge;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanCustomizer;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpanCustomizer;
|
||||
|
||||
/**
|
||||
* Brave implementation of a {@link SpanCustomizer}.
|
||||
@@ -48,7 +49,7 @@ public class BraveSpanCustomizer implements SpanCustomizer {
|
||||
}
|
||||
|
||||
static brave.SpanCustomizer toBrave(SpanCustomizer spanCustomizer) {
|
||||
return ((BraveSpanCustomizer) spanCustomizer).spanCustomizer;
|
||||
return ((BraveSpanCustomizer) AssertingSpanCustomizer.unwrap(spanCustomizer)).spanCustomizer;
|
||||
}
|
||||
|
||||
static SpanCustomizer fromBrave(brave.SpanCustomizer spanCustomizer) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanCustomizer;
|
||||
import org.springframework.cloud.sleuth.TraceContext;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpan;
|
||||
|
||||
/**
|
||||
* Brave implementation of a {@link Tracer}.
|
||||
@@ -58,7 +59,8 @@ public class BraveTracer implements Tracer {
|
||||
|
||||
@Override
|
||||
public SpanInScope withSpan(Span span) {
|
||||
return new BraveSpanInScope(tracer.withSpanInScope(span == null ? null : ((BraveSpan) span).delegate));
|
||||
return new BraveSpanInScope(
|
||||
tracer.withSpanInScope(span == null ? null : ((BraveSpan) AssertingSpan.unwrap(span)).delegate));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,10 +39,6 @@ abstract class AbstractSleuthMethodInvocationProcessor implements SleuthMethodIn
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AbstractSleuthMethodInvocationProcessor.class);
|
||||
|
||||
private static final String CLASS_KEY = "class";
|
||||
|
||||
private static final String METHOD_KEY = "method";
|
||||
|
||||
BeanFactory beanFactory;
|
||||
|
||||
private NewSpanParser newSpanParser;
|
||||
@@ -81,8 +77,9 @@ abstract class AbstractSleuthMethodInvocationProcessor implements SleuthMethodIn
|
||||
}
|
||||
|
||||
void addTags(MethodInvocation invocation, Span span) {
|
||||
span.tag(CLASS_KEY, invocation.getThis().getClass().getSimpleName());
|
||||
span.tag(METHOD_KEY, invocation.getMethod().getName());
|
||||
SleuthAnnotationSpan.ANNOTATION_NEW_OR_CONTINUE_SPAN.wrap(span)
|
||||
.tag(SleuthAnnotationSpan.Tags.CLASS, invocation.getThis().getClass().getSimpleName())
|
||||
.tag(SleuthAnnotationSpan.Tags.METHOD, invocation.getMethod().getName());
|
||||
}
|
||||
|
||||
void logEvent(Span span, String name) {
|
||||
@@ -92,7 +89,7 @@ abstract class AbstractSleuthMethodInvocationProcessor implements SleuthMethodIn
|
||||
+ "the same class then the aspect will not be properly resolved");
|
||||
return;
|
||||
}
|
||||
span.event(name);
|
||||
SleuthAnnotationSpan.ANNOTATION_NEW_OR_CONTINUE_SPAN.wrap(span).event(name);
|
||||
}
|
||||
|
||||
String log(ContinueSpan continueSpan) {
|
||||
|
||||
@@ -44,7 +44,7 @@ public class NonReactorSleuthMethodInvocationProcessor extends AbstractSleuthMet
|
||||
// close it on completion
|
||||
boolean startNewSpan = newSpan != null || span == null;
|
||||
if (startNewSpan) {
|
||||
span = tracer().nextSpan();
|
||||
span = SleuthAnnotationSpan.ANNOTATION_NEW_OR_CONTINUE_SPAN.wrap(tracer().nextSpan());
|
||||
newSpanParser().parse(invocation, newSpan, span);
|
||||
span.start();
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ public class ReactorSleuthMethodInvocationProcessor extends AbstractSleuthMethod
|
||||
if (this.span == null) {
|
||||
// If we aren't continuing a trace from this flow, use nextSpan so that it
|
||||
// can consider the "current span" (typically, backed by a thread-local)
|
||||
span = tracer.nextSpan();
|
||||
span = SleuthAnnotationSpan.ANNOTATION_NEW_OR_CONTINUE_SPAN.wrap(tracer.nextSpan());
|
||||
this.processor.newSpanParser().parse(this.invocation, this.newSpan, span);
|
||||
span.start();
|
||||
}
|
||||
@@ -177,7 +177,7 @@ public class ReactorSleuthMethodInvocationProcessor extends AbstractSleuthMethod
|
||||
Span span;
|
||||
Tracer tracer = this.processor.tracer();
|
||||
if (this.span == null) {
|
||||
span = tracer.nextSpan();
|
||||
span = SleuthAnnotationSpan.ANNOTATION_NEW_OR_CONTINUE_SPAN.wrap(tracer.nextSpan());
|
||||
this.processor.newSpanParser().parse(this.invocation, this.newSpan, span);
|
||||
span.start();
|
||||
}
|
||||
|
||||
@@ -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.instrument.annotation;
|
||||
|
||||
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 "%s.afterFailure";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.instrument.async;
|
||||
|
||||
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 Tags.values();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Span created whenever a Runnable needs to be instrumented.
|
||||
*/
|
||||
ASYNC_RUNNABLE_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "%s";
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Span created whenever a Callable needs to be instrumented.
|
||||
*/
|
||||
ASYNC_CALLABLE_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "%s";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
enum Tags 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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpan;
|
||||
import org.springframework.cloud.sleuth.internal.SpanNameUtil;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
@@ -40,10 +41,6 @@ import org.springframework.util.ReflectionUtils;
|
||||
@Aspect
|
||||
public class TraceAsyncAspect {
|
||||
|
||||
private static final String CLASS_KEY = "class";
|
||||
|
||||
private static final String METHOD_KEY = "method";
|
||||
|
||||
private final Tracer tracer;
|
||||
|
||||
private final SpanNamer spanNamer;
|
||||
@@ -60,15 +57,14 @@ public class TraceAsyncAspect {
|
||||
if (span == null) {
|
||||
span = this.tracer.nextSpan();
|
||||
}
|
||||
span = span.name(spanName);
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) {
|
||||
// TODO: Make this less generic
|
||||
span.tag(CLASS_KEY, pjp.getTarget().getClass().getSimpleName());
|
||||
span.tag(METHOD_KEY, pjp.getSignature().getName());
|
||||
AssertingSpan assertingSpan = SleuthAsyncSpan.ASYNC_ANNOTATION_SPAN.wrap(span).name(spanName);
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpan(assertingSpan.start())) {
|
||||
assertingSpan.tag(SleuthAsyncSpan.Tags.CLASS, pjp.getTarget().getClass().getSimpleName())
|
||||
.tag(SleuthAsyncSpan.Tags.METHOD, pjp.getSignature().getName());
|
||||
return pjp.proceed();
|
||||
}
|
||||
finally {
|
||||
span.end();
|
||||
assertingSpan.end();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,8 @@ public class TraceCallable<V> implements Callable<V> {
|
||||
|
||||
@Override
|
||||
public V call() throws Exception {
|
||||
Span childSpan = this.tracer.nextSpan(this.parent).name(this.spanName);
|
||||
Span childSpan = SleuthAsyncSpan.ASYNC_CALLABLE_SPAN.wrap(this.tracer.nextSpan(this.parent))
|
||||
.name(this.spanName);
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpan(childSpan.start())) {
|
||||
return this.delegate.call();
|
||||
}
|
||||
|
||||
@@ -58,7 +58,8 @@ public class TraceRunnable implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Span childSpan = this.tracer.nextSpan(this.parent).name(this.spanName);
|
||||
Span childSpan = SleuthAsyncSpan.ASYNC_RUNNABLE_SPAN.wrap(this.tracer.nextSpan(this.parent))
|
||||
.name(this.spanName);
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpan(childSpan.start())) {
|
||||
this.delegate.run();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.batch;
|
||||
|
||||
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
|
||||
import org.springframework.cloud.sleuth.docs.TagKey;
|
||||
|
||||
enum SleuthBatchSpan implements DocumentedSpan {
|
||||
|
||||
/**
|
||||
* Span created around a Job execution.
|
||||
*/
|
||||
BATCH_JOB_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "%s";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return JobTags.values();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Span created around a Job execution.
|
||||
*/
|
||||
BATCH_STEP_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "%s";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return StepTags.values();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
enum JobTags implements TagKey {
|
||||
|
||||
/**
|
||||
* Name of the Spring Batch job.
|
||||
*/
|
||||
JOB_NAME {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "batch.job.name";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* ID of the Spring Batch job instance.
|
||||
*/
|
||||
JOB_INSTANCE_ID {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "batch.job.instanceId";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* ID of the Spring Batch execution.
|
||||
*/
|
||||
JOB_EXECUTION_ID {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "batch.job.executionId";
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
enum StepTags implements TagKey {
|
||||
|
||||
/**
|
||||
* Name of the Spring Batch job.
|
||||
*/
|
||||
STEP_NAME {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "batch.step.name";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* ID of the Spring Batch execution.
|
||||
*/
|
||||
STEP_EXECUTION_ID {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "batch.step.executionId";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Type of the Spring Batch job.
|
||||
*/
|
||||
STEP_TYPE {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "batch.step.type";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* ID of the Spring Batch execution.
|
||||
*/
|
||||
JOB_EXECUTION_ID {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "batch.job.executionId";
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,12 +26,13 @@ import org.springframework.batch.core.JobExecutionListener;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanAndScope;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpan;
|
||||
|
||||
class TraceJobExecutionListener implements JobExecutionListener {
|
||||
|
||||
private final Tracer tracer;
|
||||
|
||||
private static Map<JobExecution, SpanAndScope> SPANS = new ConcurrentHashMap<>();
|
||||
private static final Map<JobExecution, SpanAndScope> SPANS = new ConcurrentHashMap<>();
|
||||
|
||||
TraceJobExecutionListener(Tracer tracer) {
|
||||
this.tracer = tracer;
|
||||
@@ -39,7 +40,8 @@ class TraceJobExecutionListener implements JobExecutionListener {
|
||||
|
||||
@Override
|
||||
public void beforeJob(JobExecution jobExecution) {
|
||||
Span span = this.tracer.nextSpan().name(jobExecution.getJobInstance().getJobName());
|
||||
Span span = SleuthBatchSpan.BATCH_JOB_SPAN.wrap(this.tracer.nextSpan())
|
||||
.name(jobExecution.getJobInstance().getJobName());
|
||||
Tracer.SpanInScope spanInScope = this.tracer.withSpan(span.start());
|
||||
SPANS.put(jobExecution, new SpanAndScope(span, spanInScope));
|
||||
}
|
||||
@@ -48,21 +50,24 @@ class TraceJobExecutionListener implements JobExecutionListener {
|
||||
public void afterJob(JobExecution jobExecution) {
|
||||
SpanAndScope spanAndScope = SPANS.remove(jobExecution);
|
||||
List<Throwable> throwables = jobExecution.getFailureExceptions();
|
||||
Span span = spanAndScope.getSpan();
|
||||
span.tag("batch.job.name", jobExecution.getJobInstance().getJobName());
|
||||
span.tag("batch.job.instanceId", String.valueOf(jobExecution.getJobInstance().getInstanceId()));
|
||||
span.tag("batch.job.executionId", String.valueOf(jobExecution.getId()));
|
||||
Tracer.SpanInScope scope = spanAndScope.getScope();
|
||||
if (!throwables.isEmpty()) {
|
||||
span.error(mergedThrowables(throwables));
|
||||
}
|
||||
span.end();
|
||||
scope.close();
|
||||
}
|
||||
|
||||
private IllegalStateException mergedThrowables(List<Throwable> throwables) {
|
||||
return new IllegalStateException(
|
||||
throwables.stream().map(Throwable::toString).collect(Collectors.joining("\n")));
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
AssertingSpan span = SleuthBatchSpan.BATCH_JOB_SPAN.wrap(spanAndScope.getSpan())
|
||||
.tag(SleuthBatchSpan.JobTags.JOB_NAME, jobExecution.getJobInstance().getJobName())
|
||||
.tag(SleuthBatchSpan.JobTags.JOB_INSTANCE_ID,
|
||||
String.valueOf(jobExecution.getJobInstance().getInstanceId()))
|
||||
.tag(SleuthBatchSpan.JobTags.JOB_EXECUTION_ID, String.valueOf(jobExecution.getId()));
|
||||
// formatter:on
|
||||
Tracer.SpanInScope scope = spanAndScope.getScope();
|
||||
if (!throwables.isEmpty()) {
|
||||
span.error(mergedThrowables(throwables));
|
||||
}
|
||||
span.end();
|
||||
scope.close();
|
||||
}
|
||||
|
||||
private IllegalStateException mergedThrowables(List<Throwable> throwables) {
|
||||
return new IllegalStateException(
|
||||
throwables.stream().map(Throwable::toString).collect(Collectors.joining("\n")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,12 +28,13 @@ import org.springframework.batch.core.StepExecutionListener;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanAndScope;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpan;
|
||||
|
||||
class TraceStepExecutionListener implements StepExecutionListener {
|
||||
|
||||
private final Tracer tracer;
|
||||
|
||||
private static Map<StepExecution, SpanAndScope> SPANS = new ConcurrentHashMap<>();
|
||||
private static final Map<StepExecution, SpanAndScope> SPANS = new ConcurrentHashMap<>();
|
||||
|
||||
TraceStepExecutionListener(Tracer tracer) {
|
||||
this.tracer = tracer;
|
||||
@@ -41,7 +42,7 @@ class TraceStepExecutionListener implements StepExecutionListener {
|
||||
|
||||
@Override
|
||||
public void beforeStep(StepExecution stepExecution) {
|
||||
Span span = this.tracer.nextSpan().name(stepExecution.getStepName());
|
||||
Span span = SleuthBatchSpan.BATCH_STEP_SPAN.wrap(this.tracer.nextSpan()).name(stepExecution.getStepName());
|
||||
Tracer.SpanInScope spanInScope = this.tracer.withSpan(span.start());
|
||||
SPANS.put(stepExecution, new SpanAndScope(span, spanInScope));
|
||||
}
|
||||
@@ -50,11 +51,13 @@ class TraceStepExecutionListener implements StepExecutionListener {
|
||||
public ExitStatus afterStep(StepExecution stepExecution) {
|
||||
SpanAndScope spanAndScope = SPANS.remove(stepExecution);
|
||||
List<Throwable> throwables = stepExecution.getFailureExceptions();
|
||||
Span span = spanAndScope.getSpan();
|
||||
span.tag("batch.step.name", stepExecution.getStepName());
|
||||
span.tag("batch.job.executionId", String.valueOf(stepExecution.getJobExecutionId()));
|
||||
span.tag("batch.step.executionId", String.valueOf(stepExecution.getId()));
|
||||
span.tag("batch.step.type", stepExecution.getExecutionContext().getString(Step.STEP_TYPE_KEY));
|
||||
// @formatter:off
|
||||
AssertingSpan span = SleuthBatchSpan.BATCH_STEP_SPAN.wrap(spanAndScope.getSpan())
|
||||
.tag(SleuthBatchSpan.StepTags.STEP_NAME, stepExecution.getStepName())
|
||||
.tag(SleuthBatchSpan.StepTags.JOB_EXECUTION_ID, String.valueOf(stepExecution.getJobExecutionId()))
|
||||
.tag(SleuthBatchSpan.StepTags.STEP_EXECUTION_ID, String.valueOf(stepExecution.getId()))
|
||||
.tag(SleuthBatchSpan.StepTags.STEP_TYPE, stepExecution.getExecutionContext().getString(Step.STEP_TYPE_KEY));
|
||||
// @formatter:on
|
||||
Tracer.SpanInScope scope = spanAndScope.getScope();
|
||||
if (!throwables.isEmpty()) {
|
||||
span.error(mergedThrowables(throwables));
|
||||
|
||||
@@ -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.instrument.circuitbreaker;
|
||||
|
||||
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
|
||||
|
||||
enum SleuthCircuitBreakerSpan implements DocumentedSpan {
|
||||
|
||||
/**
|
||||
* Span created when we wrap a Supplier passed to the CircuitBreaker.
|
||||
*/
|
||||
CIRCUIT_BREAKER_SUPPLIER_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "%s";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Span created when we wrap a Function passed to the CircuitBreaker. as fallback.
|
||||
*/
|
||||
CIRCUIT_BREAKER_FUNCTION_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "%s";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,8 +19,8 @@ package org.springframework.cloud.sleuth.instrument.circuitbreaker;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpan;
|
||||
|
||||
/**
|
||||
* Trace representation of a {@link Function}.
|
||||
@@ -34,19 +34,20 @@ class TraceFunction<T> implements Function<Throwable, T> {
|
||||
|
||||
private final Function<Throwable, T> delegate;
|
||||
|
||||
private final AtomicReference<Span> span;
|
||||
private final AtomicReference<AssertingSpan> span;
|
||||
|
||||
TraceFunction(Tracer tracer, Function<Throwable, T> delegate) {
|
||||
this.tracer = tracer;
|
||||
this.delegate = delegate;
|
||||
this.span = new AtomicReference<>(this.tracer.nextSpan());
|
||||
this.span = new AtomicReference<>(
|
||||
SleuthCircuitBreakerSpan.CIRCUIT_BREAKER_FUNCTION_SPAN.wrap(this.tracer.nextSpan()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public T apply(Throwable throwable) {
|
||||
// TODO: This name needs to be better
|
||||
String name = this.delegate.getClass().getSimpleName();
|
||||
Span span = this.span.get().name(name);
|
||||
AssertingSpan span = this.span.get().name(name);
|
||||
Throwable tr = null;
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) {
|
||||
return this.delegate.apply(throwable);
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.config;
|
||||
|
||||
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
|
||||
import org.springframework.cloud.sleuth.docs.TagKey;
|
||||
|
||||
enum SleuthConfigSpan implements DocumentedSpan {
|
||||
|
||||
/**
|
||||
* Span created around an EnvironmentRepository.
|
||||
*/
|
||||
CONFIG_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "find";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return Tags.values();
|
||||
}
|
||||
};
|
||||
|
||||
enum Tags implements TagKey {
|
||||
|
||||
/**
|
||||
* Implementation of the EnvironmentRepository.
|
||||
*/
|
||||
ENVIRONMENT_CLASS {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "config.environment.class";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Method executed on the EnvironmentRepository.
|
||||
*/
|
||||
ENVIRONMENT_METHOD {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "config.environment.method";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,8 +20,8 @@ import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpan;
|
||||
|
||||
/**
|
||||
* Aspect wrapping resolution of properties.
|
||||
@@ -40,9 +40,12 @@ public class TraceEnvironmentRepositoryAspect {
|
||||
|
||||
@Around("execution (* org.springframework.cloud.config.server.environment.EnvironmentRepository.*(..))")
|
||||
public Object traceFindEnvironment(final ProceedingJoinPoint pjp) throws Throwable {
|
||||
Span findOneSpan = this.tracer.nextSpan().name("find");
|
||||
findOneSpan.tag("config.environment.class", pjp.getTarget().getClass().getName());
|
||||
findOneSpan.tag("config.environment.method", pjp.getSignature().getName());
|
||||
// @formatter:off
|
||||
AssertingSpan findOneSpan = SleuthConfigSpan.CONFIG_SPAN.wrap(this.tracer.nextSpan())
|
||||
.name(SleuthConfigSpan.CONFIG_SPAN.getName())
|
||||
.tag(SleuthConfigSpan.Tags.ENVIRONMENT_CLASS, pjp.getTarget().getClass().getName())
|
||||
.tag(SleuthConfigSpan.Tags.ENVIRONMENT_METHOD, pjp.getSignature().getName());
|
||||
// @formatter:on
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpan(findOneSpan.start())) {
|
||||
return pjp.proceed();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.deployer;
|
||||
|
||||
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
|
||||
import org.springframework.cloud.sleuth.docs.EventValue;
|
||||
import org.springframework.cloud.sleuth.docs.TagKey;
|
||||
|
||||
enum SleuthDeployerSpan implements DocumentedSpan {
|
||||
|
||||
/**
|
||||
* Span created upon deploying of an application.
|
||||
*/
|
||||
DEPLOYER_DEPLOY_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "deploy";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return Tags.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventValue[] getEvents() {
|
||||
return Events.values();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Span created upon undeploying of an application.
|
||||
*/
|
||||
DEPLOYER_UNDEPLOY_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "undeploy";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return Tags.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventValue[] getEvents() {
|
||||
return Events.values();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Span created upon asking for a status of a deployed application.
|
||||
*/
|
||||
DEPLOYER_STATUS_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "status";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return Tags.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventValue[] getEvents() {
|
||||
return Events.values();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Span created upon asking for statuses of deployed applications.
|
||||
*/
|
||||
DEPLOYER_STATUSES_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "statuses";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return Tags.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventValue[] getEvents() {
|
||||
return Events.values();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Span created upon asking for logs of deployed applications.
|
||||
*/
|
||||
DEPLOYER_GET_LOG_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "getLog";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return Tags.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventValue[] getEvents() {
|
||||
return Events.values();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Span created upon asking for logs of deployed applications.
|
||||
*/
|
||||
DEPLOYER_SCALE_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "scale";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return TagKey.merge(Tags.values(), ScaleTags.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventValue[] getEvents() {
|
||||
return Events.values();
|
||||
}
|
||||
};
|
||||
|
||||
enum Tags implements TagKey {
|
||||
|
||||
/**
|
||||
* Name of the platform to which apps are being deployed.
|
||||
*/
|
||||
PLATFORM_NAME {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "deployer.platform.name";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* ID of the deployed application.
|
||||
*/
|
||||
APP_ID {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "deployer.app.id";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Name of the deployed application.
|
||||
*/
|
||||
APP_NAME {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "deployer.app.name";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Group of the deployed application.
|
||||
*/
|
||||
APP_GROUP {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "deployer.app.group";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* CloudFoundry API URL.
|
||||
*/
|
||||
CF_URL {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "deployer.platform.cf.url";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* CloudFoundry org.
|
||||
*/
|
||||
CF_ORG {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "deployer.platform.cf.org";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* CloudFoundry space.
|
||||
*/
|
||||
CF_SPACE {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "deployer.platform.cf.space";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Kubernetes API URL.
|
||||
*/
|
||||
K8S_URL {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "deployer.platform.k8s.url";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Kubernetes namespace.
|
||||
*/
|
||||
K8S_NAMESPACE {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "deployer.platform.k8s.namespace";
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
enum ScaleTags implements TagKey {
|
||||
|
||||
/**
|
||||
* Scale command deployment id.
|
||||
*/
|
||||
DEPLOYER_SCALE_DEPLOYMENT_ID {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "deployer.scale.deploymentId";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Scale count.
|
||||
*/
|
||||
DEPLOYER_SCALE_COUNT {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "deployer.scale.count";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum Events implements EventValue {
|
||||
|
||||
/**
|
||||
* When deployer started deploying the application.
|
||||
*/
|
||||
DEPLOYER_START {
|
||||
@Override
|
||||
public String getValue() {
|
||||
return "deployer.start";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* When deployer changes the state of the deployed application.
|
||||
*/
|
||||
DEPLOYER_STATUS_CHANGE {
|
||||
@Override
|
||||
public String getValue() {
|
||||
return "%s";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,155 +1,160 @@
|
||||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.deployer;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.deployer.spi.app.AppDeployer;
|
||||
import org.springframework.cloud.deployer.spi.app.AppScaleRequest;
|
||||
import org.springframework.cloud.deployer.spi.app.AppStatus;
|
||||
import org.springframework.cloud.deployer.spi.app.DeploymentState;
|
||||
import org.springframework.cloud.deployer.spi.core.AppDeploymentRequest;
|
||||
import org.springframework.cloud.deployer.spi.core.RuntimeEnvironmentInfo;
|
||||
import org.springframework.cloud.sleuth.CurrentTraceContext;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.reactor.ReactorSleuth;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Trace representation of an {@link AppDeployer}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public class TraceAppDeployer implements AppDeployer {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TraceAppDeployer.class);
|
||||
|
||||
private final AppDeployer delegate;
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
private Tracer tracer;
|
||||
|
||||
private CurrentTraceContext currentTraceContext;
|
||||
|
||||
private Long pollDelay;
|
||||
|
||||
public TraceAppDeployer(AppDeployer delegate, BeanFactory beanFactory, Environment environment) {
|
||||
this.delegate = delegate;
|
||||
this.beanFactory = beanFactory;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deploy(AppDeploymentRequest request) {
|
||||
Span.Builder spanBuilder = clientSpan("deploy", request);
|
||||
Span span = spanBuilder.start();
|
||||
try (Tracer.SpanInScope spanInScope = tracer().withSpan(span)) {
|
||||
span.event("deployer.start");
|
||||
String id = this.delegate.deploy(request);
|
||||
span.tag("deployer.app.id", id);
|
||||
registerListener(span, id);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
private Span.Builder clientSpan(String name) {
|
||||
return clientSpan(name, null, null);
|
||||
}
|
||||
|
||||
private Span.Builder clientSpan(String name, @Nullable AppDeploymentRequest request) {
|
||||
return clientSpan(name, null, request);
|
||||
}
|
||||
|
||||
private Span.Builder clientSpanKind(String name, Span.Builder spanBuilder) {
|
||||
return spanBuilder.kind(Span.Kind.CLIENT).name(name).remoteServiceName(remoteServiceName());
|
||||
}
|
||||
|
||||
private Span.Builder clientSpan(String name, Span parentSpan) {
|
||||
return clientSpan(name, parentSpan, null);
|
||||
}
|
||||
|
||||
private Span.Builder clientSpan(String name, @Nullable Span parentSpan, @Nullable AppDeploymentRequest request) {
|
||||
Span.Builder spanBuilder = tracer().spanBuilder();
|
||||
Span currentSpan = parentSpan != null ? parentSpan : tracer().currentSpan();
|
||||
if (currentSpan != null) {
|
||||
spanBuilder.setParent(currentSpan.context());
|
||||
}
|
||||
Map<String, String> platformSpecificInfo = environmentInfo().getPlatformSpecificInfo();
|
||||
if (request != null) {
|
||||
String platformName = request.getDeploymentProperties().get("spring.cloud.deployer.platformName");
|
||||
if (StringUtils.hasText(platformName)) {
|
||||
spanBuilder.tag("deployer.platform.name", platformName);
|
||||
}
|
||||
String appName = request.getDeploymentProperties().get("spring.cloud.deployer.appName");
|
||||
if (StringUtils.hasText(appName)) {
|
||||
spanBuilder.tag("deployer.app.name", appName);
|
||||
}
|
||||
String group = request.getDeploymentProperties().get("spring.cloud.deployer.group");
|
||||
if (StringUtils.hasText(group)) {
|
||||
spanBuilder.tag("deployer.app.name", group);
|
||||
}
|
||||
}
|
||||
addCfTags(spanBuilder, platformSpecificInfo);
|
||||
addK8sTags(spanBuilder, platformSpecificInfo);
|
||||
return clientSpanKind(name, spanBuilder);
|
||||
}
|
||||
|
||||
private void addCfTags(Span.Builder spanBuilder, Map<String, String> platformSpecificInfo) {
|
||||
if (platformSpecificInfo.containsKey("API Endpoint")) {
|
||||
spanBuilder.tag("deployer.platform.cf.url", platformSpecificInfo.get("API Endpoint"));
|
||||
}
|
||||
if (platformSpecificInfo.containsKey("Organization")) {
|
||||
spanBuilder.tag("deployer.platform.cf.org", platformSpecificInfo.get("Organization"));
|
||||
}
|
||||
if (platformSpecificInfo.containsKey("Space")) {
|
||||
spanBuilder.tag("deployer.platform.cf.space", platformSpecificInfo.get("Space"));
|
||||
}
|
||||
}
|
||||
|
||||
private void addK8sTags(Span.Builder spanBuilder, Map<String, String> platformSpecificInfo) {
|
||||
if (platformSpecificInfo.containsKey("master-url")) {
|
||||
spanBuilder.tag("deployer.platform.k8s.url", platformSpecificInfo.get("master-url"));
|
||||
}
|
||||
if (platformSpecificInfo.containsKey("namespace")) {
|
||||
spanBuilder.tag("deployer.platform.k8s.namespace", platformSpecificInfo.get("namespace"));
|
||||
}
|
||||
}
|
||||
|
||||
private String remoteServiceName() {
|
||||
return environmentInfo().getPlatformType();
|
||||
}
|
||||
|
||||
private void registerListener(Span span, String id) {
|
||||
PreviousAndCurrentStatus previousAndCurrentStatus = new PreviousAndCurrentStatus(span);
|
||||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.deployer;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.deployer.spi.app.AppDeployer;
|
||||
import org.springframework.cloud.deployer.spi.app.AppScaleRequest;
|
||||
import org.springframework.cloud.deployer.spi.app.AppStatus;
|
||||
import org.springframework.cloud.deployer.spi.app.DeploymentState;
|
||||
import org.springframework.cloud.deployer.spi.core.AppDeploymentRequest;
|
||||
import org.springframework.cloud.deployer.spi.core.RuntimeEnvironmentInfo;
|
||||
import org.springframework.cloud.sleuth.CurrentTraceContext;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpan;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpanBuilder;
|
||||
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
|
||||
import org.springframework.cloud.sleuth.instrument.reactor.ReactorSleuth;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Trace representation of an {@link AppDeployer}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public class TraceAppDeployer implements AppDeployer {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TraceAppDeployer.class);
|
||||
|
||||
private final AppDeployer delegate;
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
private Tracer tracer;
|
||||
|
||||
private CurrentTraceContext currentTraceContext;
|
||||
|
||||
private Long pollDelay;
|
||||
|
||||
public TraceAppDeployer(AppDeployer delegate, BeanFactory beanFactory, Environment environment) {
|
||||
this.delegate = delegate;
|
||||
this.beanFactory = beanFactory;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deploy(AppDeploymentRequest request) {
|
||||
AssertingSpanBuilder spanBuilder = clientSpan(SleuthDeployerSpan.DEPLOYER_DEPLOY_SPAN, request);
|
||||
AssertingSpan span = spanBuilder.start();
|
||||
try (Tracer.SpanInScope spanInScope = tracer().withSpan(span)) {
|
||||
span.event(SleuthDeployerSpan.Events.DEPLOYER_START);
|
||||
String id = this.delegate.deploy(request);
|
||||
span.tag(SleuthDeployerSpan.Tags.APP_ID, id);
|
||||
registerListener(span, id);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
private AssertingSpanBuilder clientSpan(DocumentedSpan documentedSpan) {
|
||||
return clientSpan(documentedSpan, null, null);
|
||||
}
|
||||
|
||||
private AssertingSpanBuilder clientSpan(DocumentedSpan documentedSpan, @Nullable AppDeploymentRequest request) {
|
||||
return clientSpan(documentedSpan, null, request);
|
||||
}
|
||||
|
||||
private AssertingSpanBuilder clientSpanKind(String name, AssertingSpanBuilder spanBuilder) {
|
||||
return spanBuilder.kind(Span.Kind.CLIENT).name(name).remoteServiceName(remoteServiceName());
|
||||
}
|
||||
|
||||
private AssertingSpanBuilder clientSpan(DocumentedSpan documentedSpan, Span parentSpan) {
|
||||
return clientSpan(documentedSpan, parentSpan, null);
|
||||
}
|
||||
|
||||
private AssertingSpanBuilder clientSpan(DocumentedSpan documentedSpan, @Nullable Span parentSpan,
|
||||
@Nullable AppDeploymentRequest request) {
|
||||
String name = documentedSpan.getName();
|
||||
AssertingSpanBuilder spanBuilder = AssertingSpanBuilder.of(documentedSpan, tracer().spanBuilder());
|
||||
Span currentSpan = parentSpan != null ? parentSpan : tracer().currentSpan();
|
||||
if (currentSpan != null) {
|
||||
spanBuilder.setParent(currentSpan.context());
|
||||
}
|
||||
Map<String, String> platformSpecificInfo = environmentInfo().getPlatformSpecificInfo();
|
||||
if (request != null) {
|
||||
String platformName = request.getDeploymentProperties().get("spring.cloud.deployer.platformName");
|
||||
if (StringUtils.hasText(platformName)) {
|
||||
spanBuilder.tag(SleuthDeployerSpan.Tags.PLATFORM_NAME, platformName);
|
||||
}
|
||||
String appName = request.getDeploymentProperties().get("spring.cloud.deployer.appName");
|
||||
if (StringUtils.hasText(appName)) {
|
||||
spanBuilder.tag(SleuthDeployerSpan.Tags.APP_NAME, appName);
|
||||
}
|
||||
String group = request.getDeploymentProperties().get("spring.cloud.deployer.group");
|
||||
if (StringUtils.hasText(group)) {
|
||||
spanBuilder.tag(SleuthDeployerSpan.Tags.APP_GROUP, group);
|
||||
}
|
||||
}
|
||||
addCfTags(spanBuilder, platformSpecificInfo);
|
||||
addK8sTags(spanBuilder, platformSpecificInfo);
|
||||
return clientSpanKind(name, spanBuilder);
|
||||
}
|
||||
|
||||
private void addCfTags(AssertingSpanBuilder spanBuilder, Map<String, String> platformSpecificInfo) {
|
||||
if (platformSpecificInfo.containsKey("API Endpoint")) {
|
||||
spanBuilder.tag(SleuthDeployerSpan.Tags.CF_URL, platformSpecificInfo.get("API Endpoint"));
|
||||
}
|
||||
if (platformSpecificInfo.containsKey("Organization")) {
|
||||
spanBuilder.tag(SleuthDeployerSpan.Tags.CF_ORG, platformSpecificInfo.get("Organization"));
|
||||
}
|
||||
if (platformSpecificInfo.containsKey("Space")) {
|
||||
spanBuilder.tag(SleuthDeployerSpan.Tags.CF_SPACE, platformSpecificInfo.get("Space"));
|
||||
}
|
||||
}
|
||||
|
||||
private void addK8sTags(AssertingSpanBuilder spanBuilder, Map<String, String> platformSpecificInfo) {
|
||||
if (platformSpecificInfo.containsKey("master-url")) {
|
||||
spanBuilder.tag(SleuthDeployerSpan.Tags.K8S_URL, platformSpecificInfo.get("master-url"));
|
||||
}
|
||||
if (platformSpecificInfo.containsKey("namespace")) {
|
||||
spanBuilder.tag(SleuthDeployerSpan.Tags.K8S_NAMESPACE, platformSpecificInfo.get("namespace"));
|
||||
}
|
||||
}
|
||||
|
||||
private String remoteServiceName() {
|
||||
return environmentInfo().getPlatformType();
|
||||
}
|
||||
|
||||
private void registerListener(Span span, String id) {
|
||||
PreviousAndCurrentStatus previousAndCurrentStatus = new PreviousAndCurrentStatus(span);
|
||||
// @formatter:off
|
||||
this.delegate.statusReactive(id)
|
||||
.map(previousAndCurrentStatus::updateCurrent)
|
||||
@@ -160,173 +165,174 @@ public class TraceAppDeployer implements AppDeployer {
|
||||
.doOnError(span::error)
|
||||
// we will close the span in the reactive part
|
||||
.doFinally(signalType -> span.end()).subscribe();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undeploy(String id) {
|
||||
Span.Builder spanBuilder = clientSpan("undeploy");
|
||||
Span span = spanBuilder.start();
|
||||
span.tag("deployer.app.id", id);
|
||||
try (Tracer.SpanInScope spanInScope = tracer().withSpan(span)) {
|
||||
span.event("deployer.start");
|
||||
this.delegate.undeploy(id);
|
||||
registerListener(span, id);
|
||||
}
|
||||
finally {
|
||||
span.end();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppStatus status(String id) {
|
||||
Span.Builder spanBuilder = clientSpan("status");
|
||||
Span span = spanBuilder.start();
|
||||
span.tag("deployer.app.id", id);
|
||||
try (Tracer.SpanInScope spanInScope = tracer().withSpan(span.start())) {
|
||||
return this.delegate.status(id);
|
||||
}
|
||||
finally {
|
||||
span.end();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<AppStatus> statusReactive(String id) {
|
||||
return ReactorSleuth.tracedMono(tracer(), currentTraceContext(), "status",
|
||||
() -> this.delegate.statusReactive(id), (o, span) -> span.tag("deployer.app.id", id),
|
||||
span -> clientSpan("status", span).start());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<AppStatus> statusesReactive(String... ids) {
|
||||
return ReactorSleuth.tracedFlux(tracer(), currentTraceContext(), "statuses",
|
||||
() -> this.delegate.statusesReactive(ids),
|
||||
(o, span) -> span.tag("deployer.app.ids", Arrays.toString(ids)),
|
||||
span -> clientSpan("statuses", span).start());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuntimeEnvironmentInfo environmentInfo() {
|
||||
return this.delegate.environmentInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLog(String id) {
|
||||
Span.Builder spanBuilder = clientSpan("getLog");
|
||||
Span span = spanBuilder.start();
|
||||
span.tag("deployer.app.id", id);
|
||||
try (Tracer.SpanInScope spanInScope = tracer().withSpan(span)) {
|
||||
return this.delegate.getLog(id);
|
||||
}
|
||||
finally {
|
||||
span.end();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scale(AppScaleRequest appScaleRequest) {
|
||||
Span.Builder spanBuilder = clientSpan("scale");
|
||||
Span span = spanBuilder.start();
|
||||
span.tag("deployer.scale.deploymentId", appScaleRequest.getDeploymentId());
|
||||
span.tag("deployer.scale.count", String.valueOf(appScaleRequest.getCount()));
|
||||
try (Tracer.SpanInScope spanInScope = tracer().withSpan(span.start())) {
|
||||
this.delegate.scale(appScaleRequest);
|
||||
}
|
||||
finally {
|
||||
span.end();
|
||||
}
|
||||
}
|
||||
|
||||
private Tracer tracer() {
|
||||
if (this.tracer == null) {
|
||||
this.tracer = this.beanFactory.getBean(Tracer.class);
|
||||
}
|
||||
return this.tracer;
|
||||
}
|
||||
|
||||
private CurrentTraceContext currentTraceContext() {
|
||||
if (this.currentTraceContext == null) {
|
||||
this.currentTraceContext = this.beanFactory.getBean(CurrentTraceContext.class);
|
||||
}
|
||||
return this.currentTraceContext;
|
||||
}
|
||||
|
||||
private long pollDelay() {
|
||||
if (this.pollDelay == null) {
|
||||
this.pollDelay = this.environment.getProperty("spring.sleuth.deployer.status-poll-delay", Long.class, 500L);
|
||||
}
|
||||
return this.pollDelay;
|
||||
}
|
||||
|
||||
private static final class PreviousAndCurrentStatus {
|
||||
|
||||
private final Span span;
|
||||
|
||||
private AppStatus current;
|
||||
|
||||
private AppStatus previous;
|
||||
|
||||
private PreviousAndCurrentStatus(Span span) {
|
||||
this.span = span;
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Current span is [" + span + "]");
|
||||
}
|
||||
}
|
||||
|
||||
private PreviousAndCurrentStatus updateCurrent(AppStatus current) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("State before change: current [" + this.current + "], previous [" + this.previous + "]");
|
||||
}
|
||||
this.previous = this.current;
|
||||
this.current = current;
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("State after change: current [" + this.current + "], previous [" + this.previous + "]");
|
||||
}
|
||||
if (statusChanged()) {
|
||||
annotateSpan();
|
||||
}
|
||||
else if (log.isTraceEnabled()) {
|
||||
log.trace("State has not changed, will not annotate the span");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void annotateSpan() {
|
||||
String name = this.current.getState().name();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Will annotate its state with [" + name + "]");
|
||||
}
|
||||
this.span.event(name);
|
||||
}
|
||||
|
||||
private boolean statusChanged() {
|
||||
if (this.previous == null && this.current != null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Previous is null, current is not null");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (this.current == null) {
|
||||
throw new IllegalStateException("Current state can't be null");
|
||||
}
|
||||
DeploymentState currentState = this.current.getState();
|
||||
DeploymentState previousState = this.previous.getState();
|
||||
return currentState != previousState;
|
||||
}
|
||||
|
||||
private boolean isFinished() {
|
||||
boolean finished = this.current.getState() == DeploymentState.deployed
|
||||
|| this.current.getState() == DeploymentState.undeployed
|
||||
|| this.current.getState() == DeploymentState.failed
|
||||
|| this.current.getState() == DeploymentState.error
|
||||
|| this.current.getState() == DeploymentState.unknown;
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Status is finished [" + finished + "]");
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undeploy(String id) {
|
||||
AssertingSpanBuilder spanBuilder = clientSpan(SleuthDeployerSpan.DEPLOYER_UNDEPLOY_SPAN);
|
||||
AssertingSpan span = spanBuilder.start();
|
||||
span.tag(SleuthDeployerSpan.Tags.APP_ID, id);
|
||||
try (Tracer.SpanInScope spanInScope = tracer().withSpan(span)) {
|
||||
span.event(SleuthDeployerSpan.Events.DEPLOYER_START);
|
||||
this.delegate.undeploy(id);
|
||||
registerListener(span, id);
|
||||
}
|
||||
finally {
|
||||
span.end();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppStatus status(String id) {
|
||||
AssertingSpanBuilder spanBuilder = clientSpan(SleuthDeployerSpan.DEPLOYER_STATUS_SPAN);
|
||||
AssertingSpan span = spanBuilder.start();
|
||||
span.tag(SleuthDeployerSpan.Tags.APP_ID, id);
|
||||
try (Tracer.SpanInScope spanInScope = tracer().withSpan(span.start())) {
|
||||
return this.delegate.status(id);
|
||||
}
|
||||
finally {
|
||||
span.end();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<AppStatus> statusReactive(String id) {
|
||||
return ReactorSleuth.tracedMono(tracer(), currentTraceContext(),
|
||||
SleuthDeployerSpan.DEPLOYER_STATUS_SPAN.getName(), () -> this.delegate.statusReactive(id),
|
||||
(o, span) -> span.tag(SleuthDeployerSpan.Tags.APP_ID.getKey(), id),
|
||||
span -> clientSpan(SleuthDeployerSpan.DEPLOYER_STATUS_SPAN, span).start());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<AppStatus> statusesReactive(String... ids) {
|
||||
return ReactorSleuth.tracedFlux(tracer(), currentTraceContext(),
|
||||
SleuthDeployerSpan.DEPLOYER_STATUSES_SPAN.getName(), () -> this.delegate.statusesReactive(ids),
|
||||
(o, span) -> span.tag(SleuthDeployerSpan.Tags.APP_ID.getKey(), Arrays.toString(ids)),
|
||||
span -> clientSpan(SleuthDeployerSpan.DEPLOYER_STATUSES_SPAN, span).start());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuntimeEnvironmentInfo environmentInfo() {
|
||||
return this.delegate.environmentInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLog(String id) {
|
||||
AssertingSpanBuilder spanBuilder = clientSpan(SleuthDeployerSpan.DEPLOYER_GET_LOG_SPAN);
|
||||
AssertingSpan span = spanBuilder.start();
|
||||
span.tag(SleuthDeployerSpan.Tags.APP_ID, id);
|
||||
try (Tracer.SpanInScope spanInScope = tracer().withSpan(span)) {
|
||||
return this.delegate.getLog(id);
|
||||
}
|
||||
finally {
|
||||
span.end();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scale(AppScaleRequest appScaleRequest) {
|
||||
AssertingSpanBuilder spanBuilder = clientSpan(SleuthDeployerSpan.DEPLOYER_SCALE_SPAN);
|
||||
AssertingSpan span = spanBuilder.start();
|
||||
span.tag(SleuthDeployerSpan.ScaleTags.DEPLOYER_SCALE_DEPLOYMENT_ID, appScaleRequest.getDeploymentId());
|
||||
span.tag(SleuthDeployerSpan.ScaleTags.DEPLOYER_SCALE_COUNT, String.valueOf(appScaleRequest.getCount()));
|
||||
try (Tracer.SpanInScope spanInScope = tracer().withSpan(span.start())) {
|
||||
this.delegate.scale(appScaleRequest);
|
||||
}
|
||||
finally {
|
||||
span.end();
|
||||
}
|
||||
}
|
||||
|
||||
private Tracer tracer() {
|
||||
if (this.tracer == null) {
|
||||
this.tracer = this.beanFactory.getBean(Tracer.class);
|
||||
}
|
||||
return this.tracer;
|
||||
}
|
||||
|
||||
private CurrentTraceContext currentTraceContext() {
|
||||
if (this.currentTraceContext == null) {
|
||||
this.currentTraceContext = this.beanFactory.getBean(CurrentTraceContext.class);
|
||||
}
|
||||
return this.currentTraceContext;
|
||||
}
|
||||
|
||||
private long pollDelay() {
|
||||
if (this.pollDelay == null) {
|
||||
this.pollDelay = this.environment.getProperty("spring.sleuth.deployer.status-poll-delay", Long.class, 500L);
|
||||
}
|
||||
return this.pollDelay;
|
||||
}
|
||||
|
||||
private static final class PreviousAndCurrentStatus {
|
||||
|
||||
private final Span span;
|
||||
|
||||
private AppStatus current;
|
||||
|
||||
private AppStatus previous;
|
||||
|
||||
private PreviousAndCurrentStatus(Span span) {
|
||||
this.span = span;
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Current span is [" + span + "]");
|
||||
}
|
||||
}
|
||||
|
||||
private PreviousAndCurrentStatus updateCurrent(AppStatus current) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("State before change: current [" + this.current + "], previous [" + this.previous + "]");
|
||||
}
|
||||
this.previous = this.current;
|
||||
this.current = current;
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("State after change: current [" + this.current + "], previous [" + this.previous + "]");
|
||||
}
|
||||
if (statusChanged()) {
|
||||
annotateSpan();
|
||||
}
|
||||
else if (log.isTraceEnabled()) {
|
||||
log.trace("State has not changed, will not annotate the span");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void annotateSpan() {
|
||||
String name = this.current.getState().name();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Will annotate its state with [" + name + "]");
|
||||
}
|
||||
this.span.event(String.format(SleuthDeployerSpan.Events.DEPLOYER_STATUS_CHANGE.getValue(), name));
|
||||
}
|
||||
|
||||
private boolean statusChanged() {
|
||||
if (this.previous == null && this.current != null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Previous is null, current is not null");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (this.current == null) {
|
||||
throw new IllegalStateException("Current state can't be null");
|
||||
}
|
||||
DeploymentState currentState = this.current.getState();
|
||||
DeploymentState previousState = this.previous.getState();
|
||||
return currentState != previousState;
|
||||
}
|
||||
|
||||
private boolean isFinished() {
|
||||
boolean finished = this.current.getState() == DeploymentState.deployed
|
||||
|| this.current.getState() == DeploymentState.undeployed
|
||||
|| this.current.getState() == DeploymentState.failed
|
||||
|| this.current.getState() == DeploymentState.error
|
||||
|| this.current.getState() == DeploymentState.unknown;
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Status is finished [" + finished + "]");
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.kafka.clients.consumer.ConsumerRecord;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpanBuilder;
|
||||
import org.springframework.cloud.sleuth.propagation.Propagator;
|
||||
|
||||
final class KafkaTracingUtils {
|
||||
@@ -32,10 +33,13 @@ final class KafkaTracingUtils {
|
||||
|
||||
static <K, V> void buildAndFinishSpan(ConsumerRecord<K, V> consumerRecord, Propagator propagator,
|
||||
Propagator.Getter<ConsumerRecord<?, ?>> extractor) {
|
||||
Span.Builder spanBuilder = propagator.extract(consumerRecord, extractor).kind(Span.Kind.CONSUMER)
|
||||
.name("kafka.consume").tag("kafka.topic", consumerRecord.topic())
|
||||
.tag("kafka.offset", Long.toString(consumerRecord.offset()))
|
||||
.tag("kafka.partition", Integer.toString(consumerRecord.partition()));
|
||||
// @formatter:off
|
||||
Span.Builder spanBuilder = AssertingSpanBuilder.of(SleuthKafkaSpan.KAFKA_CONSUMER_SPAN, propagator.extract(consumerRecord, extractor).kind(Span.Kind.CONSUMER))
|
||||
.name(SleuthKafkaSpan.KAFKA_CONSUMER_SPAN.getName())
|
||||
.tag(SleuthKafkaSpan.ConsumerTags.TOPIC, consumerRecord.topic())
|
||||
.tag(SleuthKafkaSpan.ConsumerTags.OFFSET, Long.toString(consumerRecord.offset()))
|
||||
.tag(SleuthKafkaSpan.ConsumerTags.PARTITION, Integer.toString(consumerRecord.partition()));
|
||||
// @formatter:on
|
||||
Span span = spanBuilder.start();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Extracted span from event headers " + span);
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.kafka;
|
||||
|
||||
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
|
||||
import org.springframework.cloud.sleuth.docs.TagKey;
|
||||
|
||||
enum SleuthKafkaSpan implements DocumentedSpan {
|
||||
|
||||
/**
|
||||
* Span created on the Kafka consumer side.
|
||||
*/
|
||||
KAFKA_CONSUMER_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "kafka.consume";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return ConsumerTags.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prefix() {
|
||||
return "kafka.";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Span created on the Kafka consumer side.
|
||||
*/
|
||||
KAFKA_PRODUCER_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "kafka.produce";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return ProducerTags.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prefix() {
|
||||
return "kafka.";
|
||||
}
|
||||
};
|
||||
|
||||
enum ConsumerTags implements TagKey {
|
||||
|
||||
/**
|
||||
* Name of the Kafka topic.
|
||||
*/
|
||||
TOPIC {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "kafka.topic";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Kafka offset number.
|
||||
*/
|
||||
OFFSET {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "kafka.offset";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Kafka partition number.
|
||||
*/
|
||||
PARTITION {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "kafka.partition";
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
enum ProducerTags implements TagKey {
|
||||
|
||||
/**
|
||||
* Name of the Kafka topic.
|
||||
*/
|
||||
TOPIC {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "kafka.topic";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,6 +38,7 @@ import org.apache.kafka.common.errors.ProducerFencedException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpanBuilder;
|
||||
import org.springframework.cloud.sleuth.propagation.Propagator;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.ResolvableType;
|
||||
@@ -134,8 +135,10 @@ public class TracingKafkaProducer<K, V> implements Producer<K, V> {
|
||||
|
||||
@Override
|
||||
public Future<RecordMetadata> send(ProducerRecord<K, V> producerRecord, Callback callback) {
|
||||
Span.Builder spanBuilder = tracer().spanBuilder().kind(Span.Kind.PRODUCER).name("kafka.produce")
|
||||
.tag("kafka.topic", producerRecord.topic());
|
||||
Span.Builder spanBuilder = AssertingSpanBuilder
|
||||
.of(SleuthKafkaSpan.KAFKA_PRODUCER_SPAN, tracer().spanBuilder().kind(Span.Kind.PRODUCER))
|
||||
.name(SleuthKafkaSpan.KAFKA_PRODUCER_SPAN.getName())
|
||||
.tag(SleuthKafkaSpan.ProducerTags.TOPIC, producerRecord.topic());
|
||||
Span span = spanBuilder.start();
|
||||
propagator().inject(span.context(), producerRecord, injector());
|
||||
try (Tracer.SpanInScope spanInScope = tracer().withSpan(span)) {
|
||||
|
||||
@@ -100,13 +100,15 @@ public class DefaultMessageSpanCustomizer implements MessageSpanCustomizer {
|
||||
|
||||
private void addTags(SpanCustomizer result, MessageChannel channel) {
|
||||
if (channel != null) {
|
||||
result.tag("channel", messageChannelName(channel));
|
||||
SleuthMessagingSpan.MESSAGING_SPAN.wrap(result).tag(SleuthMessagingSpan.Tags.CHANNEL,
|
||||
messageChannelName(channel));
|
||||
}
|
||||
}
|
||||
|
||||
private void addTags(Span.Builder result, MessageChannel channel) {
|
||||
if (channel != null) {
|
||||
result.tag("channel", messageChannelName(channel));
|
||||
SleuthMessagingSpan.MESSAGING_SPAN.wrap(result).tag(SleuthMessagingSpan.Tags.CHANNEL,
|
||||
messageChannelName(channel));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
|
||||
import org.springframework.cloud.sleuth.docs.TagKey;
|
||||
|
||||
enum SleuthMessagingSpan implements DocumentedSpan {
|
||||
|
||||
/**
|
||||
* Span created when message is sent or received.
|
||||
*/
|
||||
MESSAGING_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "%s";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return Tags.values();
|
||||
}
|
||||
};
|
||||
|
||||
enum Tags implements TagKey {
|
||||
|
||||
/**
|
||||
* Name of the Spring Integration channel.
|
||||
*/
|
||||
CHANNEL {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "channel";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* User provided keys via customization options.
|
||||
*/
|
||||
CUSTOM {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "%s";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -97,7 +97,8 @@ class TraceMessageHandler {
|
||||
|
||||
static TraceMessageHandler forNonSpringIntegration(Tracer tracer, Propagator propagator,
|
||||
Propagator.Setter<MessageHeaderAccessor> injector, Propagator.Getter<MessageHeaderAccessor> extractor) {
|
||||
Function<Span, Span> preSendFunction = span -> tracer.nextSpan(span).name("handle").start();
|
||||
Function<Span, Span> preSendFunction = span -> SleuthMessagingSpan.MESSAGING_SPAN.wrap(tracer.nextSpan(span))
|
||||
.name("handle").start();
|
||||
TriConsumer<MessageHeaderAccessor, Span, Span> preSendMessageManipulator = (headers, parentSpan, childSpan) -> {
|
||||
headers.setHeader("traceHandlerParentSpan", parentSpan);
|
||||
headers.setHeader(Span.class.getName(), childSpan);
|
||||
@@ -136,7 +137,8 @@ class TraceMessageHandler {
|
||||
MessageHeaderAccessor headers = mutableHeaderAccessor(message);
|
||||
Span extracted = this.propagator.extract(headers, this.extractor).start();
|
||||
// Start and finish a consumer span as we will immediately process it.
|
||||
Span.Builder consumerSpanBuilder = this.tracer.spanBuilder().setParent(extracted.context());
|
||||
Span.Builder consumerSpanBuilder = SleuthMessagingSpan.MESSAGING_SPAN
|
||||
.wrap(this.tracer.spanBuilder().setParent(extracted.context()));
|
||||
Span consumerSpan = consumerSpan(destinationName, extracted, consumerSpanBuilder);
|
||||
// create and scope a span for the message processor
|
||||
Span span = this.preSendFunction.apply(consumerSpan);
|
||||
@@ -187,7 +189,8 @@ class TraceMessageHandler {
|
||||
|
||||
private void addTags(Span.Builder result, String destinationName) {
|
||||
if (StringUtils.hasText(destinationName)) {
|
||||
result.tag("channel", SpanNameUtil.shorten(destinationName));
|
||||
SleuthMessagingSpan.MESSAGING_SPAN.wrap(result).tag(SleuthMessagingSpan.Tags.CHANNEL,
|
||||
SpanNameUtil.shorten(destinationName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,6 +321,7 @@ class TraceMessageHandler {
|
||||
if (message == null) {
|
||||
message = error.getClass().getSimpleName();
|
||||
}
|
||||
// TODO: Go with span.error(...)
|
||||
span.tag("error", message);
|
||||
}
|
||||
span.end();
|
||||
|
||||
@@ -305,7 +305,7 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
|
||||
}
|
||||
Span consumerSpan = consumerSpan(message, channel, headers);
|
||||
// create and scope a span for the message processor
|
||||
Span handle = this.tracer.nextSpan(consumerSpan);
|
||||
Span handle = SleuthMessagingSpan.MESSAGING_SPAN.wrap(this.tracer.nextSpan(consumerSpan));
|
||||
handle = this.messageSpanCustomizer.customizeHandle(handle, message, channel).start();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Created consumer span " + handle);
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.quartz;
|
||||
|
||||
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
|
||||
import org.springframework.cloud.sleuth.docs.TagKey;
|
||||
|
||||
enum SleuthQuartzSpan implements DocumentedSpan {
|
||||
|
||||
/**
|
||||
* Span created when trigger is fired and then completed.
|
||||
*/
|
||||
QUARTZ_TRIGGER_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "%s";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return Tags.values();
|
||||
}
|
||||
};
|
||||
|
||||
enum Tags implements TagKey {
|
||||
|
||||
/**
|
||||
* Name of the trigger.
|
||||
*/
|
||||
TRIGGER {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "quartz.trigger";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import org.quartz.TriggerListener;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpan;
|
||||
import org.springframework.cloud.sleuth.propagation.Propagator;
|
||||
|
||||
/**
|
||||
@@ -37,8 +38,6 @@ import org.springframework.cloud.sleuth.propagation.Propagator;
|
||||
*/
|
||||
public class TracingJobListener implements JobListener, TriggerListener {
|
||||
|
||||
static final String TRIGGER_TAG_KEY = "quartz.trigger";
|
||||
|
||||
static final String CONTEXT_SPAN_KEY = Span.class.getName();
|
||||
|
||||
static final String CONTEXT_SPAN_IN_SCOPE_KEY = Tracer.SpanInScope.class.getName();
|
||||
@@ -68,8 +67,9 @@ public class TracingJobListener implements JobListener, TriggerListener {
|
||||
@Override
|
||||
public void triggerFired(Trigger trigger, JobExecutionContext context) {
|
||||
Span nextSpan = propagator.extract(context.getMergedJobDataMap(), GETTER).start();
|
||||
Span span = nextSpan.name(context.getTrigger().getJobKey().toString()).tag(TRIGGER_TAG_KEY,
|
||||
context.getTrigger().getKey().toString());
|
||||
AssertingSpan span = SleuthQuartzSpan.QUARTZ_TRIGGER_SPAN.wrap(nextSpan)
|
||||
.tag(SleuthQuartzSpan.Tags.TRIGGER, context.getTrigger().getKey().toString())
|
||||
.name(context.getTrigger().getJobKey().toString());
|
||||
context.put(CONTEXT_SPAN_KEY, span);
|
||||
context.put(CONTEXT_SPAN_IN_SCOPE_KEY, tracer.withSpan(span.start()));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.r2dbc;
|
||||
|
||||
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
|
||||
import org.springframework.cloud.sleuth.docs.EventValue;
|
||||
import org.springframework.cloud.sleuth.docs.TagKey;
|
||||
|
||||
enum SleuthR2dbcSpan implements DocumentedSpan {
|
||||
|
||||
/**
|
||||
* Span created on the Kafka consumer side.
|
||||
*/
|
||||
R2DBC_QUERY_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "query";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return Tags.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prefix() {
|
||||
return "r2dbc.";
|
||||
}
|
||||
};
|
||||
|
||||
enum Tags implements TagKey {
|
||||
|
||||
/**
|
||||
* Name of the R2DBC connection.
|
||||
*/
|
||||
CONNECTION {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "r2dbc.connection";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Name of the R2DBC thread.
|
||||
*/
|
||||
THREAD {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "r2dbc.thread";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Name of the R2DBC query.
|
||||
*/
|
||||
QUERY {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "r2dbc.query[%s]";
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
enum Events implements EventValue {
|
||||
|
||||
/**
|
||||
* Annotated before executing a method annotated with @ContinueSpan or @NewSpan.
|
||||
*/
|
||||
QUERY_RESULT {
|
||||
@Override
|
||||
public String getValue() {
|
||||
return "r2dbc.query_result";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,120 +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.instrument.r2dbc;
|
||||
|
||||
import io.r2dbc.proxy.core.QueryExecutionInfo;
|
||||
import io.r2dbc.proxy.core.QueryInfo;
|
||||
import io.r2dbc.proxy.listener.ProxyExecutionListener;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.boot.autoconfigure.r2dbc.R2dbcProperties;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Trace representation of a {@link ProxyExecutionListener}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public class TraceProxyExecutionListener implements ProxyExecutionListener {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TraceProxyExecutionListener.class);
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
private final ConnectionFactory connectionFactory;
|
||||
|
||||
private Tracer tracer;
|
||||
|
||||
public TraceProxyExecutionListener(BeanFactory beanFactory, ConnectionFactory connectionFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.connectionFactory = connectionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeQuery(QueryExecutionInfo executionInfo) {
|
||||
if (tracer().currentSpan() == null) {
|
||||
return;
|
||||
}
|
||||
String name = this.connectionFactory.getMetadata().getName();
|
||||
Span span = clientSpan(executionInfo, name);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Created a new child span before query [" + span + "]");
|
||||
}
|
||||
tagQueries(executionInfo, span);
|
||||
executionInfo.getValueStore().put(Span.class, span);
|
||||
}
|
||||
|
||||
Span clientSpan(QueryExecutionInfo executionInfo, String name) {
|
||||
R2dbcProperties r2dbcProperties = this.beanFactory.getBean(R2dbcProperties.class);
|
||||
String url = r2dbcProperties.getUrl();
|
||||
Span.Builder builder = tracer().spanBuilder().kind(Span.Kind.CLIENT).name("query")
|
||||
.remoteServiceName(name)
|
||||
.tag("rd2bc.connection", name).tag("rd2bc.thread", executionInfo.getThreadName());
|
||||
if (StringUtils.hasText(url)) {
|
||||
builder.remoteUrl(url);
|
||||
}
|
||||
return builder.start();
|
||||
}
|
||||
|
||||
private void tagQueries(QueryExecutionInfo executionInfo, Span span) {
|
||||
int i = 0;
|
||||
for (QueryInfo queryInfo : executionInfo.getQueries()) {
|
||||
span.tag("r2dbc.query[" + i + "]", queryInfo.getQuery());
|
||||
i = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterQuery(QueryExecutionInfo executionInfo) {
|
||||
Span span = executionInfo.getValueStore().get(Span.class, Span.class);
|
||||
if (span != null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Continued the child span in after query [" + span + "]");
|
||||
}
|
||||
final Throwable throwable = executionInfo.getThrowable();
|
||||
if (throwable != null) {
|
||||
span.error(throwable);
|
||||
}
|
||||
span.end();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eachQueryResult(QueryExecutionInfo executionInfo) {
|
||||
Span span = executionInfo.getValueStore().get(Span.class, Span.class);
|
||||
if (span != null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Marking after query result for span [" + span + "]");
|
||||
}
|
||||
span.event("r2dbc.query_result");
|
||||
}
|
||||
}
|
||||
|
||||
private Tracer tracer() {
|
||||
if (this.tracer == null) {
|
||||
this.tracer = this.beanFactory.getBean(Tracer.class);
|
||||
}
|
||||
return this.tracer;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.r2dbc;
|
||||
|
||||
import io.r2dbc.proxy.core.QueryExecutionInfo;
|
||||
import io.r2dbc.proxy.core.QueryInfo;
|
||||
import io.r2dbc.proxy.listener.ProxyExecutionListener;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.boot.autoconfigure.r2dbc.R2dbcProperties;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpan;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpanBuilder;
|
||||
import org.springframework.cloud.sleuth.internal.ContextUtil;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Trace representation of a {@link ProxyExecutionListener}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public class TraceProxyExecutionListener implements ProxyExecutionListener {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TraceProxyExecutionListener.class);
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
private final ConnectionFactory connectionFactory;
|
||||
|
||||
private Tracer tracer;
|
||||
|
||||
public TraceProxyExecutionListener(BeanFactory beanFactory, ConnectionFactory connectionFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.connectionFactory = connectionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeQuery(QueryExecutionInfo executionInfo) {
|
||||
if (isContextUnusable()) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Context is not ready - won't do anything");
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (tracer().currentSpan() == null) {
|
||||
return;
|
||||
}
|
||||
String name = this.connectionFactory.getMetadata().getName();
|
||||
AssertingSpan span = clientSpan(executionInfo, name);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Created a new child span before query [" + span + "]");
|
||||
}
|
||||
tagQueries(executionInfo, span);
|
||||
executionInfo.getValueStore().put(Span.class, span);
|
||||
}
|
||||
|
||||
AssertingSpan clientSpan(QueryExecutionInfo executionInfo, String name) {
|
||||
R2dbcProperties r2dbcProperties = this.beanFactory.getBean(R2dbcProperties.class);
|
||||
String url = r2dbcProperties.getUrl();
|
||||
// @formatter:off
|
||||
AssertingSpanBuilder builder = AssertingSpanBuilder.of(SleuthR2dbcSpan.R2DBC_QUERY_SPAN, tracer.spanBuilder())
|
||||
.kind(Span.Kind.CLIENT)
|
||||
.name(SleuthR2dbcSpan.R2DBC_QUERY_SPAN.getName()).remoteServiceName(name)
|
||||
.tag(SleuthR2dbcSpan.Tags.CONNECTION, name)
|
||||
.tag(SleuthR2dbcSpan.Tags.THREAD, executionInfo.getThreadName());
|
||||
// @formatter:on
|
||||
if (StringUtils.hasText(url)) {
|
||||
builder.remoteUrl(url);
|
||||
}
|
||||
return builder.start();
|
||||
}
|
||||
|
||||
private void tagQueries(QueryExecutionInfo executionInfo, AssertingSpan span) {
|
||||
int i = 0;
|
||||
for (QueryInfo queryInfo : executionInfo.getQueries()) {
|
||||
span.tag(String.format(SleuthR2dbcSpan.Tags.QUERY.getKey(), i), queryInfo.getQuery());
|
||||
i = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterQuery(QueryExecutionInfo executionInfo) {
|
||||
if (isContextUnusable()) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Context is not ready - won't do anything");
|
||||
}
|
||||
return;
|
||||
}
|
||||
Span span = executionInfo.getValueStore().get(Span.class, Span.class);
|
||||
if (span != null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Continued the child span in after query [" + span + "]");
|
||||
}
|
||||
final Throwable throwable = executionInfo.getThrowable();
|
||||
if (throwable != null) {
|
||||
span.error(throwable);
|
||||
}
|
||||
span.end();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eachQueryResult(QueryExecutionInfo executionInfo) {
|
||||
if (isContextUnusable()) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Context is not ready - won't do anything");
|
||||
}
|
||||
return;
|
||||
}
|
||||
Span span = executionInfo.getValueStore().get(Span.class, Span.class);
|
||||
if (span != null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Marking after query result for span [" + span + "]");
|
||||
}
|
||||
SleuthR2dbcSpan.R2DBC_QUERY_SPAN.wrap(span).event(SleuthR2dbcSpan.Events.QUERY_RESULT);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isContextUnusable() {
|
||||
return ContextUtil.isContextUnusable(this.beanFactory);
|
||||
}
|
||||
|
||||
private Tracer tracer() {
|
||||
if (this.tracer == null) {
|
||||
this.tracer = this.beanFactory.getBean(Tracer.class);
|
||||
}
|
||||
return this.tracer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.rsocket;
|
||||
|
||||
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
|
||||
import org.springframework.cloud.sleuth.docs.TagKey;
|
||||
|
||||
enum SleuthRSocketSpan implements DocumentedSpan {
|
||||
|
||||
/**
|
||||
* Span created on the RSocket responder side.
|
||||
*/
|
||||
RSOCKET_RESPONDER_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "%s";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Span created on the RSocket responder side.
|
||||
*/
|
||||
RSOCKET_REQUESTER_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "%s";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return Tags.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prefix() {
|
||||
return "rsocket.";
|
||||
}
|
||||
};
|
||||
|
||||
enum Tags implements TagKey {
|
||||
|
||||
/**
|
||||
* Name of the RSocket route.
|
||||
*/
|
||||
ROUTE {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "rsocket.route";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Name of the R2DBC thread.
|
||||
*/
|
||||
REQUEST_TYPE {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "rsocket.request-type";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,6 +39,7 @@ import reactor.util.context.ContextView;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceContext;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpanBuilder;
|
||||
import org.springframework.cloud.sleuth.internal.EncodingUtils;
|
||||
import org.springframework.cloud.sleuth.propagation.Propagator;
|
||||
|
||||
@@ -89,9 +90,10 @@ public class TracingRequesterRSocketProxy extends RSocketProxy {
|
||||
final RoutingMetadata routingMetadata = new RoutingMetadata(extracted);
|
||||
final Iterator<String> iterator = routingMetadata.iterator();
|
||||
String route = iterator.next();
|
||||
Span span = spanBuilder.kind(Span.Kind.PRODUCER).name(frameType.name() + " " + route).start();
|
||||
span.tag("rsocket.route", route);
|
||||
span.tag("rsocket.request-type", frameType.name());
|
||||
Span span = AssertingSpanBuilder
|
||||
.of(SleuthRSocketSpan.RSOCKET_REQUESTER_SPAN, spanBuilder.kind(Span.Kind.PRODUCER))
|
||||
.name(frameType.name() + " " + route).tag(SleuthRSocketSpan.Tags.ROUTE, route)
|
||||
.tag(SleuthRSocketSpan.Tags.REQUEST_TYPE, frameType.name()).start();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Extracted result from context or thread local " + span);
|
||||
}
|
||||
@@ -156,7 +158,9 @@ public class TracingRequesterRSocketProxy extends RSocketProxy {
|
||||
final RoutingMetadata routingMetadata = new RoutingMetadata(CompositeMetadataUtils
|
||||
.extract(payload.sliceMetadata(), WellKnownMimeType.MESSAGE_RSOCKET_ROUTING.getString()));
|
||||
final Iterator<String> iterator = routingMetadata.iterator();
|
||||
Span span = spanBuilder.kind(Span.Kind.PRODUCER).name(iterator.next()).start();
|
||||
Span span = AssertingSpanBuilder
|
||||
.of(SleuthRSocketSpan.RSOCKET_REQUESTER_SPAN, spanBuilder.kind(Span.Kind.PRODUCER))
|
||||
.name(iterator.next()).start();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Extracted result from context or thread local " + span);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.springframework.cloud.sleuth.ThreadLocalSpan;
|
||||
import org.springframework.cloud.sleuth.TraceContext;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.WithThreadLocalSpan;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpanBuilder;
|
||||
import org.springframework.cloud.sleuth.instrument.reactor.ReactorSleuth;
|
||||
import org.springframework.cloud.sleuth.internal.EncodingUtils;
|
||||
import org.springframework.cloud.sleuth.propagation.Propagator;
|
||||
@@ -140,7 +141,9 @@ public class TracingResponderRSocketProxy extends RSocketProxy implements WithTh
|
||||
final Iterator<String> iterator = routingMetadata.iterator();
|
||||
name = requestType.name() + " " + iterator.next();
|
||||
}
|
||||
return consumerSpanBuilder.kind(Span.Kind.CONSUMER).name(name).start();
|
||||
return AssertingSpanBuilder
|
||||
.of(SleuthRSocketSpan.RSOCKET_RESPONDER_SPAN, consumerSpanBuilder.kind(Span.Kind.CONSUMER)).name(name)
|
||||
.start();
|
||||
}
|
||||
|
||||
private Span.Builder consumerSpanBuilder(ByteBuf headers) {
|
||||
|
||||
@@ -113,8 +113,6 @@ public class SleuthRxJavaSchedulersHook extends RxJavaSchedulersHook {
|
||||
*/
|
||||
static class TraceAction implements Action0 {
|
||||
|
||||
private static final String THREAD_NAME_KEY = "thread";
|
||||
|
||||
private final Action0 actual;
|
||||
|
||||
private final Tracer tracer;
|
||||
@@ -149,8 +147,9 @@ public class SleuthRxJavaSchedulersHook extends RxJavaSchedulersHook {
|
||||
Span span = this.parent;
|
||||
boolean created = false;
|
||||
if (span == null) {
|
||||
span = this.tracer.nextSpan().name(RXJAVA_COMPONENT).start();
|
||||
span.tag(THREAD_NAME_KEY, Thread.currentThread().getName());
|
||||
span = SleuthRxJavaSpan.RX_JAVA_TRACE_ACTION_SPAN.wrap(this.tracer.nextSpan())
|
||||
.name(SleuthRxJavaSpan.RX_JAVA_TRACE_ACTION_SPAN.getName())
|
||||
.tag(SleuthRxJavaSpan.Tags.THREAD, Thread.currentThread().getName()).start();
|
||||
created = true;
|
||||
}
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpan(span)) {
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.rxjava;
|
||||
|
||||
import rx.functions.Action;
|
||||
|
||||
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
|
||||
import org.springframework.cloud.sleuth.docs.TagKey;
|
||||
|
||||
enum SleuthRxJavaSpan implements DocumentedSpan {
|
||||
|
||||
/**
|
||||
* Span that wraps a Rx Java {@link Action}.
|
||||
*/
|
||||
RX_JAVA_TRACE_ACTION_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "rxjava";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return Tags.values();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Tags related to RX Java.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.3
|
||||
*/
|
||||
enum Tags implements TagKey {
|
||||
|
||||
/**
|
||||
* Name of the thread.
|
||||
*/
|
||||
THREAD {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "thread";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.scheduling;
|
||||
|
||||
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
|
||||
import org.springframework.cloud.sleuth.docs.TagKey;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
enum SleuthSchedulingSpan implements DocumentedSpan {
|
||||
|
||||
/**
|
||||
* Span that wraps a {@link Scheduled} annotated method. Either creates a new span or
|
||||
* continues an existing one.
|
||||
*/
|
||||
SCHEDULED_ANNOTATION_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "%s";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return Tags.values();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
enum Tags implements TagKey {
|
||||
|
||||
/**
|
||||
* Class name where a method got annotated with @Scheduled.
|
||||
*/
|
||||
CLASS {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "class";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Method name that got annotated with @Scheduled.
|
||||
*/
|
||||
METHOD {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "method";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import org.aspectj.lang.annotation.Aspect;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpan;
|
||||
import org.springframework.cloud.sleuth.internal.SpanNameUtil;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -43,10 +44,6 @@ import org.springframework.lang.Nullable;
|
||||
@Aspect
|
||||
public class TraceSchedulingAspect {
|
||||
|
||||
private static final String CLASS_KEY = "class";
|
||||
|
||||
private static final String METHOD_KEY = "method";
|
||||
|
||||
private final Tracer tracer;
|
||||
|
||||
@Nullable
|
||||
@@ -66,10 +63,10 @@ public class TraceSchedulingAspect {
|
||||
return pjp.proceed();
|
||||
}
|
||||
String spanName = SpanNameUtil.toLowerHyphen(pjp.getSignature().getName());
|
||||
Span span = startOrContinueRenamedSpan(spanName);
|
||||
AssertingSpan span = SleuthSchedulingSpan.SCHEDULED_ANNOTATION_SPAN.wrap(startOrContinueSpan()).name(spanName);
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) {
|
||||
span.tag(CLASS_KEY, pjp.getTarget().getClass().getSimpleName());
|
||||
span.tag(METHOD_KEY, pjp.getSignature().getName());
|
||||
span.tag(SleuthSchedulingSpan.Tags.CLASS, pjp.getTarget().getClass().getSimpleName())
|
||||
.tag(SleuthSchedulingSpan.Tags.METHOD, pjp.getSignature().getName());
|
||||
return pjp.proceed();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
@@ -81,12 +78,12 @@ public class TraceSchedulingAspect {
|
||||
}
|
||||
}
|
||||
|
||||
private Span startOrContinueRenamedSpan(String spanName) {
|
||||
private Span startOrContinueSpan() {
|
||||
Span currentSpan = this.tracer.currentSpan();
|
||||
if (currentSpan != null) {
|
||||
return currentSpan.name(spanName);
|
||||
return currentSpan;
|
||||
}
|
||||
return this.tracer.nextSpan().name(spanName);
|
||||
return SleuthSchedulingSpan.SCHEDULED_ANNOTATION_SPAN.wrap(this.tracer.nextSpan());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.instrument.task;
|
||||
|
||||
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
|
||||
|
||||
enum SleuthTaskSpan implements DocumentedSpan {
|
||||
|
||||
/**
|
||||
* Span created when a task runner is executed.
|
||||
*/
|
||||
TASK_RUNNER_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "%s";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Span created within the lifecycle of a task.
|
||||
*/
|
||||
TASK_EXECUTION_LISTENER_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "%s";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,7 +46,7 @@ public class TraceApplicationRunner implements ApplicationRunner {
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
Span span = tracer().nextSpan().name(this.beanName);
|
||||
Span span = SleuthTaskSpan.TASK_RUNNER_SPAN.wrap(tracer().nextSpan()).name(this.beanName);
|
||||
try (Tracer.SpanInScope spanInScope = tracer().withSpan(span.start())) {
|
||||
this.delegate.run(args);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public class TraceCommandLineRunner implements CommandLineRunner {
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
Span span = tracer().nextSpan().name(this.beanName);
|
||||
Span span = SleuthTaskSpan.TASK_RUNNER_SPAN.wrap(tracer().nextSpan()).name(this.beanName);
|
||||
try (Tracer.SpanInScope spanInScope = tracer().withSpan(span.start())) {
|
||||
this.delegate.run(args);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@ public class TraceTaskExecutionListener implements TaskExecutionListener, Ordere
|
||||
|
||||
@Override
|
||||
public void onTaskStartup(TaskExecution taskExecution) {
|
||||
Span span = this.tracer.nextSpan().name(this.projectName).start();
|
||||
Span span = SleuthTaskSpan.TASK_EXECUTION_LISTENER_SPAN.wrap(this.tracer.nextSpan()).name(this.projectName)
|
||||
.start();
|
||||
this.threadLocalSpan.set(span);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Put the span [" + span + "] to thread local");
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.tx;
|
||||
|
||||
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
|
||||
import org.springframework.cloud.sleuth.docs.EventValue;
|
||||
import org.springframework.cloud.sleuth.docs.TagKey;
|
||||
|
||||
enum SleuthTxSpan implements DocumentedSpan {
|
||||
|
||||
/**
|
||||
* Span created when there was no previous transaction. If there was one, we will
|
||||
* continue it unless propagation is required.
|
||||
*/
|
||||
TX_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "tx";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return Tags.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prefix() {
|
||||
return "tx.";
|
||||
}
|
||||
};
|
||||
|
||||
enum Tags implements TagKey {
|
||||
|
||||
/**
|
||||
* Name of the TransactionManager.
|
||||
*/
|
||||
TRANSACTION_MANAGER {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "tx.transaction-manager";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether the transaction is read-only.
|
||||
*/
|
||||
READ_ONLY {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "tx.read-only";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Transaction propagation level.
|
||||
*/
|
||||
PROPAGATION_LEVEL {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "tx.propagation-level";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Transaction isolation level.
|
||||
*/
|
||||
ISOLATION_LEVEL {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "tx.isolation-level";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Transaction timeout.
|
||||
*/
|
||||
TIMEOUT {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "tx.timeout";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Transaction name.
|
||||
*/
|
||||
NAME {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "tx.name";
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
enum Events implements EventValue {
|
||||
|
||||
/**
|
||||
* Annotated after transaction commit.
|
||||
*/
|
||||
COMMIT {
|
||||
@Override
|
||||
public String getValue() {
|
||||
return "tx.commit";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Annotated after transaction rollback.
|
||||
*/
|
||||
ROLLBACK {
|
||||
@Override
|
||||
public String getValue() {
|
||||
return "tx.rollback";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,175 +1,175 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.tx;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanAndScope;
|
||||
import org.springframework.cloud.sleuth.ThreadLocalSpan;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
|
||||
/**
|
||||
* A trace representation of a {@link PlatformTransactionManager}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public class TracePlatformTransactionManager implements PlatformTransactionManager {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TracePlatformTransactionManager.class);
|
||||
|
||||
private final PlatformTransactionManager delegate;
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
private Tracer tracer;
|
||||
|
||||
volatile ThreadLocalSpan threadLocalSpan;
|
||||
|
||||
public TracePlatformTransactionManager(PlatformTransactionManager delegate, BeanFactory beanFactory) {
|
||||
this.delegate = delegate;
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
void initialize() {
|
||||
if (this.threadLocalSpan == null) {
|
||||
this.threadLocalSpan = new ThreadLocalSpan(tracer());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
|
||||
initialize();
|
||||
SpanAndScope spanAndScope = this.threadLocalSpan.get();
|
||||
Span currentSpan = spanAndScope != null ? spanAndScope.getSpan() : tracer().currentSpan();
|
||||
Span span = fallbackSpan();
|
||||
try {
|
||||
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
|
||||
TransactionStatus status = this.delegate.getTransaction(definition);
|
||||
span = taggedSpan(currentSpan, span, def, status);
|
||||
return status;
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(
|
||||
"Exception occurred while trying to get a transaction, will mark the span with error and report it");
|
||||
}
|
||||
span.error(e);
|
||||
span.end();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
Span fallbackSpan() {
|
||||
return tracer().nextSpan().name("tx").start();
|
||||
}
|
||||
|
||||
private Span taggedSpan(Span currentSpan, Span span, TransactionDefinition def, TransactionStatus status) {
|
||||
if (status.isNewTransaction() || currentSpan == null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Creating new span cause a new transaction is started");
|
||||
}
|
||||
TracePlatformTransactionManagerTags.tag(span, def, this.delegate.getClass());
|
||||
}
|
||||
else {
|
||||
span = currentSpan;
|
||||
}
|
||||
this.threadLocalSpan.set(span);
|
||||
return span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit(TransactionStatus status) throws TransactionException {
|
||||
SpanAndScope spanAndScope = this.threadLocalSpan.get();
|
||||
if (spanAndScope == null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("No span and scope found - this shouldn't happen, sth is wrong");
|
||||
}
|
||||
this.delegate.commit(status);
|
||||
return;
|
||||
}
|
||||
Exception ex = null;
|
||||
Span span = spanAndScope.getSpan();
|
||||
try {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Wrapping commit");
|
||||
}
|
||||
this.delegate.commit(status);
|
||||
}
|
||||
catch (Exception e) {
|
||||
ex = e;
|
||||
span.error(e);
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
span.event("tx.commit");
|
||||
span.end();
|
||||
if (ex == null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("No exception was found - will clear thread local span");
|
||||
}
|
||||
this.threadLocalSpan.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback(TransactionStatus status) throws TransactionException {
|
||||
SpanAndScope spanAndScope = this.threadLocalSpan.get();
|
||||
if (spanAndScope == null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("No span and scope found - this shouldn't happen, sth is wrong");
|
||||
}
|
||||
this.delegate.rollback(status);
|
||||
return;
|
||||
}
|
||||
Span span = spanAndScope.getSpan();
|
||||
try {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Wrapping rollback");
|
||||
}
|
||||
this.delegate.rollback(status);
|
||||
}
|
||||
catch (Exception e) {
|
||||
span.error(e);
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
span.event("tx.rollback");
|
||||
span.end();
|
||||
this.threadLocalSpan.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private Tracer tracer() {
|
||||
if (this.tracer == null) {
|
||||
this.tracer = this.beanFactory.getBean(Tracer.class);
|
||||
}
|
||||
return this.tracer;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.tx;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanAndScope;
|
||||
import org.springframework.cloud.sleuth.ThreadLocalSpan;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
|
||||
/**
|
||||
* A trace representation of a {@link PlatformTransactionManager}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public class TracePlatformTransactionManager implements PlatformTransactionManager {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TracePlatformTransactionManager.class);
|
||||
|
||||
private final PlatformTransactionManager delegate;
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
private Tracer tracer;
|
||||
|
||||
volatile ThreadLocalSpan threadLocalSpan;
|
||||
|
||||
public TracePlatformTransactionManager(PlatformTransactionManager delegate, BeanFactory beanFactory) {
|
||||
this.delegate = delegate;
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
void initialize() {
|
||||
if (this.threadLocalSpan == null) {
|
||||
this.threadLocalSpan = new ThreadLocalSpan(tracer());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
|
||||
initialize();
|
||||
SpanAndScope spanAndScope = this.threadLocalSpan.get();
|
||||
Span currentSpan = spanAndScope != null ? spanAndScope.getSpan() : tracer().currentSpan();
|
||||
Span span = fallbackSpan();
|
||||
try {
|
||||
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
|
||||
TransactionStatus status = this.delegate.getTransaction(definition);
|
||||
span = taggedSpan(currentSpan, span, def, status);
|
||||
return status;
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(
|
||||
"Exception occurred while trying to get a transaction, will mark the span with error and report it");
|
||||
}
|
||||
span.error(e);
|
||||
span.end();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
Span fallbackSpan() {
|
||||
return SleuthTxSpan.TX_SPAN.wrap(tracer().nextSpan()).name(SleuthTxSpan.TX_SPAN.getName()).start();
|
||||
}
|
||||
|
||||
private Span taggedSpan(Span currentSpan, Span span, TransactionDefinition def, TransactionStatus status) {
|
||||
if (status.isNewTransaction() || currentSpan == null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Creating new span cause a new transaction is started");
|
||||
}
|
||||
TracePlatformTransactionManagerTags.tag(span, def, this.delegate.getClass());
|
||||
}
|
||||
else {
|
||||
span = currentSpan;
|
||||
}
|
||||
this.threadLocalSpan.set(span);
|
||||
return span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit(TransactionStatus status) throws TransactionException {
|
||||
SpanAndScope spanAndScope = this.threadLocalSpan.get();
|
||||
if (spanAndScope == null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("No span and scope found - this shouldn't happen, sth is wrong");
|
||||
}
|
||||
this.delegate.commit(status);
|
||||
return;
|
||||
}
|
||||
Exception ex = null;
|
||||
Span span = spanAndScope.getSpan();
|
||||
try {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Wrapping commit");
|
||||
}
|
||||
this.delegate.commit(status);
|
||||
}
|
||||
catch (Exception e) {
|
||||
ex = e;
|
||||
span.error(e);
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
SleuthTxSpan.TX_SPAN.wrap(span).event(SleuthTxSpan.Events.COMMIT);
|
||||
span.end();
|
||||
if (ex == null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("No exception was found - will clear thread local span");
|
||||
}
|
||||
this.threadLocalSpan.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback(TransactionStatus status) throws TransactionException {
|
||||
SpanAndScope spanAndScope = this.threadLocalSpan.get();
|
||||
if (spanAndScope == null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("No span and scope found - this shouldn't happen, sth is wrong");
|
||||
}
|
||||
this.delegate.rollback(status);
|
||||
return;
|
||||
}
|
||||
Span span = spanAndScope.getSpan();
|
||||
try {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Wrapping rollback");
|
||||
}
|
||||
this.delegate.rollback(status);
|
||||
}
|
||||
catch (Exception e) {
|
||||
span.error(e);
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
SleuthTxSpan.TX_SPAN.wrap(span).event(SleuthTxSpan.Events.ROLLBACK);
|
||||
span.end();
|
||||
this.threadLocalSpan.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private Tracer tracer() {
|
||||
if (this.tracer == null) {
|
||||
this.tracer = this.beanFactory.getBean(Tracer.class);
|
||||
}
|
||||
return this.tracer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.cloud.sleuth.instrument.tx;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpan;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -28,16 +29,19 @@ final class TracePlatformTransactionManagerTags {
|
||||
}
|
||||
|
||||
static void tag(Span span, TransactionDefinition def, Class transactionManagerClass) {
|
||||
span.tag("tx.transaction-manager", ClassUtils.getQualifiedName(transactionManagerClass));
|
||||
span.tag("tx.read-only", String.valueOf(def.isReadOnly()));
|
||||
span.tag("tx.propagation-level", propagationLevel(def));
|
||||
span.tag("tx.isolation-level", isolationLevel(def));
|
||||
if (def.getTimeout() > 0) {
|
||||
span.tag("tx.timeout", String.valueOf(def.getTimeout()));
|
||||
}
|
||||
if (StringUtils.hasText(def.getName())) {
|
||||
span.tag("tx.name", def.getName());
|
||||
}
|
||||
// @formatter:off
|
||||
AssertingSpan assertingSpan = SleuthTxSpan.TX_SPAN.wrap(span)
|
||||
.tag(SleuthTxSpan.Tags.TRANSACTION_MANAGER, ClassUtils.getQualifiedName(transactionManagerClass))
|
||||
.tag(SleuthTxSpan.Tags.READ_ONLY, String.valueOf(def.isReadOnly()))
|
||||
.tag(SleuthTxSpan.Tags.PROPAGATION_LEVEL, propagationLevel(def))
|
||||
.tag(SleuthTxSpan.Tags.ISOLATION_LEVEL, isolationLevel(def));
|
||||
if (def.getTimeout() > 0) {
|
||||
assertingSpan.tag(SleuthTxSpan.Tags.TIMEOUT, String.valueOf(def.getTimeout()));
|
||||
}
|
||||
if (StringUtils.hasText(def.getName())) {
|
||||
assertingSpan.tag(SleuthTxSpan.Tags.NAME, def.getName());
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String propagationLevel(TransactionDefinition def) {
|
||||
|
||||
@@ -1,187 +1,188 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.tx;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.CurrentTraceContext;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanAndScope;
|
||||
import org.springframework.cloud.sleuth.TraceContext;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.transaction.ReactiveTransaction;
|
||||
import org.springframework.transaction.ReactiveTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
|
||||
/**
|
||||
* A trace representation of a {@link ReactiveTransactionManager}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public class TraceReactiveTransactionManager implements ReactiveTransactionManager {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TraceReactiveTransactionManager.class);
|
||||
|
||||
private final ReactiveTransactionManager delegate;
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
private Tracer tracer;
|
||||
|
||||
private CurrentTraceContext currentTraceContext;
|
||||
|
||||
public TraceReactiveTransactionManager(ReactiveTransactionManager delegate, BeanFactory beanFactory) {
|
||||
this.delegate = delegate;
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
private Tracer tracer() {
|
||||
if (this.tracer == null) {
|
||||
this.tracer = this.beanFactory.getBean(Tracer.class);
|
||||
}
|
||||
return this.tracer;
|
||||
}
|
||||
|
||||
private CurrentTraceContext currentTraceContext() {
|
||||
if (this.currentTraceContext == null) {
|
||||
this.currentTraceContext = this.beanFactory.getBean(CurrentTraceContext.class);
|
||||
}
|
||||
return this.currentTraceContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ReactiveTransaction> getReactiveTransaction(TransactionDefinition definition)
|
||||
throws TransactionException {
|
||||
return Mono.deferContextual(contextView -> this.delegate.getReactiveTransaction(definition).map(tx -> {
|
||||
Span span = span(contextView);
|
||||
if (tx.isNewTransaction() || span == null) {
|
||||
if (span == null) {
|
||||
span = tracer().nextSpan().name("tx").start();
|
||||
}
|
||||
else {
|
||||
span = tracer().nextSpan(span).name("tx").start();
|
||||
}
|
||||
TracePlatformTransactionManagerTags.tag(span, definition, this.delegate.getClass());
|
||||
}
|
||||
Tracer.SpanInScope withSpan = tracer().withSpan(span);
|
||||
SpanAndScope spanAndScope = new SpanAndScope(span, withSpan);
|
||||
return new TraceReactiveTransaction(tx, spanAndScope);
|
||||
}));
|
||||
}
|
||||
|
||||
private Span span(reactor.util.context.ContextView contextView) {
|
||||
Span span = contextView.getOrDefault(Span.class, null);
|
||||
if (span == null) {
|
||||
TraceContext traceContext = contextView.getOrDefault(TraceContext.class, null);
|
||||
if (traceContext == null) {
|
||||
Span currentSpan = tracer().currentSpan();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("There's no Span or TraceContext in the reactor context. Current span is [" + currentSpan
|
||||
+ "]");
|
||||
}
|
||||
span = currentSpan;
|
||||
}
|
||||
else {
|
||||
span = spanFromContext(traceContext);
|
||||
}
|
||||
}
|
||||
return span;
|
||||
}
|
||||
|
||||
private Span spanFromContext(TraceContext traceContext) {
|
||||
try (CurrentTraceContext.Scope scope = currentTraceContext().maybeScope(traceContext)) {
|
||||
return tracer().currentSpan();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> commit(ReactiveTransaction transaction) throws TransactionException {
|
||||
if (!(transaction instanceof TraceReactiveTransaction)) {
|
||||
return this.delegate.commit(transaction);
|
||||
}
|
||||
TraceReactiveTransaction reactiveTransaction = (TraceReactiveTransaction) transaction;
|
||||
SpanAndScope spanAndScope = reactiveTransaction.spanAndScope;
|
||||
Span span = spanAndScope.getSpan();
|
||||
Tracer.SpanInScope scope = spanAndScope.getScope();
|
||||
return this.delegate.commit(reactiveTransaction.delegate)
|
||||
// TODO: Fix me when this is resolved in Reactor
|
||||
// .doOnSubscribe(__ -> scope.close())
|
||||
.doOnError(span::error).doOnSuccess(signalType -> {
|
||||
span.end();
|
||||
scope.close();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> rollback(ReactiveTransaction transaction) throws TransactionException {
|
||||
if (!(transaction instanceof TraceReactiveTransaction)) {
|
||||
return this.delegate.rollback(transaction);
|
||||
}
|
||||
TraceReactiveTransaction reactiveTransaction = (TraceReactiveTransaction) transaction;
|
||||
SpanAndScope spanAndScope = reactiveTransaction.spanAndScope;
|
||||
Span span = spanAndScope.getSpan();
|
||||
Tracer.SpanInScope scope = spanAndScope.getScope();
|
||||
return this.delegate.rollback(reactiveTransaction.delegate)
|
||||
// TODO: Fix me when this is resolved in Reactor
|
||||
// .doOnSubscribe(__ -> scope.close())
|
||||
.doOnError(span::error).doFinally(signalType -> {
|
||||
span.end();
|
||||
if (scope != null) {
|
||||
scope.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static class TraceReactiveTransaction implements ReactiveTransaction {
|
||||
|
||||
final ReactiveTransaction delegate;
|
||||
|
||||
final SpanAndScope spanAndScope;
|
||||
|
||||
TraceReactiveTransaction(ReactiveTransaction delegate, SpanAndScope spanAndScope) {
|
||||
this.delegate = delegate;
|
||||
this.spanAndScope = spanAndScope;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNewTransaction() {
|
||||
return this.delegate.isNewTransaction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() {
|
||||
this.delegate.setRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRollbackOnly() {
|
||||
return this.delegate.isRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompleted() {
|
||||
return this.delegate.isCompleted();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.tx;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.CurrentTraceContext;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanAndScope;
|
||||
import org.springframework.cloud.sleuth.TraceContext;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.transaction.ReactiveTransaction;
|
||||
import org.springframework.transaction.ReactiveTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
|
||||
/**
|
||||
* A trace representation of a {@link ReactiveTransactionManager}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public class TraceReactiveTransactionManager implements ReactiveTransactionManager {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TraceReactiveTransactionManager.class);
|
||||
|
||||
private final ReactiveTransactionManager delegate;
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
private Tracer tracer;
|
||||
|
||||
private CurrentTraceContext currentTraceContext;
|
||||
|
||||
public TraceReactiveTransactionManager(ReactiveTransactionManager delegate, BeanFactory beanFactory) {
|
||||
this.delegate = delegate;
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
private Tracer tracer() {
|
||||
if (this.tracer == null) {
|
||||
this.tracer = this.beanFactory.getBean(Tracer.class);
|
||||
}
|
||||
return this.tracer;
|
||||
}
|
||||
|
||||
private CurrentTraceContext currentTraceContext() {
|
||||
if (this.currentTraceContext == null) {
|
||||
this.currentTraceContext = this.beanFactory.getBean(CurrentTraceContext.class);
|
||||
}
|
||||
return this.currentTraceContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ReactiveTransaction> getReactiveTransaction(TransactionDefinition definition)
|
||||
throws TransactionException {
|
||||
return Mono.deferContextual(contextView -> this.delegate.getReactiveTransaction(definition).map(tx -> {
|
||||
Span span = SleuthTxSpan.TX_SPAN.wrap(span(contextView));
|
||||
if (tx.isNewTransaction() || span == null) {
|
||||
if (span == null) {
|
||||
span = SleuthTxSpan.TX_SPAN.wrap(tracer().nextSpan()).name(SleuthTxSpan.TX_SPAN.getName()).start();
|
||||
}
|
||||
else {
|
||||
span = SleuthTxSpan.TX_SPAN.wrap(tracer().nextSpan(span)).name(SleuthTxSpan.TX_SPAN.getName())
|
||||
.start();
|
||||
}
|
||||
TracePlatformTransactionManagerTags.tag(span, definition, this.delegate.getClass());
|
||||
}
|
||||
Tracer.SpanInScope withSpan = tracer().withSpan(span);
|
||||
SpanAndScope spanAndScope = new SpanAndScope(span, withSpan);
|
||||
return new TraceReactiveTransaction(tx, spanAndScope);
|
||||
}));
|
||||
}
|
||||
|
||||
private Span span(reactor.util.context.ContextView contextView) {
|
||||
Span span = contextView.getOrDefault(Span.class, null);
|
||||
if (span == null) {
|
||||
TraceContext traceContext = contextView.getOrDefault(TraceContext.class, null);
|
||||
if (traceContext == null) {
|
||||
Span currentSpan = tracer().currentSpan();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("There's no Span or TraceContext in the reactor context. Current span is [" + currentSpan
|
||||
+ "]");
|
||||
}
|
||||
span = currentSpan;
|
||||
}
|
||||
else {
|
||||
span = spanFromContext(traceContext);
|
||||
}
|
||||
}
|
||||
return span;
|
||||
}
|
||||
|
||||
private Span spanFromContext(TraceContext traceContext) {
|
||||
try (CurrentTraceContext.Scope scope = currentTraceContext().maybeScope(traceContext)) {
|
||||
return SleuthTxSpan.TX_SPAN.wrap(tracer().currentSpan()).start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> commit(ReactiveTransaction transaction) throws TransactionException {
|
||||
if (!(transaction instanceof TraceReactiveTransaction)) {
|
||||
return this.delegate.commit(transaction);
|
||||
}
|
||||
TraceReactiveTransaction reactiveTransaction = (TraceReactiveTransaction) transaction;
|
||||
SpanAndScope spanAndScope = reactiveTransaction.spanAndScope;
|
||||
Span span = spanAndScope.getSpan();
|
||||
Tracer.SpanInScope scope = spanAndScope.getScope();
|
||||
return this.delegate.commit(reactiveTransaction.delegate)
|
||||
// TODO: Fix me when this is resolved in Reactor
|
||||
// .doOnSubscribe(__ -> scope.close())
|
||||
.doOnError(span::error).doOnSuccess(signalType -> {
|
||||
span.end();
|
||||
scope.close();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> rollback(ReactiveTransaction transaction) throws TransactionException {
|
||||
if (!(transaction instanceof TraceReactiveTransaction)) {
|
||||
return this.delegate.rollback(transaction);
|
||||
}
|
||||
TraceReactiveTransaction reactiveTransaction = (TraceReactiveTransaction) transaction;
|
||||
SpanAndScope spanAndScope = reactiveTransaction.spanAndScope;
|
||||
Span span = spanAndScope.getSpan();
|
||||
Tracer.SpanInScope scope = spanAndScope.getScope();
|
||||
return this.delegate.rollback(reactiveTransaction.delegate)
|
||||
// TODO: Fix me when this is resolved in Reactor
|
||||
// .doOnSubscribe(__ -> scope.close())
|
||||
.doOnError(span::error).doFinally(signalType -> {
|
||||
span.end();
|
||||
if (scope != null) {
|
||||
scope.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static class TraceReactiveTransaction implements ReactiveTransaction {
|
||||
|
||||
final ReactiveTransaction delegate;
|
||||
|
||||
final SpanAndScope spanAndScope;
|
||||
|
||||
TraceReactiveTransaction(ReactiveTransaction delegate, SpanAndScope spanAndScope) {
|
||||
this.delegate = delegate;
|
||||
this.spanAndScope = spanAndScope;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNewTransaction() {
|
||||
return this.delegate.isNewTransaction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() {
|
||||
this.delegate.setRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRollbackOnly() {
|
||||
return this.delegate.isRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompleted() {
|
||||
return this.delegate.isCompleted();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
|
||||
import org.springframework.cloud.sleuth.docs.TagKey;
|
||||
|
||||
enum SleuthWebSpan implements DocumentedSpan {
|
||||
|
||||
/**
|
||||
* Span around a WebFilter. Will continue the current span or create a new one and tag
|
||||
* it
|
||||
*/
|
||||
WEB_FILTER_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "%s";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return Tags.values();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Tags related to web.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.3
|
||||
*/
|
||||
enum Tags implements TagKey {
|
||||
|
||||
/**
|
||||
* Name of the class that is processing the request.
|
||||
*/
|
||||
CLASS {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "mvc.controller.class";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Name of the method that is processing the request.
|
||||
*/
|
||||
METHOD {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "mvc.controller.method";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Response status code.
|
||||
*/
|
||||
RESPONSE_STATUS_CODE {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "http.status_code";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import org.springframework.cloud.sleuth.CurrentTraceContext;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceContext;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpan;
|
||||
import org.springframework.cloud.sleuth.http.HttpServerHandler;
|
||||
import org.springframework.cloud.sleuth.http.HttpServerRequest;
|
||||
import org.springframework.cloud.sleuth.http.HttpServerResponse;
|
||||
@@ -62,13 +63,8 @@ public class TraceWebFilter implements WebFilter, Ordered, ApplicationContextAwa
|
||||
// Remember that this can be used in other packages
|
||||
protected static final String TRACE_REQUEST_ATTR = Span.class.getName();
|
||||
|
||||
static final String MVC_CONTROLLER_CLASS_KEY = "mvc.controller.class";
|
||||
static final String MVC_CONTROLLER_METHOD_KEY = "mvc.controller.method";
|
||||
|
||||
private static final Log log = LogFactory.getLog(TraceWebFilter.class);
|
||||
|
||||
private static final String STATUS_CODE_KEY = "http.status_code";
|
||||
|
||||
private static final String TRACE_SPAN_WITHOUT_PARENT = TraceWebFilter.class.getName() + ".SPAN_WITH_NO_PARENT";
|
||||
|
||||
private final Tracer tracer;
|
||||
@@ -203,6 +199,7 @@ public class TraceWebFilter implements WebFilter, Ordered, ApplicationContextAwa
|
||||
|
||||
private Span findOrCreateSpan(Context c) {
|
||||
Span span;
|
||||
AssertingSpan assertingSpan = null;
|
||||
if (c.hasKey(Span.class)) {
|
||||
Span parent = c.get(Span.class);
|
||||
try (Tracer.SpanInScope spanInScope = this.tracer.withSpan(parent)) {
|
||||
@@ -231,9 +228,13 @@ public class TraceWebFilter implements WebFilter, Ordered, ApplicationContextAwa
|
||||
else if (log.isDebugEnabled()) {
|
||||
log.debug("Found tracer specific span in reactor context [" + span + "]");
|
||||
}
|
||||
this.exchange.getAttributes().put(TRACE_REQUEST_ATTR, span);
|
||||
assertingSpan = SleuthWebSpan.WEB_FILTER_SPAN.wrap(span);
|
||||
this.exchange.getAttributes().put(TRACE_REQUEST_ATTR, assertingSpan);
|
||||
}
|
||||
return span;
|
||||
if (assertingSpan == null) {
|
||||
assertingSpan = SleuthWebSpan.WEB_FILTER_SPAN.wrap(span);
|
||||
}
|
||||
return assertingSpan;
|
||||
}
|
||||
|
||||
static final class WebFilterTraceSubscriber implements CoreSubscriber<Void> {
|
||||
@@ -302,7 +303,7 @@ public class TraceWebFilter implements WebFilter, Ordered, ApplicationContextAwa
|
||||
private void addClassMethodTag(Object handler, Span span) {
|
||||
if (handler instanceof HandlerMethod) {
|
||||
String methodName = ((HandlerMethod) handler).getMethod().getName();
|
||||
span.tag(MVC_CONTROLLER_METHOD_KEY, methodName);
|
||||
SleuthWebSpan.WEB_FILTER_SPAN.wrap(span).tag(SleuthWebSpan.Tags.METHOD, methodName);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Adding a method tag with value [" + methodName + "] to a span " + span);
|
||||
}
|
||||
@@ -323,13 +324,14 @@ public class TraceWebFilter implements WebFilter, Ordered, ApplicationContextAwa
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Adding a class tag with value [" + className + "] to a span " + span);
|
||||
}
|
||||
span.tag(MVC_CONTROLLER_CLASS_KEY, className);
|
||||
SleuthWebSpan.WEB_FILTER_SPAN.wrap(span).tag(SleuthWebSpan.Tags.CLASS, className);
|
||||
}
|
||||
|
||||
private void addResponseTagsForSpanWithoutParent(ServerWebExchange exchange, ServerHttpResponse response,
|
||||
Span span) {
|
||||
if (spanWithoutParent(exchange) && response.getStatusCode() != null && span != null) {
|
||||
span.tag(STATUS_CODE_KEY, String.valueOf(response.getStatusCode().value()));
|
||||
SleuthWebSpan.WEB_FILTER_SPAN.wrap(span).tag(SleuthWebSpan.Tags.CLASS,
|
||||
String.valueOf(response.getStatusCode().value()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanCustomizer;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpanCustomizer;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
@@ -43,10 +44,12 @@ public class HandlerParser {
|
||||
};
|
||||
|
||||
/** Simple class name that processed the request. ex BookController */
|
||||
public static final String CONTROLLER_CLASS = "mvc.controller.class";
|
||||
// TODO: Remove me
|
||||
public static final String CONTROLLER_CLASS = SleuthMvcSpan.Tags.CLASS.getKey();
|
||||
|
||||
/** Method name that processed the request. ex listOfBooks */
|
||||
public static final String CONTROLLER_METHOD = "mvc.controller.method";
|
||||
// TODO: Remove me
|
||||
public static final String CONTROLLER_METHOD = SleuthMvcSpan.Tags.METHOD.getKey();
|
||||
|
||||
/**
|
||||
* Invoked prior to request invocation during
|
||||
@@ -60,13 +63,14 @@ public class HandlerParser {
|
||||
* @param customizer span customizer
|
||||
*/
|
||||
protected void preHandle(HttpServletRequest request, Object handler, SpanCustomizer customizer) {
|
||||
AssertingSpanCustomizer span = SleuthMvcSpan.MVC_HANDLER_INTERCEPTOR_SPAN.wrap(customizer);
|
||||
if (WebMvcRuntime.get().isHandlerMethod(handler)) {
|
||||
HandlerMethod handlerMethod = ((HandlerMethod) handler);
|
||||
customizer.tag(CONTROLLER_CLASS, handlerMethod.getBeanType().getSimpleName());
|
||||
customizer.tag(CONTROLLER_METHOD, handlerMethod.getMethod().getName());
|
||||
span.tag(SleuthMvcSpan.Tags.CLASS, handlerMethod.getBeanType().getSimpleName());
|
||||
span.tag(SleuthMvcSpan.Tags.METHOD, handlerMethod.getMethod().getName());
|
||||
}
|
||||
else {
|
||||
customizer.tag(CONTROLLER_CLASS, handler.getClass().getSimpleName());
|
||||
span.tag(SleuthMvcSpan.Tags.CLASS, handler.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web.mvc;
|
||||
|
||||
import org.springframework.cloud.sleuth.docs.DocumentedSpan;
|
||||
import org.springframework.cloud.sleuth.docs.TagKey;
|
||||
|
||||
enum SleuthMvcSpan implements DocumentedSpan {
|
||||
|
||||
/**
|
||||
* Span around a HandlerInterceptor. Will continue the current span and tag it
|
||||
*/
|
||||
MVC_HANDLER_INTERCEPTOR_SPAN {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "%s";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagKey[] getTagKeys() {
|
||||
return Tags.values();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
enum Tags implements TagKey {
|
||||
|
||||
/**
|
||||
* Class name where a method got annotated with @Scheduled.
|
||||
*/
|
||||
CLASS {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "mvc.controller.class";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Method name that got annotated with @Scheduled.
|
||||
*/
|
||||
METHOD {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "mvc.controller.method";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,143 +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.instrument.r2dbc;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import io.r2dbc.proxy.core.QueryExecutionInfo;
|
||||
import io.r2dbc.proxy.core.ValueStore;
|
||||
import io.r2dbc.proxy.test.MockQueryExecutionInfo;
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import io.r2dbc.spi.ConnectionFactoryMetadata;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.support.StaticListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.r2dbc.R2dbcProperties;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.tracer.SimpleSpan;
|
||||
import org.springframework.cloud.sleuth.tracer.SimpleTracer;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
class TraceProxyExecutionListenerTests {
|
||||
|
||||
SimpleTracer simpleTracer = new SimpleTracer();
|
||||
|
||||
ConnectionFactory connectionFactory = connectionFactory();
|
||||
|
||||
TraceProxyExecutionListener listener = new TraceProxyExecutionListener(beanFactory(), connectionFactory);
|
||||
|
||||
@Test
|
||||
void should_do_nothing_on_before_query_when_there_was_no_previous_span() {
|
||||
MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.empty();
|
||||
|
||||
listener.beforeQuery(queryExecutionInfo);
|
||||
|
||||
then(this.simpleTracer.spans).isEmpty();
|
||||
then(queryExecutionInfo.getValueStore().get(Span.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_do_nothing_on_after_query_when_there_was_no_previous_span() {
|
||||
MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.empty();
|
||||
|
||||
listener.afterQuery(queryExecutionInfo);
|
||||
|
||||
then(this.simpleTracer.spans).isEmpty();
|
||||
then(queryExecutionInfo.getValueStore().get(Span.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_do_nothing_on_each_query_when_there_was_no_previous_span() {
|
||||
MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.empty();
|
||||
|
||||
listener.eachQueryResult(queryExecutionInfo);
|
||||
|
||||
then(this.simpleTracer.spans).isEmpty();
|
||||
then(queryExecutionInfo.getValueStore().get(Span.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_put_a_child_span_in_value_store_when_a_span_was_already_in_context() {
|
||||
MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.empty();
|
||||
this.simpleTracer.nextSpan().start();
|
||||
AtomicReference<Span> clientSpan = new AtomicReference<>();
|
||||
listener = new TraceProxyExecutionListener(beanFactory(), connectionFactory) {
|
||||
@Override
|
||||
Span clientSpan(QueryExecutionInfo executionInfo, String name) {
|
||||
Span span = super.clientSpan(executionInfo, name);
|
||||
clientSpan.set(span);
|
||||
return span;
|
||||
}
|
||||
};
|
||||
|
||||
listener.beforeQuery(queryExecutionInfo);
|
||||
|
||||
then(queryExecutionInfo.getValueStore().get(Span.class)).isSameAs(clientSpan.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_annotate_a_span_on_query_result() {
|
||||
SimpleSpan span = new SimpleSpan();
|
||||
ValueStore valueStore = ValueStore.create();
|
||||
valueStore.put(Span.class, span);
|
||||
MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.builder().valueStore(valueStore).build();
|
||||
|
||||
listener.eachQueryResult(queryExecutionInfo);
|
||||
|
||||
then(span.events).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_end_span_on_after_query() {
|
||||
SimpleSpan span = new SimpleSpan().start();
|
||||
ValueStore valueStore = ValueStore.create();
|
||||
valueStore.put(Span.class, span);
|
||||
MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.builder().throwable(new RuntimeException())
|
||||
.valueStore(valueStore).build();
|
||||
|
||||
listener.afterQuery(queryExecutionInfo);
|
||||
|
||||
then(span.throwable).isNotNull();
|
||||
then(span.ended).isTrue();
|
||||
}
|
||||
|
||||
private ConnectionFactory connectionFactory() {
|
||||
return new ConnectionFactory() {
|
||||
@Override
|
||||
public Publisher<? extends Connection> create() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectionFactoryMetadata getMetadata() {
|
||||
return () -> "my-name";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private BeanFactory beanFactory() {
|
||||
StaticListableBeanFactory beanFactory = new StaticListableBeanFactory();
|
||||
beanFactory.addBean("tracer", this.simpleTracer);
|
||||
beanFactory.addBean("r2dbcProperties", new R2dbcProperties());
|
||||
return beanFactory;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.r2dbc;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import io.r2dbc.proxy.core.QueryExecutionInfo;
|
||||
import io.r2dbc.proxy.core.ValueStore;
|
||||
import io.r2dbc.proxy.test.MockQueryExecutionInfo;
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import io.r2dbc.spi.ConnectionFactoryMetadata;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.support.StaticListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.r2dbc.R2dbcProperties;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpan;
|
||||
import org.springframework.cloud.sleuth.tracer.SimpleSpan;
|
||||
import org.springframework.cloud.sleuth.tracer.SimpleTracer;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
class TraceProxyExecutionListenerTests {
|
||||
|
||||
SimpleTracer simpleTracer = new SimpleTracer();
|
||||
|
||||
ConnectionFactory connectionFactory = connectionFactory();
|
||||
|
||||
TraceProxyExecutionListener listener = new TraceProxyExecutionListener(beanFactory(), connectionFactory) {
|
||||
@Override
|
||||
boolean isContextUnusable() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
void should_do_nothing_on_before_query_when_there_was_no_previous_span() {
|
||||
MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.empty();
|
||||
|
||||
listener.beforeQuery(queryExecutionInfo);
|
||||
|
||||
then(this.simpleTracer.spans).isEmpty();
|
||||
then(queryExecutionInfo.getValueStore().get(Span.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_do_nothing_on_after_query_when_there_was_no_previous_span() {
|
||||
MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.empty();
|
||||
|
||||
listener.afterQuery(queryExecutionInfo);
|
||||
|
||||
then(this.simpleTracer.spans).isEmpty();
|
||||
then(queryExecutionInfo.getValueStore().get(Span.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_do_nothing_on_each_query_when_there_was_no_previous_span() {
|
||||
MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.empty();
|
||||
|
||||
listener.eachQueryResult(queryExecutionInfo);
|
||||
|
||||
then(this.simpleTracer.spans).isEmpty();
|
||||
then(queryExecutionInfo.getValueStore().get(Span.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_put_a_child_span_in_value_store_when_a_span_was_already_in_context() {
|
||||
MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.empty();
|
||||
this.simpleTracer.nextSpan().start();
|
||||
AtomicReference<Span> clientSpan = new AtomicReference<>();
|
||||
listener = new TraceProxyExecutionListener(beanFactory(), connectionFactory) {
|
||||
@Override
|
||||
AssertingSpan clientSpan(QueryExecutionInfo executionInfo, String name) {
|
||||
AssertingSpan span = super.clientSpan(executionInfo, name);
|
||||
clientSpan.set(span);
|
||||
return span;
|
||||
}
|
||||
};
|
||||
|
||||
listener.beforeQuery(queryExecutionInfo);
|
||||
|
||||
then(queryExecutionInfo.getValueStore().get(Span.class)).isSameAs(clientSpan.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_annotate_a_span_on_query_result() {
|
||||
SimpleSpan span = new SimpleSpan();
|
||||
ValueStore valueStore = ValueStore.create();
|
||||
valueStore.put(Span.class, span);
|
||||
MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.builder().valueStore(valueStore).build();
|
||||
|
||||
listener.eachQueryResult(queryExecutionInfo);
|
||||
|
||||
then(span.events).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_end_span_on_after_query() {
|
||||
SimpleSpan span = new SimpleSpan().start();
|
||||
ValueStore valueStore = ValueStore.create();
|
||||
valueStore.put(Span.class, span);
|
||||
MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.builder().throwable(new RuntimeException())
|
||||
.valueStore(valueStore).build();
|
||||
|
||||
listener.afterQuery(queryExecutionInfo);
|
||||
|
||||
then(span.throwable).isNotNull();
|
||||
then(span.ended).isTrue();
|
||||
}
|
||||
|
||||
private ConnectionFactory connectionFactory() {
|
||||
return new ConnectionFactory() {
|
||||
@Override
|
||||
public Publisher<? extends Connection> create() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectionFactoryMetadata getMetadata() {
|
||||
return () -> "my-name";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private BeanFactory beanFactory() {
|
||||
StaticListableBeanFactory beanFactory = new StaticListableBeanFactory();
|
||||
beanFactory.addBean("tracer", this.simpleTracer);
|
||||
beanFactory.addBean("r2dbcProperties", new R2dbcProperties());
|
||||
return beanFactory;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.support.StaticListableBeanFactory;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanAndScope;
|
||||
import org.springframework.cloud.sleuth.docs.AssertingSpan;
|
||||
import org.springframework.cloud.sleuth.tracer.SimpleSpan;
|
||||
import org.springframework.cloud.sleuth.tracer.SimpleTracer;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
@@ -186,7 +187,7 @@ class TracePlatformTransactionManagerTests {
|
||||
private SimpleSpan thenATaggedSpanWasCreated(TracePlatformTransactionManager manager) {
|
||||
SpanAndScope spanAndScope = manager.threadLocalSpan.get();
|
||||
then(spanAndScope).isNotNull();
|
||||
SimpleSpan span = (SimpleSpan) spanAndScope.getSpan();
|
||||
SimpleSpan span = AssertingSpan.unwrap(spanAndScope.getSpan());
|
||||
then(span.started).isTrue();
|
||||
then(span.tags).isNotEmpty();
|
||||
return span;
|
||||
|
||||
@@ -1,113 +1,113 @@
|
||||
/*
|
||||
* 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.tracer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceContext;
|
||||
|
||||
/**
|
||||
* A noop implementation. Does nothing.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public class SimpleSpanBuilder implements Span.Builder {
|
||||
|
||||
public List<String> events = new ArrayList<>();
|
||||
|
||||
public Map<String, String> tags = new HashMap<>();
|
||||
|
||||
public Throwable error;
|
||||
|
||||
public Span.Kind spanKind;
|
||||
|
||||
public String remoteServiceName;
|
||||
|
||||
public String name;
|
||||
|
||||
public SimpleTracer simpleTracer;
|
||||
|
||||
public SimpleSpanBuilder(SimpleTracer simpleTracer) {
|
||||
this.simpleTracer = simpleTracer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder setParent(TraceContext context) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder setNoParent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder event(String value) {
|
||||
this.events.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder tag(String key, String value) {
|
||||
this.tags.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder error(Throwable throwable) {
|
||||
this.error = throwable;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder kind(Span.Kind spanKind) {
|
||||
this.spanKind = spanKind;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder remoteServiceName(String remoteServiceName) {
|
||||
this.remoteServiceName = remoteServiceName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span start() {
|
||||
SimpleSpan span = new SimpleSpan();
|
||||
this.tags.forEach(span::tag);
|
||||
this.events.forEach(span::event);
|
||||
span.remoteServiceName(this.remoteServiceName);
|
||||
span.error(this.error);
|
||||
span.spanKind = this.spanKind;
|
||||
span.name(this.name);
|
||||
span.start();
|
||||
simpleTracer.spans.add(span);
|
||||
return span;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.tracer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceContext;
|
||||
|
||||
/**
|
||||
* A noop implementation. Does nothing.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public class SimpleSpanBuilder implements Span.Builder {
|
||||
|
||||
public List<String> events = new ArrayList<>();
|
||||
|
||||
public Map<String, String> tags = new HashMap<>();
|
||||
|
||||
public Throwable error;
|
||||
|
||||
public Span.Kind spanKind;
|
||||
|
||||
public String remoteServiceName;
|
||||
|
||||
public String name;
|
||||
|
||||
public SimpleTracer simpleTracer;
|
||||
|
||||
public SimpleSpanBuilder(SimpleTracer simpleTracer) {
|
||||
this.simpleTracer = simpleTracer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder setParent(TraceContext context) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder setNoParent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder event(String value) {
|
||||
this.events.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder tag(String key, String value) {
|
||||
this.tags.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder error(Throwable throwable) {
|
||||
this.error = throwable;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder kind(Span.Kind spanKind) {
|
||||
this.spanKind = spanKind;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder remoteServiceName(String remoteServiceName) {
|
||||
this.remoteServiceName = remoteServiceName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span start() {
|
||||
SimpleSpan span = new SimpleSpan();
|
||||
this.tags.forEach(span::tag);
|
||||
this.events.forEach(span::event);
|
||||
span.remoteServiceName(this.remoteServiceName);
|
||||
span.error(this.error);
|
||||
span.spanKind = this.spanKind;
|
||||
span.name(this.name);
|
||||
span.start();
|
||||
simpleTracer.spans.add(span);
|
||||
return span;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,128 +1,128 @@
|
||||
/*
|
||||
* 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.tracer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
|
||||
import org.springframework.cloud.sleuth.BaggageInScope;
|
||||
import org.springframework.cloud.sleuth.ScopedSpan;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanCustomizer;
|
||||
import org.springframework.cloud.sleuth.TraceContext;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
/**
|
||||
* A noop implementation. Does nothing.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public class SimpleTracer implements Tracer {
|
||||
|
||||
public List<SimpleSpan> spans = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public Span nextSpan(Span parent) {
|
||||
return new SimpleSpan();
|
||||
}
|
||||
|
||||
public SimpleSpan getOnlySpan() {
|
||||
BDDAssertions.then(this.spans).hasSize(1);
|
||||
SimpleSpan span = this.spans.get(0);
|
||||
BDDAssertions.then(span.started).as("Span must be started").isTrue();
|
||||
BDDAssertions.then(span.ended).as("Span must be finished").isTrue();
|
||||
return span;
|
||||
}
|
||||
|
||||
public SimpleSpan getLastSpan() {
|
||||
BDDAssertions.then(this.spans).isNotEmpty();
|
||||
SimpleSpan span = this.spans.get(this.spans.size() - 1);
|
||||
BDDAssertions.then(span.started).as("Span must be started").isTrue();
|
||||
return span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpanInScope withSpan(Span span) {
|
||||
return new NoOpSpanInScope();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpanCustomizer currentSpanCustomizer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span currentSpan() {
|
||||
if (this.spans.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return this.spans.get(spans.size() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleSpan nextSpan() {
|
||||
final SimpleSpan span = new SimpleSpan();
|
||||
this.spans.add(span);
|
||||
return span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScopedSpan startScopedSpan(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder spanBuilder() {
|
||||
return new SimpleSpanBuilder(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceContext.Builder traceContextBuilder() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAllBaggage() {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaggageInScope getBaggage(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaggageInScope getBaggage(TraceContext traceContext, String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaggageInScope createBaggage(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaggageInScope createBaggage(String name, String value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.tracer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
|
||||
import org.springframework.cloud.sleuth.BaggageInScope;
|
||||
import org.springframework.cloud.sleuth.ScopedSpan;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanCustomizer;
|
||||
import org.springframework.cloud.sleuth.TraceContext;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
/**
|
||||
* A noop implementation. Does nothing.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public class SimpleTracer implements Tracer {
|
||||
|
||||
public List<SimpleSpan> spans = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public Span nextSpan(Span parent) {
|
||||
return new SimpleSpan();
|
||||
}
|
||||
|
||||
public SimpleSpan getOnlySpan() {
|
||||
BDDAssertions.then(this.spans).hasSize(1);
|
||||
SimpleSpan span = this.spans.get(0);
|
||||
BDDAssertions.then(span.started).as("Span must be started").isTrue();
|
||||
BDDAssertions.then(span.ended).as("Span must be finished").isTrue();
|
||||
return span;
|
||||
}
|
||||
|
||||
public SimpleSpan getLastSpan() {
|
||||
BDDAssertions.then(this.spans).isNotEmpty();
|
||||
SimpleSpan span = this.spans.get(this.spans.size() - 1);
|
||||
BDDAssertions.then(span.started).as("Span must be started").isTrue();
|
||||
return span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpanInScope withSpan(Span span) {
|
||||
return new NoOpSpanInScope();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpanCustomizer currentSpanCustomizer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span currentSpan() {
|
||||
if (this.spans.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return this.spans.get(spans.size() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleSpan nextSpan() {
|
||||
final SimpleSpan span = new SimpleSpan();
|
||||
this.spans.add(span);
|
||||
return span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScopedSpan startScopedSpan(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span.Builder spanBuilder() {
|
||||
return new SimpleSpanBuilder(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceContext.Builder traceContextBuilder() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAllBaggage() {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaggageInScope getBaggage(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaggageInScope getBaggage(TraceContext traceContext, String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaggageInScope createBaggage(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaggageInScope createBaggage(String name, String value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
logging.level.org.springframework.cloud: DEBUG
|
||||
logging.level.org.springframework.cloud: TRACE
|
||||
|
||||
@@ -50,7 +50,6 @@ import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.cloud.sleuth.instrument.quartz.TracingJobListener.CONTEXT_SPAN_IN_SCOPE_KEY;
|
||||
import static org.springframework.cloud.sleuth.instrument.quartz.TracingJobListener.CONTEXT_SPAN_KEY;
|
||||
import static org.springframework.cloud.sleuth.instrument.quartz.TracingJobListener.TRIGGER_TAG_KEY;
|
||||
|
||||
/**
|
||||
* @author Branden Cash
|
||||
@@ -125,7 +124,8 @@ public abstract class TracingJobListenerTest implements TestTracingAwareSupplier
|
||||
// expect
|
||||
FinishedSpan span = tracerTest().handler().takeLocalSpan();
|
||||
assertThat(span.getName()).isEqualToIgnoringCase(SUCCESSFUL_JOB_KEY.toString());
|
||||
assertThat(span.getTags().get(TRIGGER_TAG_KEY)).isEqualToIgnoringCase(TRIGGER_KEY.toString());
|
||||
assertThat(span.getTags().get(SleuthQuartzSpan.Tags.TRIGGER.getKey()))
|
||||
.isEqualToIgnoringCase(TRIGGER_KEY.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user