diff --git a/docs/src/main/asciidoc/_spans.adoc b/docs/src/main/asciidoc/_spans.adoc index 4172c04e5..2ded3abf3 100644 --- a/docs/src/main/asciidoc/_spans.adoc +++ b/docs/src/main/asciidoc/_spans.adoc @@ -507,6 +507,46 @@ Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.in |method|Method name that got annotated with @Scheduled. |=== +=== Session Create Span + +> Span created when a new session has to be created. + +**Span name** `session.create`. + +Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.session.SleuthSessionSpan` + +=== Session Delete Span + +> Span created when a session is deleted. + +**Span name** `session.delete`. + +Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.session.SleuthSessionSpan` + +=== Session Find Span + +> Span created when a new session is searched for. + +**Span name** `session.find`. + +Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.session.SleuthSessionSpan` + +IMPORTANT: All tags and events must be prefixed with `session.` prefix! + +.Tag Keys +|=== +|Name | Description +|session.index.name| +|=== + +=== Session Save Span + +> Span created when a new session is saved. + +**Span name** `session.save`. + +Fully qualified name of the enclosing class `org.springframework.cloud.sleuth.instrument.session.SleuthSessionSpan` + === Task Execution Listener Span > Span created within the lifecycle of a task. diff --git a/docs/src/main/asciidoc/integrations.adoc b/docs/src/main/asciidoc/integrations.adoc index 1d43c2604..a0d24f277 100644 --- a/docs/src/main/asciidoc/integrations.adoc +++ b/docs/src/main/asciidoc/integrations.adoc @@ -714,3 +714,11 @@ Please check the <> page under `spring.sleuth.j You can configure P6Spy manually using one of available configuration methods. For more information please refer to the http://p6spy.readthedocs.io/en/latest/configandusage.html[P6Spy Configuration Guide]. In order to disable this instrumentation set `spring.sleuth.jdbc.enabled` to `false`. + +[[sleuth-session-integration]] +== Spring Session + +This feature is available for all tracer implementations. + +We're instrumenting the `Session` repositories that wraps all operations in a span. +In order to disable this instrumentation set `spring.sleuth.session.enabled` to `false`. diff --git a/spring-cloud-sleuth-autoconfigure/pom.xml b/spring-cloud-sleuth-autoconfigure/pom.xml index 34c1f7bfa..de8c4d1b1 100644 --- a/spring-cloud-sleuth-autoconfigure/pom.xml +++ b/spring-cloud-sleuth-autoconfigure/pom.xml @@ -213,6 +213,11 @@ HikariCP true + + org.springframework.session + spring-session-data-redis + true + org.springframework.cloud diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/session/TraceSessionAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/session/TraceSessionAutoConfiguration.java new file mode 100644 index 000000000..1dda32807 --- /dev/null +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/session/TraceSessionAutoConfiguration.java @@ -0,0 +1,51 @@ +/* + * Copyright 2013-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.sleuth.autoconfig.instrument.session; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.sleuth.CurrentTraceContext; +import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration; +import org.springframework.cloud.sleuth.instrument.session.TraceSessionRepositoryAspect; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.session.SessionRepository; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} that registers instrumentation for Spring Session. + * + * @author Marcin Grzejszczak + * @since 3.1.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(SessionRepository.class) +@ConditionalOnProperty(value = "spring.sleuth.session.enabled", matchIfMissing = true) +@ConditionalOnBean(Tracer.class) +@AutoConfigureAfter(BraveAutoConfiguration.class) +public class TraceSessionAutoConfiguration { + + // Bean post processors don't work cause session-redis requires concrete classes + @Bean + TraceSessionRepositoryAspect traceSessionRepositoryAspect(Tracer tracer, CurrentTraceContext currentTraceContext) { + return new TraceSessionRepositoryAspect(tracer, currentTraceContext); + } + +} diff --git a/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 95cc9ca24..1cfec09f1 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -185,6 +185,12 @@ "description": "Enable R2dbc instrumentation.", "defaultValue": true }, + { + "name": "spring.sleuth.session.enabled", + "type": "java.lang.Boolean", + "description": "Enable Spring Session instrumentation.", + "defaultValue": true + }, { "name": "spring.sleuth.vault.enabled", "type": "java.lang.Boolean", diff --git a/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/spring.factories index 16b2fd99f..f859d669d 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/spring.factories @@ -18,6 +18,7 @@ org.springframework.cloud.sleuth.autoconfig.instrument.web.client.TraceWebClient org.springframework.cloud.sleuth.autoconfig.instrument.web.client.feign.TraceFeignClientAutoConfiguration,\ org.springframework.cloud.sleuth.autoconfig.instrument.web.client.TraceWebAsyncClientAutoConfiguration,\ org.springframework.cloud.sleuth.autoconfig.instrument.scheduling.TraceSchedulingAutoConfiguration,\ +org.springframework.cloud.sleuth.autoconfig.instrument.session.TraceSessionAutoConfiguration,\ org.springframework.cloud.sleuth.autoconfig.instrument.reactor.TraceReactorAutoConfiguration,\ org.springframework.cloud.sleuth.autoconfig.instrument.messaging.TraceFunctionAutoConfiguration,\ org.springframework.cloud.sleuth.autoconfig.instrument.messaging.TraceSpringIntegrationAutoConfiguration,\ diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/rpc/BraveRpcAutoConfigurationIntegrationTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/rpc/BraveRpcAutoConfigurationIntegrationTests.java index 0c2ea1e76..730c7a278 100644 --- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/rpc/BraveRpcAutoConfigurationIntegrationTests.java +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/rpc/BraveRpcAutoConfigurationIntegrationTests.java @@ -30,6 +30,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration; import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration; @@ -62,7 +63,7 @@ public class BraveRpcAutoConfigurationIntegrationTests { @EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class, GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class, - R2dbcDataAutoConfiguration.class }) + R2dbcDataAutoConfiguration.class, RedisAutoConfiguration.class }) @Configuration(proxyBeanMethods = false) public static class Config { diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/web/client/WebClientTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/web/client/WebClientTests.java index 5da06cf57..c5fcf7908 100644 --- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/web/client/WebClientTests.java +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/web/client/WebClientTests.java @@ -43,6 +43,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration; import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; @@ -150,8 +151,8 @@ public class WebClientTests { @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class, - R2dbcAutoConfiguration.class, R2dbcDataAutoConfiguration.class, MongoAutoConfiguration.class, - MongoDataAutoConfiguration.class }) + R2dbcAutoConfiguration.class, R2dbcDataAutoConfiguration.class, RedisAutoConfiguration.class, + MongoAutoConfiguration.class, MongoDataAutoConfiguration.class }) @DisableSecurity public static class TestConfiguration { diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/async/TraceAsyncDefaultAutoConfigurationTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/async/TraceAsyncDefaultAutoConfigurationTests.java index 4f001fb06..d96ce17c4 100644 --- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/async/TraceAsyncDefaultAutoConfigurationTests.java +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/async/TraceAsyncDefaultAutoConfigurationTests.java @@ -26,6 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration; import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration; @@ -53,7 +54,7 @@ public class TraceAsyncDefaultAutoConfigurationTests { @EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class, GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class, - R2dbcDataAutoConfiguration.class }) + R2dbcDataAutoConfiguration.class, RedisAutoConfiguration.class }) static class Config { @Bean diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/quartz/TraceQuartzAutoConfigurationTest.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/quartz/TraceQuartzAutoConfigurationTest.java index 165e728c6..8ce3abc66 100644 --- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/quartz/TraceQuartzAutoConfigurationTest.java +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/quartz/TraceQuartzAutoConfigurationTest.java @@ -27,6 +27,7 @@ import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration; import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration; @@ -115,10 +116,12 @@ public class TraceQuartzAutoConfigurationTest { } @Configuration(proxyBeanMethods = false) - @EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class, - GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, - MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class, - R2dbcDataAutoConfiguration.class }) + @EnableAutoConfiguration( + exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class, + GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, + MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class, + R2dbcDataAutoConfiguration.class, RedisAutoConfiguration.class }, + excludeName = "org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration") public static class EnableAutoConfig { } diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/session/TraceSessionAutoConfigurationTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/session/TraceSessionAutoConfigurationTests.java new file mode 100644 index 000000000..67e03c395 --- /dev/null +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/session/TraceSessionAutoConfigurationTests.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.sleuth.autoconfig.instrument.session; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.FilteredClassLoader; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.cloud.sleuth.autoconfig.TraceNoOpAutoConfiguration; +import org.springframework.cloud.sleuth.instrument.session.TraceSessionRepositoryAspect; +import org.springframework.session.SessionRepository; + +class TraceSessionAutoConfigurationTests { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withPropertyValues("spring.sleuth.noop.enabled=true").withConfiguration( + AutoConfigurations.of(TraceNoOpAutoConfiguration.class, TraceSessionAutoConfiguration.class)); + + @Test + void should_register_session_aspect() { + this.contextRunner + .run(context -> Assertions.assertThat(context).hasSingleBean(TraceSessionRepositoryAspect.class)); + } + + @Test + void should_not_register_session_aspect_when_session_repository_not_on_classpath() { + this.contextRunner.withClassLoader(new FilteredClassLoader(SessionRepository.class)) + .run(context -> Assertions.assertThat(context).doesNotHaveBean(TraceSessionRepositoryAspect.class)); + } + + @Test + void should_not_register_session_aspect_when_session_instrumentation_is_disabled() { + this.contextRunner.withPropertyValues("spring.sleuth.session.enabled=false") + .run(context -> Assertions.assertThat(context).doesNotHaveBean(TraceSessionRepositoryAspect.class)); + } + +} diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/BraveWebClientAutoConfigurationTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/BraveWebClientAutoConfigurationTests.java index 8306face3..016442269 100644 --- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/BraveWebClientAutoConfigurationTests.java +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/BraveWebClientAutoConfigurationTests.java @@ -30,6 +30,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration; import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration; @@ -123,7 +124,7 @@ public class BraveWebClientAutoConfigurationTests { @EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class, GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class, - R2dbcDataAutoConfiguration.class }) + R2dbcDataAutoConfiguration.class, RedisAutoConfiguration.class }) static class Config { // custom builder diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/GH846Tests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/GH846Tests.java index 0fd557aad..0801a617d 100644 --- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/GH846Tests.java +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/GH846Tests.java @@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration; import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration; @@ -57,7 +58,7 @@ public class GH846Tests { @EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class, GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class, - R2dbcDataAutoConfiguration.class }) + R2dbcDataAutoConfiguration.class, RedisAutoConfiguration.class }) static class App { @Bean diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceWebClientDisabledTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceWebClientDisabledTests.java index 43e9d29d9..07c6b8e7b 100644 --- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceWebClientDisabledTests.java +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceWebClientDisabledTests.java @@ -21,6 +21,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration; import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration; @@ -46,7 +47,7 @@ public class TraceWebClientDisabledTests { @EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class, GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class, - R2dbcDataAutoConfiguration.class }) + R2dbcDataAutoConfiguration.class, RedisAutoConfiguration.class }) public static class Config { } diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/zipkin2/ZipkinSamplerTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/zipkin2/ZipkinSamplerTests.java index f3497539d..b3061be84 100644 --- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/zipkin2/ZipkinSamplerTests.java +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/zipkin2/ZipkinSamplerTests.java @@ -23,6 +23,7 @@ import org.springframework.boot.actuate.autoconfigure.security.servlet.Managemen import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration; import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration; @@ -53,10 +54,12 @@ public class ZipkinSamplerTests { } @Configuration(proxyBeanMethods = false) - @EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class, - GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, - MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class, - R2dbcDataAutoConfiguration.class }) + @EnableAutoConfiguration( + exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class, + GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, + MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class, + R2dbcDataAutoConfiguration.class, RedisAutoConfiguration.class }, + excludeName = "org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration") static class TestConfig { } diff --git a/spring-cloud-sleuth-autoconfigure/src/test/resources/application.yml b/spring-cloud-sleuth-autoconfigure/src/test/resources/application.yml index 1e05c6764..e5b38daa4 100644 --- a/spring-cloud-sleuth-autoconfigure/src/test/resources/application.yml +++ b/spring-cloud-sleuth-autoconfigure/src/test/resources/application.yml @@ -1,3 +1,3 @@ logging.level.org.springframework.cloud: DEBUG -spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration, org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration, org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration, org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration, org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration, org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration +spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration, org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration, org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration, org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration, org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration, org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration, org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration, org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration diff --git a/spring-cloud-sleuth-instrumentation/pom.xml b/spring-cloud-sleuth-instrumentation/pom.xml index 17b95a094..fd7a5aa4d 100644 --- a/spring-cloud-sleuth-instrumentation/pom.xml +++ b/spring-cloud-sleuth-instrumentation/pom.xml @@ -197,6 +197,11 @@ spring-security-oauth2-autoconfigure true + + org.springframework.session + spring-session-data-redis + true + p6spy diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/TraceCircuitBreakerFactoryAspect.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/TraceCircuitBreakerFactoryAspect.java index 32505fbc7..58c38607e 100644 --- a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/TraceCircuitBreakerFactoryAspect.java +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/TraceCircuitBreakerFactoryAspect.java @@ -24,7 +24,7 @@ import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; import org.springframework.cloud.sleuth.Tracer; /** - * Aspec around {@link CircuitBreaker} creation. + * Aspect around {@link CircuitBreaker} creation. * * @author Marcin Grzejszczak * @since 3.0.0 diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/session/SleuthSessionSpan.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/session/SleuthSessionSpan.java new file mode 100644 index 000000000..d34a1b025 --- /dev/null +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/session/SleuthSessionSpan.java @@ -0,0 +1,85 @@ +/* + * 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.session; + +import org.springframework.cloud.sleuth.docs.DocumentedSpan; +import org.springframework.cloud.sleuth.docs.TagKey; + +enum SleuthSessionSpan implements DocumentedSpan { + + /** + * Span created when a new session has to be created. + */ + SESSION_CREATE_SPAN { + @Override + public String getName() { + return "session.create"; + } + }, + + /** + * Span created when a new session is searched for. + */ + SESSION_FIND_SPAN { + @Override + public String getName() { + return "session.find"; + } + + @Override + public TagKey[] getTagKeys() { + return Tags.values(); + } + + @Override + public String prefix() { + return "session."; + } + }, + + /** + * Span created when a new session is saved. + */ + SESSION_SAVE_SPAN { + @Override + public String getName() { + return "session.save"; + } + }, + + /** + * Span created when a session is deleted. + */ + SESSION_DELETE_SPAN { + @Override + public String getName() { + return "session.delete"; + } + }; + + enum Tags implements TagKey { + + INDEX_NAME { + @Override + public String getKey() { + return "session.index.name"; + } + } + + } + +} diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/session/TraceFindByIndexNameSessionRepository.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/session/TraceFindByIndexNameSessionRepository.java new file mode 100644 index 000000000..eb2480f39 --- /dev/null +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/session/TraceFindByIndexNameSessionRepository.java @@ -0,0 +1,62 @@ +/* + * 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.session; + +import java.util.Map; + +import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.docs.AssertingSpan; +import org.springframework.session.FindByIndexNameSessionRepository; + +class TraceFindByIndexNameSessionRepository extends TraceSessionRepository implements FindByIndexNameSessionRepository { + + private final FindByIndexNameSessionRepository delegate; + + TraceFindByIndexNameSessionRepository(Tracer tracer, FindByIndexNameSessionRepository delegate) { + super(tracer, delegate); + this.delegate = delegate; + } + + @Override + public Map findByPrincipalName(String principalName) { + AssertingSpan span = newSessionFindSpan(); + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { + return this.delegate.findByPrincipalName(principalName); + } + finally { + span.end(); + } + } + + private AssertingSpan newSessionFindSpan() { + return AssertingSpan.of(SleuthSessionSpan.SESSION_FIND_SPAN, this.tracer.nextSpan()) + .name(SleuthSessionSpan.SESSION_FIND_SPAN.getName()); + } + + @Override + public Map findByIndexNameAndIndexValue(String indexName, String indexValue) { + AssertingSpan span = newSessionFindSpan(); + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { + span.tag(SleuthSessionSpan.Tags.INDEX_NAME, indexName); + return this.delegate.findByIndexNameAndIndexValue(indexName, indexValue); + } + finally { + span.end(); + } + } + +} diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/session/TraceReactiveSessionRepository.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/session/TraceReactiveSessionRepository.java new file mode 100644 index 000000000..ffd3506a2 --- /dev/null +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/session/TraceReactiveSessionRepository.java @@ -0,0 +1,66 @@ +/* + * 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.session; + +import reactor.core.publisher.Mono; + +import org.springframework.cloud.sleuth.CurrentTraceContext; +import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.instrument.reactor.ReactorSleuth; +import org.springframework.session.ReactiveSessionRepository; +import org.springframework.session.Session; + +class TraceReactiveSessionRepository implements ReactiveSessionRepository { + + private final ReactiveSessionRepository delegate; + + private final Tracer tracer; + + private final CurrentTraceContext currentTraceContext; + + TraceReactiveSessionRepository(Tracer tracer, CurrentTraceContext currentTraceContext, + ReactiveSessionRepository delegate) { + this.delegate = delegate; + this.tracer = tracer; + this.currentTraceContext = currentTraceContext; + } + + @Override + public Mono createSession() { + return ReactorSleuth.tracedMono(this.tracer, this.currentTraceContext, + SleuthSessionSpan.SESSION_CREATE_SPAN.getName(), () -> this.delegate.createSession()); + } + + @Override + public Mono save(Session session) { + return ReactorSleuth.tracedMono(this.tracer, this.currentTraceContext, + SleuthSessionSpan.SESSION_SAVE_SPAN.getName(), () -> this.delegate.save(session)); + } + + @Override + public Mono findById(String id) { + return ReactorSleuth.tracedMono(this.tracer, this.currentTraceContext, + SleuthSessionSpan.SESSION_FIND_SPAN.getName(), () -> this.delegate.findById(id)); + } + + @Override + public Mono deleteById(String id) { + return ReactorSleuth.tracedMono(this.tracer, this.currentTraceContext, + SleuthSessionSpan.SESSION_DELETE_SPAN.getName(), () -> this.delegate.deleteById(id)); + } + +} diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/session/TraceSessionRepository.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/session/TraceSessionRepository.java new file mode 100644 index 000000000..9a0a1bba0 --- /dev/null +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/session/TraceSessionRepository.java @@ -0,0 +1,87 @@ +/* + * 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.session; + +import java.util.function.Supplier; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.docs.AssertingSpan; +import org.springframework.session.Session; +import org.springframework.session.SessionRepository; + +class TraceSessionRepository implements SessionRepository { + + private static final Log log = LogFactory.getLog(TraceSessionRepository.class); + + private final SessionRepository delegate; + + protected final Tracer tracer; + + TraceSessionRepository(Tracer tracer, SessionRepository delegate) { + this.delegate = delegate; + this.tracer = tracer; + } + + @Override + public Session createSession() { + return wrap(SleuthSessionSpan.SESSION_CREATE_SPAN, (Supplier) this.delegate::createSession); + } + + private T wrap(SleuthSessionSpan sessionSpan, Supplier supplier) { + AssertingSpan span = newSpan(sessionSpan); + if (log.isDebugEnabled()) { + log.debug( + "Wrapping call in a span with name [" + span.getDocumentedSpan().getName() + "] - [" + span + "]"); + } + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { + return supplier.get(); + } + finally { + span.end(); + } + } + + private void wrap(SleuthSessionSpan sessionSpan, Runnable runnable) { + wrap(sessionSpan, () -> { + runnable.run(); + return null; + }); + } + + private AssertingSpan newSpan(SleuthSessionSpan sessionCreateSpan) { + return AssertingSpan.of(sessionCreateSpan, this.tracer.nextSpan()).name(sessionCreateSpan.getName()); + } + + @Override + public void save(Session session) { + wrap(SleuthSessionSpan.SESSION_SAVE_SPAN, () -> this.delegate.save(session)); + } + + @Override + public Session findById(String id) { + return wrap(SleuthSessionSpan.SESSION_FIND_SPAN, () -> this.delegate.findById(id)); + } + + @Override + public void deleteById(String id) { + wrap(SleuthSessionSpan.SESSION_DELETE_SPAN, () -> this.delegate.deleteById(id)); + } + +} diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/session/TraceSessionRepositoryAspect.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/session/TraceSessionRepositoryAspect.java new file mode 100644 index 000000000..32ab521b8 --- /dev/null +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/session/TraceSessionRepositoryAspect.java @@ -0,0 +1,143 @@ +/* + * 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.session; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; + +import org.springframework.cloud.sleuth.CurrentTraceContext; +import org.springframework.cloud.sleuth.Tracer; +import org.springframework.session.FindByIndexNameSessionRepository; +import org.springframework.session.ReactiveSessionRepository; +import org.springframework.session.SessionRepository; +import org.springframework.util.ReflectionUtils; + +/** + * Aspect around {@link SessionRepository} and {@link ReactiveSessionRepository} method + * execution. + * + * @author Marcin Grzejszczak + * @since 3.1.0 + */ +@Aspect +public class TraceSessionRepositoryAspect { + + private static final Log log = LogFactory.getLog(TraceSessionRepositoryAspect.class); + + private final Tracer tracer; + + private final CurrentTraceContext currentTraceContext; + + public TraceSessionRepositoryAspect(Tracer tracer, CurrentTraceContext currentTraceContext) { + this.tracer = tracer; + this.currentTraceContext = currentTraceContext; + } + + // RedisIndexedSessionRepository + @Around("execution(public * org.springframework.session.SessionRepository.*(..))") + public Object wrapSessionRepository(ProceedingJoinPoint pjp) throws Throwable { + SessionRepository target = (SessionRepository) pjp.getTarget(); + if (target instanceof TraceSessionRepository) { + return pjp.proceed(); + } + target = wrapSessionRepository(target); + return callMethodOnWrappedObject(pjp, target); + } + + private SessionRepository wrapSessionRepository(SessionRepository target) { + if (target instanceof FindByIndexNameSessionRepository) { + return new TraceFindByIndexNameSessionRepository(this.tracer, (FindByIndexNameSessionRepository) target); + } + return new TraceSessionRepository(this.tracer, target); + } + + private Object callMethodOnWrappedObject(ProceedingJoinPoint pjp, T target) throws Throwable { + Method method = getMethod(pjp, target); + if (method != null) { + if (log.isDebugEnabled()) { + log.debug("Found a corresponding method on the trace representation [" + method + "]"); + } + return method.invoke(target, pjp.getArgs()); + } + if (log.isDebugEnabled()) { + log.debug("Method [" + pjp.getSignature().getName() + + "] not found on the trace representation. Will run the original one."); + } + return pjp.proceed(); + } + + @Around("execution(public * org.springframework.session.ReactiveSessionRepository.*(..))") + public Object wrapReactiveSessionRepository(ProceedingJoinPoint pjp) throws Throwable { + ReactiveSessionRepository target = (ReactiveSessionRepository) pjp.getTarget(); + if (target instanceof TraceReactiveSessionRepository) { + return pjp.proceed(); + } + target = new TraceReactiveSessionRepository(this.tracer, this.currentTraceContext, target); + return callMethodOnWrappedObject(pjp, target); + } + + private Method getMethod(ProceedingJoinPoint pjp, Object tracingWrapper) { + MethodSignature signature = (MethodSignature) pjp.getSignature(); + Method method = signature.getMethod(); + Method foundMethodOnTracingWrapper = ReflectionUtils.findMethod(tracingWrapper.getClass(), method.getName(), + method.getParameterTypes()); + if (foundMethodOnTracingWrapper != null) { + if (log.isDebugEnabled()) { + log.debug("Found an exact match for method execution [" + foundMethodOnTracingWrapper + "]"); + } + return foundMethodOnTracingWrapper; + } + Method[] uniquePublicDeclaredMethodsOnTracingWrapper = ReflectionUtils + .getUniqueDeclaredMethods(tracingWrapper.getClass(), m -> Modifier.isPublic(m.getModifiers())); + if (uniquePublicDeclaredMethodsOnTracingWrapper.length == 0) { + return null; + } + if (log.isTraceEnabled()) { + log.trace("Will pick one of the unique declared methods [" + + Arrays.toString(uniquePublicDeclaredMethodsOnTracingWrapper) + "] that has a name [" + + method.getName() + "]"); + } + Object[] argsOnOriginalObject = pjp.getArgs(); + return Arrays.stream(uniquePublicDeclaredMethodsOnTracingWrapper) + .filter(m -> m.getName().equals(method.getName()) + && paramsAreOfSameTyperInherited(argsOnOriginalObject, m.getParameterTypes())) + .findFirst().orElse(null); + } + + private boolean paramsAreOfSameTyperInherited(Object[] argsOnOriginalObject, Class[] typeOnTracingWrapper) { + if (argsOnOriginalObject.length != typeOnTracingWrapper.length) { + return false; + } + for (int i = 0; i < argsOnOriginalObject.length; i++) { + Class argType = argsOnOriginalObject[i].getClass(); + Class typeOnWrapper = typeOnTracingWrapper[i]; + if (!typeOnWrapper.isAssignableFrom(argType)) { + return false; + } + } + return true; + } + +} diff --git a/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/session/TraceFindByIndexNameSessionRepositoryTests.java b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/session/TraceFindByIndexNameSessionRepositoryTests.java new file mode 100644 index 000000000..98f36b2a1 --- /dev/null +++ b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/session/TraceFindByIndexNameSessionRepositoryTests.java @@ -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.session; + +import org.junit.jupiter.api.Test; + +import org.springframework.cloud.sleuth.tracer.SimpleSpan; +import org.springframework.session.FindByIndexNameSessionRepository; +import org.springframework.session.SessionRepository; + +import static org.assertj.core.api.BDDAssertions.then; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +class TraceFindByIndexNameSessionRepositoryTests extends TraceSessionRepositoryTests { + + FindByIndexNameSessionRepository delegate = mock(FindByIndexNameSessionRepository.class); + + TraceFindByIndexNameSessionRepository traceSessionRepository = new TraceFindByIndexNameSessionRepository( + this.simpleTracer, this.delegate); + + @Test + void should_trace_session_find_by_principle_name() { + this.traceSessionRepository.findByPrincipalName("foo"); + + verify(this.delegate).findByPrincipalName(any()); + SimpleSpan lastSpan = this.simpleTracer.getLastSpan(); + then(lastSpan.name).isEqualTo(SleuthSessionSpan.SESSION_FIND_SPAN.getName()); + } + + @Test + void should_trace_session_find_by_index_name_value() { + this.traceSessionRepository.findByIndexNameAndIndexValue("indexName1", "indexValue1"); + + verify(this.delegate).findByIndexNameAndIndexValue(any(), any()); + SimpleSpan lastSpan = this.simpleTracer.getLastSpan(); + then(lastSpan.name).isEqualTo(SleuthSessionSpan.SESSION_FIND_SPAN.getName()); + then(lastSpan.tags).containsEntry(SleuthSessionSpan.Tags.INDEX_NAME.getKey(), "indexName1"); + } + + @Override + SessionRepository sessionRepositoryDelegate() { + return this.delegate; + } + + @Override + SessionRepository sessionRepository() { + return this.traceSessionRepository; + } + +} diff --git a/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/session/TraceReactiveSessionRepositoryTests.java b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/session/TraceReactiveSessionRepositoryTests.java new file mode 100644 index 000000000..64e22a363 --- /dev/null +++ b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/session/TraceReactiveSessionRepositoryTests.java @@ -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.session; + +import java.time.Duration; + +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Mono; + +import org.springframework.cloud.sleuth.tracer.SimpleCurrentTraceContext; +import org.springframework.cloud.sleuth.tracer.SimpleSpan; +import org.springframework.cloud.sleuth.tracer.SimpleTracer; +import org.springframework.session.ReactiveSessionRepository; + +import static org.assertj.core.api.BDDAssertions.then; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +class TraceReactiveSessionRepositoryTests { + + SimpleCurrentTraceContext simpleCurrentTraceContext = new SimpleCurrentTraceContext(); + + SimpleTracer simpleTracer = new SimpleTracer() { + @Override + public SimpleSpan nextSpan() { + SimpleSpan span = super.nextSpan(); + simpleCurrentTraceContext.traceContext = span.context(); + return span; + } + }; + + ReactiveSessionRepository delegate = mock(ReactiveSessionRepository.class); + + ReactiveSessionRepository traceSessionRepository = new TraceReactiveSessionRepository(this.simpleTracer, + this.simpleCurrentTraceContext, delegate); + + @Test + void should_trace_session_creation() { + given(this.delegate.createSession()).willReturn(Mono.empty()); + + this.traceSessionRepository.createSession().block(Duration.ofMillis(10)); + + verify(this.delegate).createSession(); + then(this.simpleTracer.getLastSpan().name).isEqualTo(SleuthSessionSpan.SESSION_CREATE_SPAN.getName()); + } + + @Test + void should_trace_session_save() { + given(this.delegate.save(any())).willReturn(Mono.empty()); + + this.traceSessionRepository.save(null).block(Duration.ofMillis(10)); + + verify(this.delegate).save(any()); + then(this.simpleTracer.getLastSpan().name).isEqualTo(SleuthSessionSpan.SESSION_SAVE_SPAN.getName()); + } + + @Test + void should_trace_session_find_by_id() { + given(this.delegate.findById(any())).willReturn(Mono.empty()); + + this.traceSessionRepository.findById("").block(Duration.ofMillis(10)); + + verify(this.delegate).findById(any()); + then(this.simpleTracer.getLastSpan().name).isEqualTo(SleuthSessionSpan.SESSION_FIND_SPAN.getName()); + } + + @Test + void should_trace_session_delete_by_id() { + given(this.delegate.deleteById(any())).willReturn(Mono.empty()); + + this.traceSessionRepository.deleteById("").block(Duration.ofMillis(10)); + + verify(this.delegate).deleteById(any()); + then(this.simpleTracer.getLastSpan().name).isEqualTo(SleuthSessionSpan.SESSION_DELETE_SPAN.getName()); + } + +} diff --git a/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/session/TraceSessionRepositoryTests.java b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/session/TraceSessionRepositoryTests.java new file mode 100644 index 000000000..dac9d30f2 --- /dev/null +++ b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/session/TraceSessionRepositoryTests.java @@ -0,0 +1,77 @@ +/* + * 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.session; + +import org.junit.jupiter.api.Test; + +import org.springframework.cloud.sleuth.tracer.SimpleTracer; +import org.springframework.session.SessionRepository; + +import static org.assertj.core.api.BDDAssertions.then; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +class TraceSessionRepositoryTests { + + SimpleTracer simpleTracer = new SimpleTracer(); + + SessionRepository delegate = mock(SessionRepository.class); + + SessionRepository traceSessionRepository = new TraceSessionRepository(this.simpleTracer, delegate); + + @Test + void should_trace_session_creation() { + sessionRepository().createSession(); + + verify(sessionRepositoryDelegate()).createSession(); + then(this.simpleTracer.getLastSpan().name).isEqualTo(SleuthSessionSpan.SESSION_CREATE_SPAN.getName()); + } + + @Test + void should_trace_session_save() { + sessionRepository().save(null); + + verify(sessionRepositoryDelegate()).save(any()); + then(this.simpleTracer.getLastSpan().name).isEqualTo(SleuthSessionSpan.SESSION_SAVE_SPAN.getName()); + } + + @Test + void should_trace_session_find_by_id() { + sessionRepository().findById(""); + + verify(sessionRepositoryDelegate()).findById(any()); + then(this.simpleTracer.getLastSpan().name).isEqualTo(SleuthSessionSpan.SESSION_FIND_SPAN.getName()); + } + + @Test + void should_trace_session_delete_by_id() { + sessionRepository().deleteById(""); + + verify(sessionRepositoryDelegate()).deleteById(any()); + then(this.simpleTracer.getLastSpan().name).isEqualTo(SleuthSessionSpan.SESSION_DELETE_SPAN.getName()); + } + + SessionRepository sessionRepository() { + return this.traceSessionRepository; + } + + SessionRepository sessionRepositoryDelegate() { + return this.delegate; + } + +} diff --git a/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/tracer/SimpleSpan.java b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/tracer/SimpleSpan.java index ac609ac27..c4213fd4b 100644 --- a/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/tracer/SimpleSpan.java +++ b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/tracer/SimpleSpan.java @@ -104,4 +104,11 @@ public class SimpleSpan implements Span { return this; } + @Override + public String toString() { + return "SimpleSpan{" + "tags=" + tags + ", started=" + started + ", ended=" + ended + ", throwable=" + throwable + + ", remoteServiceName='" + remoteServiceName + '\'' + ", spanKind=" + spanKind + ", events=" + events + + ", name='" + name + '\'' + '}'; + } + } diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/brave/baggage/MultipleHopsIntegrationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/brave/baggage/MultipleHopsIntegrationTests.java index f373f8588..300ff616a 100644 --- a/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/brave/baggage/MultipleHopsIntegrationTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/brave/baggage/MultipleHopsIntegrationTests.java @@ -22,6 +22,7 @@ import brave.sampler.Sampler; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration; @@ -74,8 +75,9 @@ public class MultipleHopsIntegrationTests } @Configuration(proxyBeanMethods = false) - @EnableAutoConfiguration(exclude = { MongoAutoConfiguration.class, QuartzAutoConfiguration.class, - JmxAutoConfiguration.class, R2dbcAutoConfiguration.class, R2dbcDataAutoConfiguration.class }) + @EnableAutoConfiguration( + exclude = { MongoAutoConfiguration.class, QuartzAutoConfiguration.class, JmxAutoConfiguration.class, + R2dbcAutoConfiguration.class, R2dbcDataAutoConfiguration.class, RedisAutoConfiguration.class }) static class Config { @Bean diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/JmsTracingConfigurationTest.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/JmsTracingConfigurationTest.java index b7d5b2105..f4a41ed5c 100644 --- a/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/JmsTracingConfigurationTest.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/JmsTracingConfigurationTest.java @@ -36,6 +36,7 @@ import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration; import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; @@ -168,8 +169,9 @@ public class JmsTracingConfigurationTest { } @Configuration(proxyBeanMethods = false) -@EnableAutoConfiguration(exclude = { KafkaAutoConfiguration.class, MongoAutoConfiguration.class, - QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class, R2dbcDataAutoConfiguration.class }) +@EnableAutoConfiguration( + exclude = { KafkaAutoConfiguration.class, MongoAutoConfiguration.class, QuartzAutoConfiguration.class, + R2dbcAutoConfiguration.class, R2dbcDataAutoConfiguration.class, RedisAutoConfiguration.class }) class JmsTestTracingConfiguration { } diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloSpringIntegration.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloSpringIntegration.java index 734e096d3..a51599cfd 100644 --- a/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloSpringIntegration.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloSpringIntegration.java @@ -23,6 +23,7 @@ import brave.test.TestSpanHandler; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration; @@ -33,7 +34,7 @@ import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.web.client.RestTemplate; @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, - R2dbcAutoConfiguration.class, R2dbcDataAutoConfiguration.class }) + R2dbcAutoConfiguration.class, R2dbcDataAutoConfiguration.class, RedisAutoConfiguration.class }) @ImportResource("classpath:beans/applicationContext.xml") @EnableIntegration @EnableAsync