[英]How can I throw an exception within an execution of a reactive pipeline that contains the stack trace of the creator of the reactive pipeline?
If I run the following reactive pipeline:如果我运行以下反应式管道:
Mono.empty()
.switchIfEmpty(Mono.defer(() -> Mono.error(new RuntimeException())))
.block();
I get the stack trace of the reactive step ( switchIfEmpty
);我得到了反应步骤的堆栈跟踪( switchIfEmpty
); but I am interested in the stack trace of the creator of the pipeline.但我对管道创建者的堆栈跟踪感兴趣。 The reactive stack trace portion is useless and I am actually interested in debugging the request that triggered the creation of the pipeline (which was sub-sequentially executed).反应式堆栈跟踪部分是无用的,我实际上对调试触发创建管道的请求(它是按顺序执行的)感兴趣。
Even when the stack trace of the creator of the pipeline is sometimes revealed below the reactive stack elements, it is not a guarantee since I have seen pipelines with too many unexpected bifurcations that have lost the pipeline creator stack trace at worst.即使管道创建者的堆栈跟踪有时会显示在反应堆栈元素下方,这也不能保证,因为我见过太多意外分叉的管道,最坏的情况是丢失了管道创建者堆栈跟踪。 The best case scenarios are that the stack trace of interest is so buried under stack elements that it makes debugging almost impossible.最好的情况是感兴趣的堆栈跟踪被隐藏在堆栈元素下,以至于几乎不可能进行调试。
Since the pipeline I am creating will be used to do a round trip request over a network, I am okay with a solution that demands paying a maximum performance price equivalent to 1 order of magnitude below the cost of doing a network request round trip.由于我创建的管道将用于通过网络执行往返请求,因此我可以接受一个解决方案,该解决方案要求支付的最大性能价格相当于执行网络请求往返成本的 1 个数量级。
The solution is quite simple:解决方案非常简单:
The following should work:以下应该有效:
Mono.empty()
.switchIfEmpty(Mono.error(new RuntimeException()))
.block();
This pipeline will be more expensive in performance terms;这条管道在性能方面会更加昂贵; but will respect the OP's requirement of not adding more than a fraction of what a network request roundtrip costs.但会尊重 OP 的要求,即不增加网络请求往返成本的一小部分。
The following test cases verify and illustrate (check out the sequence of print outs) the effectiveness of this technique to obtain the desired stack trace:以下测试用例验证并说明(检查打印输出的顺序)该技术获得所需堆栈跟踪的有效性:
import org.junit.jupiter.api.Test;
import java.io.PrintWriter;
import java.io.StringWriter;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
public class MonoSwitchIfEmptyTest {
@Test
void whenRequestThrowsPrecompiledException_thenExpectDesiredStackTrace() {
trySwitchIfEmptyWithDesiredStackTrace(createAppropriateSwitchIfEmptyMono(true), true);
}
@Test
void whenRequestThrowsLiveException_thenExpectUndesiredStackTrace() {
trySwitchIfEmptyWithDesiredStackTrace(createAppropriateSwitchIfEmptyMono(false), false);
}
// This is done here to force the creation of an extra stack element into the stack trace.
// That stack element is the one that later will be checked to determine if the test passed or not.
private Mono<Void> createAppropriateSwitchIfEmptyMono(boolean desiredStackTrace) {
return desiredStackTrace ?
Mono.error(new LoggedRuntimeException()) :
Mono.defer(() -> Mono.error(new LoggedRuntimeException()));
}
private void trySwitchIfEmptyWithDesiredStackTrace(Mono<Void> switchIfEmptyMono, boolean desiredStackTrace) {
Mono<Void> loggedError = Mono.fromRunnable(() -> System.out.println("Throwing exception..."))
.then(switchIfEmptyMono);
Mono<Object> testedPipeline = Mono.fromRunnable(() -> loggedBusyWork(1000))
.switchIfEmpty(loggedError);
StepVerifier.create(testedPipeline)
.expectErrorMatches(e -> ((LoggedRuntimeException)e).isRelevantStackTrace() == desiredStackTrace)
.verify();
}
private void loggedBusyWork(int millis) {
long startTime = System.currentTimeMillis();
System.out.println("Starting busy work @ " + startTime + "...");
while (System.currentTimeMillis() - startTime < millis);
System.out.println("End busy work @ " + System.currentTimeMillis());
}
static class LoggedRuntimeException extends RuntimeException {
public LoggedRuntimeException() {
System.out.println("Creating exception...");
String stackTrace = getStackTraceStr();
System.out.println("Stack trace: \n" + stackTrace);
}
private String getStackTraceStr() {
StringWriter writer = new StringWriter();
printStackTrace(new PrintWriter(writer));
return writer.toString();
}
public boolean isRelevantStackTrace() {
return getStackTrace()[1].toString().contains(MonoSwitchIfEmptyTest.class.getName());
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.