获取返回 Mono.empty() 的 Mono/Flux 的行号
Getting the line number of the Mono/Flux that returned Mono.empty()
假设我有一长串 Monos。链中的某些单声道可能 return Mono.empty()
.
我可以用 switchIfEmpty
恢复,但我想知道哪个单声道引发了空(也许这样我就可以知道在哪里添加更智能的空处理)。
有没有办法以编程方式获取此信息?
愚蠢的例子。在我 return how did I get here?
的情况下,我如何知道第一个 flatMap
或第二个 flatMap
是否触发了空处理程序?
Mono.just("data")
.flatMap(t -> {
if (System.currentTimeMillis() % 2 == 0) {
return Mono.empty();
}
return Mono.just("happy1");
})
.flatMap(t -> {
if (System.currentTimeMillis() % 2 == 0) {
return Mono.empty();
}
return Mono.just("happy2");
})
.map(s -> {
return "successful complete: " + s;
})
.switchIfEmpty(Mono.fromCallable(() -> {
return "how did I get here?";
}))
.block();
由于 Flux
和 Mono
的动态特性,以及 onComplete
信号被认为足够中性,通常只是通过,所以没有通用解决方案。
在您的特定示例中,您可以将 Mono.empty()
替换为 Mono.empty().doOnComplete(() -> /* log something */)
之类的内容。
您甚至可以直接在 if 块中执行日志记录,但装饰空技巧可能适用于更多情况。
另一种可能性是将空虚变成错误,而不是打开 onComplete 信号。
错误不那么中性,因此有一些方法可以丰富它们以用于调试目的。例如,在每个 flatMap 之后使用 .checkpoint("flatMapX")
语句,您将获得额外的堆栈跟踪部分,这些部分将指向由于空而失败的 flatMap。
在 Mono
中将空虚变成错误的一种方法是 .single()
,它将强制执行一个 onNext()
或传播 onError(NoSuchElementException)
.
使用此技巧要记住的一件事是 checkpoint
的位置很重要:它必须在 single()
之后,以便从 single() 引发的错误得到检测和丰富.
因此,如果我以您的代码段为基础:
static final String PARSEABLE_MARKER = "PARSEABLE MARKER: <";
static final char MARKER_END = '>';
String parseLocation(Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
String trace = sw.toString();
int start = trace.indexOf(PARSEABLE_MARKER);
if (start > 0) {
trace = trace.substring(start + PARSEABLE_MARKER.length());
trace = trace.substring(0, trace.indexOf(MARKER_END));
return trace;
}
return "I don't know";
}
String testInner() {
Random random = new Random();
final boolean first = random.nextBoolean();
return Mono.just("data")
.flatMap(t -> {
if (System.currentTimeMillis() % 2 == 0 && first) {
return Mono.empty();
}
return Mono.just("happy1");
})
.single()
.checkpoint(PARSEABLE_MARKER + "the first flatMap" + MARKER_END)
.flatMap(t -> {
if (System.currentTimeMillis() % 2 == 0 && !first) {
return Mono.empty();
}
return Mono.just("happy2");
})
.single()
.checkpoint(PARSEABLE_MARKER + "the second flatMap" + MARKER_END)
.map(s -> {
return "successful complete: " + s;
})
.onErrorResume(NoSuchElementException.class, e ->
Mono.just("how did I get here? " + parseLocation(e)))
.block();
}
这可以是 运行 在一个测试循环中,例如:
@Test
void test() {
int successCount = 0;
int firstCount = 0;
int secondCount = 0;
for (int i = 0; i < 100; i++) {
String message = testInner();
if (message.startsWith("how")) {
if (message.contains("first")) {
firstCount++;
}
else if (message.contains("second")) {
secondCount++;
}
else {
System.out.println(message);
}
}
else {
successCount++;
}
}
System.out.printf("Stats: %d successful, %d detected first, %d detected second", successCount, firstCount, secondCount);
}
打印如下内容:
Stats: 85 successful, 5 detected first, 10 detected second
假设我有一长串 Monos。链中的某些单声道可能 return Mono.empty()
.
我可以用 switchIfEmpty
恢复,但我想知道哪个单声道引发了空(也许这样我就可以知道在哪里添加更智能的空处理)。
有没有办法以编程方式获取此信息?
愚蠢的例子。在我 return how did I get here?
的情况下,我如何知道第一个 flatMap
或第二个 flatMap
是否触发了空处理程序?
Mono.just("data")
.flatMap(t -> {
if (System.currentTimeMillis() % 2 == 0) {
return Mono.empty();
}
return Mono.just("happy1");
})
.flatMap(t -> {
if (System.currentTimeMillis() % 2 == 0) {
return Mono.empty();
}
return Mono.just("happy2");
})
.map(s -> {
return "successful complete: " + s;
})
.switchIfEmpty(Mono.fromCallable(() -> {
return "how did I get here?";
}))
.block();
由于 Flux
和 Mono
的动态特性,以及 onComplete
信号被认为足够中性,通常只是通过,所以没有通用解决方案。
在您的特定示例中,您可以将 Mono.empty()
替换为 Mono.empty().doOnComplete(() -> /* log something */)
之类的内容。
您甚至可以直接在 if 块中执行日志记录,但装饰空技巧可能适用于更多情况。
另一种可能性是将空虚变成错误,而不是打开 onComplete 信号。
错误不那么中性,因此有一些方法可以丰富它们以用于调试目的。例如,在每个 flatMap 之后使用 .checkpoint("flatMapX")
语句,您将获得额外的堆栈跟踪部分,这些部分将指向由于空而失败的 flatMap。
在 Mono
中将空虚变成错误的一种方法是 .single()
,它将强制执行一个 onNext()
或传播 onError(NoSuchElementException)
.
使用此技巧要记住的一件事是 checkpoint
的位置很重要:它必须在 single()
之后,以便从 single() 引发的错误得到检测和丰富.
因此,如果我以您的代码段为基础:
static final String PARSEABLE_MARKER = "PARSEABLE MARKER: <";
static final char MARKER_END = '>';
String parseLocation(Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
String trace = sw.toString();
int start = trace.indexOf(PARSEABLE_MARKER);
if (start > 0) {
trace = trace.substring(start + PARSEABLE_MARKER.length());
trace = trace.substring(0, trace.indexOf(MARKER_END));
return trace;
}
return "I don't know";
}
String testInner() {
Random random = new Random();
final boolean first = random.nextBoolean();
return Mono.just("data")
.flatMap(t -> {
if (System.currentTimeMillis() % 2 == 0 && first) {
return Mono.empty();
}
return Mono.just("happy1");
})
.single()
.checkpoint(PARSEABLE_MARKER + "the first flatMap" + MARKER_END)
.flatMap(t -> {
if (System.currentTimeMillis() % 2 == 0 && !first) {
return Mono.empty();
}
return Mono.just("happy2");
})
.single()
.checkpoint(PARSEABLE_MARKER + "the second flatMap" + MARKER_END)
.map(s -> {
return "successful complete: " + s;
})
.onErrorResume(NoSuchElementException.class, e ->
Mono.just("how did I get here? " + parseLocation(e)))
.block();
}
这可以是 运行 在一个测试循环中,例如:
@Test
void test() {
int successCount = 0;
int firstCount = 0;
int secondCount = 0;
for (int i = 0; i < 100; i++) {
String message = testInner();
if (message.startsWith("how")) {
if (message.contains("first")) {
firstCount++;
}
else if (message.contains("second")) {
secondCount++;
}
else {
System.out.println(message);
}
}
else {
successCount++;
}
}
System.out.printf("Stats: %d successful, %d detected first, %d detected second", successCount, firstCount, secondCount);
}
打印如下内容:
Stats: 85 successful, 5 detected first, 10 detected second