mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-10-02 15:29:05 +00:00
Add FailureAnalyzedException support
Add `FailureAnalyzedException` to reduce the number of internal `FailureAnalizers` required when only a simple "description" and "action" are required. Closes gh-49320
This commit is contained in:
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2012-present 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.diagnostics;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* {@link RuntimeException} that includes a {@link FailureAnalysis}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public class FailureAnalyzedException extends RuntimeException {
|
||||
|
||||
private final @Nullable String action;
|
||||
|
||||
/**
|
||||
* Create a new {@link FailureAnalyzedException} instance.
|
||||
* @param description the {@link FailureAnalysis} description
|
||||
* @param action the {@link FailureAnalysis} action
|
||||
*/
|
||||
public FailureAnalyzedException(String description, @Nullable String action) {
|
||||
super(description);
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link FailureAnalyzedException} instance.
|
||||
* @param description the {@link FailureAnalysis} description
|
||||
* @param action the {@link FailureAnalysis} action
|
||||
* @param cause the cause of the exception
|
||||
*/
|
||||
public FailureAnalyzedException(String description, @Nullable String action, Throwable cause) {
|
||||
super(description, cause);
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link FailureAnalysis} to use for this exception.
|
||||
* @return the failure analysis
|
||||
*/
|
||||
public FailureAnalysis analysis() {
|
||||
return new FailureAnalysis(getMessage(), this.action, this);
|
||||
}
|
||||
|
||||
static @Nullable FailureAnalysis analyze(Throwable failure) {
|
||||
while (failure != null) {
|
||||
if (failure instanceof FailureAnalyzedException failureAnalyzedException) {
|
||||
return failureAnalyzedException.analysis();
|
||||
}
|
||||
failure = failure.getCause();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+6
-2
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.boot.diagnostics;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -64,8 +66,10 @@ final class FailureAnalyzers implements SpringBootExceptionReporter {
|
||||
|
||||
private static List<FailureAnalyzer> loadFailureAnalyzers(@Nullable ConfigurableApplicationContext context,
|
||||
SpringFactoriesLoader springFactoriesLoader) {
|
||||
return springFactoriesLoader.load(FailureAnalyzer.class, getArgumentResolver(context),
|
||||
FailureHandler.logging(logger));
|
||||
List<FailureAnalyzer> analyzers = new ArrayList<>(springFactoriesLoader.load(FailureAnalyzer.class,
|
||||
getArgumentResolver(context), FailureHandler.logging(logger)));
|
||||
analyzers.add(FailureAnalyzedException::analyze);
|
||||
return Collections.unmodifiableList(analyzers);
|
||||
}
|
||||
|
||||
private static @Nullable ArgumentResolver getArgumentResolver(@Nullable ConfigurableApplicationContext context) {
|
||||
|
||||
+49
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.boot.diagnostics;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -47,6 +50,8 @@ class FailureAnalyzersTests {
|
||||
|
||||
private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
|
||||
private final List<FailureAnalysis> reports = new ArrayList<>();
|
||||
|
||||
@BeforeEach
|
||||
void configureMock() {
|
||||
failureAnalyzer = mock(FailureAnalyzer.class);
|
||||
@@ -87,6 +92,37 @@ class FailureAnalyzersTests {
|
||||
then(failureAnalyzer).should().analyze(failure);
|
||||
}
|
||||
|
||||
@Test
|
||||
void failureAnalyzedExceptionAreReported() {
|
||||
FailureAnalyzedException failure = new FailureAnalyzedException("Bad", "Fix it!");
|
||||
analyzeAndReport(failure);
|
||||
assertThat(this.reports).hasSize(1);
|
||||
FailureAnalysis report = this.reports.get(0);
|
||||
assertThat(report.getDescription()).isEqualTo("Bad");
|
||||
assertThat(report.getAction()).isEqualTo("Fix it!");
|
||||
assertThat(report.getCause()).isSameAs(failure);
|
||||
}
|
||||
|
||||
@Test
|
||||
void wrappedFailureAnalyzedExceptionAreReported() {
|
||||
FailureAnalyzedException failure = new FailureAnalyzedException("Bad", "Fix it!");
|
||||
analyzeAndReport(new IllegalStateException("the state of this!", failure));
|
||||
assertThat(this.reports).hasSize(1);
|
||||
FailureAnalysis report = this.reports.get(0);
|
||||
assertThat(report.getDescription()).isEqualTo("Bad");
|
||||
assertThat(report.getAction()).isEqualTo("Fix it!");
|
||||
assertThat(report.getCause()).isSameAs(failure);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenOtherAnalyzerIsAvailableFailureAnalyzedExceptionAreNotReported() {
|
||||
FailureAnalyzedException failure = new FailureAnalyzedException("Bad", "Fix it!");
|
||||
analyzeAndReport(new IllegalStateException("the state of this!", failure), IllegalStateFailureAnalyzer.class);
|
||||
assertThat(this.reports).hasSize(1);
|
||||
FailureAnalysis report = this.reports.get(0);
|
||||
assertThat(report.getDescription()).isEqualTo("analyzed state");
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private void analyzeAndReport(Throwable failure, Class<? extends FailureAnalyzer>... failureAnalyzerClasses) {
|
||||
analyzeAndReport(failure, this.context, failureAnalyzerClasses);
|
||||
@@ -99,6 +135,7 @@ class FailureAnalyzersTests {
|
||||
for (Class<? extends FailureAnalyzer> failureAnalyzerClass : failureAnalyzerClasses) {
|
||||
loader.add(FailureAnalyzer.class, failureAnalyzerClass);
|
||||
}
|
||||
loader.addInstance(FailureAnalysisReporter.class, this.reports::add);
|
||||
new FailureAnalyzers(context, loader).reportException(failure);
|
||||
}
|
||||
|
||||
@@ -151,4 +188,16 @@ class FailureAnalyzersTests {
|
||||
|
||||
}
|
||||
|
||||
static class IllegalStateFailureAnalyzer implements FailureAnalyzer {
|
||||
|
||||
@Override
|
||||
public @Nullable FailureAnalysis analyze(Throwable failure) {
|
||||
if (failure instanceof IllegalStateException) {
|
||||
return new FailureAnalysis("analyzed state", null, failure);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user