Add support for reactor-tools debug agent
The `reactor-tools` dependency now brings a new Reactor Debug Agent which instruments loaded classes for better Reactor stacktraces. This commit removes the `spring.reactor.stacktrace-mode.enabled` configuration property since the related Reactor Hook is about to be removed. As a replacement, we're introducing `spring.reactor.debug-agent.enabled` which tells whether the Reactor Debug Agent should be loaded, given that the `reactor-tools` dependency is available. This option is enabled by default, since adding the dependency on classpath is a strong signal already. Fixes gh-17128
This commit is contained in:
parent
b1a3849b27
commit
3c28622ebc
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2019 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.boot.autoconfigure.reactor.core;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Hooks;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Reactor Core.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Eddú Meléndez
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ Mono.class, Flux.class })
|
||||
@EnableConfigurationProperties(ReactorCoreProperties.class)
|
||||
public class ReactorCoreAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
protected void initialize(ReactorCoreProperties properties) {
|
||||
if (properties.isDebug()) {
|
||||
Hooks.onOperatorDebug();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2019 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.boot.autoconfigure.reactor.core;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
|
||||
|
||||
/**
|
||||
* Properties for Reactor Core.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.reactor")
|
||||
public class ReactorCoreProperties {
|
||||
|
||||
/**
|
||||
* Whether Reactor should collect stacktrace information at runtime.
|
||||
*/
|
||||
private boolean debug;
|
||||
|
||||
private final StacktraceMode stacktraceMode = new StacktraceMode();
|
||||
|
||||
public boolean isDebug() {
|
||||
return this.debug;
|
||||
}
|
||||
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
public StacktraceMode getStacktraceMode() {
|
||||
return this.stacktraceMode;
|
||||
}
|
||||
|
||||
public class StacktraceMode {
|
||||
|
||||
@DeprecatedConfigurationProperty(replacement = "spring.reactor.debug")
|
||||
@Deprecated
|
||||
public boolean isEnabled() {
|
||||
return isDebug();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setEnabled(boolean enabled) {
|
||||
setDebug(enabled);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -775,6 +775,14 @@
|
|||
"name": "spring.rabbitmq.listener.type",
|
||||
"defaultValue": "simple"
|
||||
},
|
||||
{
|
||||
"name": "spring.reactor.stacktrace-mode.enabled",
|
||||
"description": "Whether Reactor should collect stacktrace information at runtime.",
|
||||
"defaultValue": false,
|
||||
"deprecation": {
|
||||
"replacement": "spring.reactor.debug-agent.enabled"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spring.security.filter.dispatcher-types",
|
||||
"defaultValue": [
|
||||
|
|
|
@ -100,7 +100,6 @@ org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
|
|||
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2019 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.boot.autoconfigure.reactor.core;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Hooks;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.context.runner.ContextConsumer;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReactorCoreAutoConfiguration}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ReactorCoreAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(ReactorCoreAutoConfiguration.class));
|
||||
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void resetDebugFlag() {
|
||||
Hooks.resetOnOperatorDebug();
|
||||
}
|
||||
|
||||
@Test
|
||||
void debugOperatorIsDisabledByDefault() {
|
||||
this.contextRunner.run(assertDebugOperator(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void debugOperatorIsSetWithProperty() {
|
||||
this.contextRunner.withPropertyValues("spring.reactor.debug=true").run(assertDebugOperator(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated
|
||||
void debugOperatorIsSetWithDeprecatedProperty() {
|
||||
this.contextRunner.withPropertyValues("spring.reactor.stacktrace-mode.enabled=true")
|
||||
.run(assertDebugOperator(true));
|
||||
}
|
||||
|
||||
private ContextConsumer<AssertableApplicationContext> assertDebugOperator(boolean expected) {
|
||||
return (context) -> assertThat(ReflectionTestUtils.getField(Hooks.class, "GLOBAL_TRACE")).isEqualTo(expected);
|
||||
}
|
||||
|
||||
}
|
|
@ -76,6 +76,11 @@
|
|||
<artifactId>reactor-netty</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-tools</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-tcnative-boringssl-static</artifactId>
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2012-2019 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.boot.reactor;
|
||||
|
||||
import reactor.tools.agent.ReactorDebugAgent;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link EnvironmentPostProcessor} to enable the Reactor Debug Agent if available.
|
||||
* <p>
|
||||
* The debug agent is enabled by default, unless the
|
||||
* {@code "spring.reactor.debug-agent.enabled"} configuration property is set to false. We
|
||||
* are using here an {@link EnvironmentPostProcessor} instead of an auto-configuration
|
||||
* class to enable the agent as soon as possible during the startup process.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public class DebugAgentEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
|
||||
|
||||
private static final String REACTOR_DEBUGAGENT_CLASS = "reactor.tools.agent.ReactorDebugAgent";
|
||||
|
||||
private static final String DEBUGAGENT_ENABLED_CONFIG_KEY = "spring.reactor.debug-agent.enabled";
|
||||
|
||||
@Override
|
||||
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
|
||||
if (ClassUtils.isPresent(REACTOR_DEBUGAGENT_CLASS, null)) {
|
||||
Boolean agentEnabled = environment.getProperty(DEBUGAGENT_ENABLED_CONFIG_KEY, Boolean.class);
|
||||
if (agentEnabled != Boolean.FALSE) {
|
||||
ReactorDebugAgent.init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
}
|
|
@ -719,6 +719,13 @@
|
|||
"sourceType": "org.springframework.boot.context.config.ConfigFileApplicationListener",
|
||||
"description": "Unconditionally activate the specified comma-separated list of profiles (or list of profiles if using YAML)."
|
||||
},
|
||||
{
|
||||
"name": "spring.reactor.debug-agent.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"sourceType": "org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor",
|
||||
"description": "Whether the Reactor Debug Agent should be enabled when reactor-tools is present.",
|
||||
"defaultValue": true
|
||||
},
|
||||
{
|
||||
"name": "trace",
|
||||
"type": "java.lang.Boolean",
|
||||
|
|
|
@ -34,7 +34,8 @@ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
|
|||
org.springframework.boot.env.EnvironmentPostProcessor=\
|
||||
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
|
||||
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
|
||||
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
|
||||
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
|
||||
org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
|
||||
|
||||
# Failure Analyzers
|
||||
org.springframework.boot.diagnostics.FailureAnalyzer=\
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2012-2019 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.boot.reactor;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.Scannable;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link DebugAgentEnvironmentPostProcessor}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class DebugAgentEnvironmentPostProcessorTests {
|
||||
|
||||
static {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
DebugAgentEnvironmentPostProcessor postProcessor = new DebugAgentEnvironmentPostProcessor();
|
||||
postProcessor.postProcessEnvironment(environment, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void enablesReactorDebugAgent() {
|
||||
InstrumentedFluxProvider fluxProvider = new InstrumentedFluxProvider();
|
||||
Flux<Integer> flux = fluxProvider.newFluxJust();
|
||||
assertThat(Scannable.from(flux).stepName())
|
||||
.startsWith("Flux.just ⇢ at org.springframework.boot.reactor.InstrumentedFluxProvider.newFluxJust");
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
* Copyright 2012-2019 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.
|
||||
|
@ -14,7 +14,20 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.reactor;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* Auto-configuration for Reactor Core.
|
||||
* Utility class that should be instrumented by the reactor debug agent.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @see DebugAgentEnvironmentPostProcessorTests
|
||||
*/
|
||||
package org.springframework.boot.autoconfigure.reactor.core;
|
||||
class InstrumentedFluxProvider {
|
||||
|
||||
Flux<Integer> newFluxJust() {
|
||||
return Flux.just(1);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue