简体   繁体   English

如何在包含反应式管道创建者的堆栈跟踪的反应式管道的执行中引发异常?

[英]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:解决方案非常简单:

  1. Create the exception at the same time of creating the pipeline;在创建管道的同时创建异常; but do not throw it.但不要扔。 That exception will be constructed with the stack trace of interest.该异常将使用感兴趣的堆栈跟踪构造。
  2. Build the pipeline using the constructed exception to be used if needed.如果需要,使用构造的异常构建管道。

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());
        }
    }
}

Complete code on GitHub GitHub 上的完整代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 通过异常 VS 传播错误对象中断反应式管道 - Interrupting the reactive pipeline via exception VS propagating error object 如何在不丢失数据的情况下构建具有不同发布者的反应式管道? - How to build a reactive pipeline with different Publishers without losing data? 是否可以在没有堆栈跟踪的情况下抛出自定义异常? - Is it possible to throw a custom Exception without stack trace? Java 7:在没有堆栈跟踪的情况下抛出异常 - Java 7: throw exception without stack trace 我如何打印异常堆栈跟踪 - How do I print the exception stack trace 如何获得此异常的堆栈跟踪? - How do I obtain a stack trace for this exception? 如何从另一个管道中排出管道? - How do I drain a pipeline from within another pipeline? Spring 集成:如何按需使用 QueueChannel 作为背压感知反应 (Flux) 管道 - Spring Integration: How to consume QueueChannel on-demand as backpressure aware reactive (Flux) pipeline 如何返回包含 Reactive Mono 和 Flux 的 Reactive Flux? - How to return a Reactive Flux that contains a Reactive Mono and Flux? 作为if语句的结果,我如何在另一个反应流中使用一个反应流 - How can I use one reactive stream inside another reactive stream as result of if statement
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM