Skip to content

Commit 4da42c0

Browse files
committed
Configure automatic context propagation for Reactor
This commit adds a new configuration property, `spring.reactor.context-propagation` that configures the context propagation mode for Reactor operators. By default the value is set to "AUTO" for reinstating automatically context values as ThreadLocals within Reactor operators. The "LIMITED" mode restricts this feature ot the "tap" and "handle" operators but has a slightly lower footprint. Closes gh-34201
1 parent bf15797 commit 4da42c0

File tree

7 files changed

+190
-0
lines changed

7 files changed

+190
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.reactor;
18+
19+
import reactor.core.publisher.Hooks;
20+
21+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
22+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
23+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
24+
import org.springframework.context.annotation.Configuration;
25+
26+
/**
27+
* {@link EnableAutoConfiguration Auto-configuration} for Reactor.
28+
*
29+
* @author Brian Clozel
30+
* @since 3.0.2
31+
*/
32+
@Configuration(proxyBeanMethods = false)
33+
@ConditionalOnClass(Hooks.class)
34+
@EnableConfigurationProperties(ReactorProperties.class)
35+
public class ReactorAutoConfiguration {
36+
37+
public ReactorAutoConfiguration(ReactorProperties properties) {
38+
if (properties.getContextPropagation() == ReactorProperties.ContextPropagationMode.AUTO) {
39+
Hooks.enableAutomaticContextPropagation();
40+
}
41+
}
42+
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.reactor;
18+
19+
import org.springframework.boot.context.properties.ConfigurationProperties;
20+
21+
/**
22+
* Configuration properties for Reactor.
23+
*
24+
* @author Brian Clozel
25+
* @since 3.0.3
26+
*/
27+
@ConfigurationProperties(prefix = "spring.reactor")
28+
public class ReactorProperties {
29+
30+
/**
31+
* Context Propagation support mode for Reactor operators.
32+
*/
33+
private ContextPropagationMode contextPropagation = ContextPropagationMode.AUTO;
34+
35+
public ContextPropagationMode getContextPropagation() {
36+
return this.contextPropagation;
37+
}
38+
39+
public void setContextPropagation(ContextPropagationMode contextPropagation) {
40+
this.contextPropagation = contextPropagation;
41+
}
42+
43+
public enum ContextPropagationMode {
44+
45+
/**
46+
* Context Propagation is applied to all Reactor operators.
47+
*/
48+
AUTO,
49+
50+
/**
51+
* Context Propagation is only applied to "tap" and "handle" Reactor operators.
52+
*/
53+
LIMITED
54+
55+
}
56+
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* Auto-configuration for Reactor.
19+
*/
20+
package org.springframework.boot.autoconfigure.reactor;

spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2176,6 +2176,10 @@
21762176
"level": "error"
21772177
}
21782178
},
2179+
{
2180+
"name": "spring.reactor.context-propagation",
2181+
"defaultValue": "auto"
2182+
},
21792183
{
21802184
"name": "spring.reactor.stacktrace-mode.enabled",
21812185
"description": "Whether Reactor should collect stacktrace information at runtime.",

spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration
9393
org.springframework.boot.autoconfigure.netty.NettyAutoConfiguration
9494
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
9595
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration
96+
org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration
9697
org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration
9798
org.springframework.boot.autoconfigure.r2dbc.R2dbcTransactionManagerAutoConfiguration
9899
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.reactor;
18+
19+
import java.util.concurrent.atomic.AtomicReference;
20+
21+
import io.micrometer.context.ContextRegistry;
22+
import org.junit.jupiter.api.BeforeAll;
23+
import org.junit.jupiter.api.Test;
24+
import reactor.core.publisher.Mono;
25+
import reactor.util.context.Context;
26+
27+
import org.springframework.boot.autoconfigure.AutoConfigurations;
28+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
29+
30+
import static org.assertj.core.api.Assertions.assertThat;
31+
32+
/**
33+
* Tests for {@link ReactorAutoConfiguration}.
34+
*
35+
* @author Brian Clozel
36+
*/
37+
class ReactorAutoConfigurationTests {
38+
39+
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
40+
.withConfiguration(AutoConfigurations.of(ReactorAutoConfiguration.class));
41+
42+
private static final String THREADLOCAL_KEY = "ReactorAutoConfigurationTests";
43+
44+
private static final ThreadLocal<String> THREADLOCAL_VALUE = ThreadLocal.withInitial(() -> "failure");
45+
46+
@BeforeAll
47+
static void initializeThreadLocalAccessors() {
48+
ContextRegistry globalRegistry = ContextRegistry.getInstance();
49+
globalRegistry.registerThreadLocalAccessor(THREADLOCAL_KEY, THREADLOCAL_VALUE);
50+
}
51+
52+
@Test
53+
void shouldConfigureAutomaticContextPropagation() {
54+
AtomicReference<String> threadLocalValue = new AtomicReference<>();
55+
this.contextRunner.run((applicationContext) -> {
56+
Mono.just("test").doOnNext((element) -> threadLocalValue.set(THREADLOCAL_VALUE.get()))
57+
.contextWrite(Context.of(THREADLOCAL_KEY, "success")).block();
58+
assertThat(threadLocalValue.get()).isEqualTo("success");
59+
});
60+
}
61+
62+
}

spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/observability.adoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,7 @@ For JDBC, the https://github.com/jdbc-observations/datasource-micrometer[Datasou
2121
Read more about it https://jdbc-observations.github.io/datasource-micrometer/docs/current/docs/html/[in the reference documentation].
2222
For R2DBC, the https://github.com/spring-projects-experimental/r2dbc-micrometer-spring-boot[Spring Boot Auto Configuration for R2DBC Observation] creates observations for R2DBC query invocations.
2323

24+
Observability support relies on the https://github.com/micrometer-metrics/context-propagation[Context Propagation library] for forwarding the current observation across threads and reactive pipelines.
25+
`ThreadLocal` values are automatically reinstated in reactive operators, this behavior is controlled with the configprop:spring.reactor.context-propagation[] property.
26+
2427
The next sections will provide more details about logging, metrics and traces.

0 commit comments

Comments
 (0)