diff --git a/agent/src/main/java/reactor/blockhound/BlockHound.java b/agent/src/main/java/reactor/blockhound/BlockHound.java index 9a3df87..791d0e5 100644 --- a/agent/src/main/java/reactor/blockhound/BlockHound.java +++ b/agent/src/main/java/reactor/blockhound/BlockHound.java @@ -193,7 +193,26 @@ public static class Builder { }}; private Consumer onBlockingMethod = method -> { - throw new BlockingOperationError(method); + Error error = new BlockingOperationError(method); + + // Strip BlockHound's internal noisy frames from the stacktrace to not mislead the users + StackTraceElement[] stackTrace = error.getStackTrace(); + int length = stackTrace.length; + for (int i = 0; i < length; i++) { + StackTraceElement stackTraceElement = stackTrace[i]; + if (!BlockHoundRuntime.class.getName().equals(stackTraceElement.getClassName())) { + continue; + } + + if ("checkBlocking".equals(stackTraceElement.getMethodName())) { + if (i + 1 < length) { + error.setStackTrace(Arrays.copyOfRange(stackTrace, i + 1, length)); + } + break; + } + } + + throw error; }; private Predicate threadPredicate = t -> false; diff --git a/example/src/test/java/com/example/StackTraceTest.java b/example/src/test/java/com/example/StackTraceTest.java new file mode 100644 index 0000000..2641740 --- /dev/null +++ b/example/src/test/java/com/example/StackTraceTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019-Present Pivotal Software Inc, All Rights Reserved. + * + * 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 com.example; + +import org.assertj.core.api.Assertions; +import org.junit.Test; +import reactor.blockhound.BlockHound; +import reactor.blockhound.BlockingOperationError; +import reactor.core.scheduler.NonBlocking; + +import java.util.concurrent.CompletableFuture; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +public class StackTraceTest { + + static { + BlockHound.install(); + } + + @Test + public void shouldHideInternalFrames() { + CompletableFuture future = new CompletableFuture<>(); + class TestThread extends Thread implements NonBlocking { + @Override + public void run() { + try { + Thread.sleep(0); + future.complete(null); + } + catch (Throwable e) { + future.completeExceptionally(e); + } + } + } + new TestThread().start(); + + assertThat(Assertions.catchThrowable(future::join)) + .as("exception") + .isNotNull() + .hasCauseInstanceOf(BlockingOperationError.class) + .satisfies(e -> { + assertThat(e.getCause().getStackTrace()) + .as("Cause's stacktrace") + .extracting(StackTraceElement::getClassName, StackTraceElement::getMethodName) + .containsExactly( + tuple("java.lang.Thread", "sleep"), + tuple(TestThread.class.getName(), "run") + ); + }); + } +}