Skip to content

Latest commit

 

History

History
112 lines (91 loc) · 3.88 KB

customization.md

File metadata and controls

112 lines (91 loc) · 3.88 KB

Customization

BlockHound provides three means of usage:

  1. BlockHound.install() - will use ServiceLoader to load all known reactor.blockhound.integration.BlockHoundIntegrations
  2. BlockHound.install(BlockHoundIntegration... integrations) - same as BlockHound.install(), but adds user-provided integrations to the list.
  3. BlockHound.builder().install() - will create a new builder, without discovering any integrations.
    You may install them manually by using BlockHound.builder().with(new MyIntegration()).install().

Marking more methods as blocking

  • Builder#markAsBlocking(Class clazz, String methodName, String signature)
  • Builder#markAsBlocking(String className, String methodName, String signature)

Example:

builder.markAsBlocking("com.example.NativeHelper", "doSomethingBlocking", "(I)V");

Note that the signature argument is JVM's notation for the method signature.

(Dis-)allowing blocking calls inside methods

  • Builder#allowBlockingCallsInside(String className, String methodName)
  • Builder#disallowBlockingCallsInside(String className, String methodName)

Example:

This will allow blocking method calls inside Logger#callAppenders down the callstack:

builder.allowBlockingCallsInside(
    "ch.qos.logback.classic.Logger",
    "callAppenders"
);

While this disallows blocking calls unless there is an allowed method down the callstack:

builder.disallowBlockingCallsInside(
    "reactor.core.publisher.Flux",
    "subscribe"
);

Example using Allow/Disalow

The below example demonstrates how to allow the NonBlockingClass.outer() method to block, but not the NonBlockingClass.inner() method, which is called by the outer() method:

public class BlockingDisallowTest {

    static {
        BlockHound.install(b -> b
                .allowBlockingCallsInside(NonBlockingClass.class.getName(), "outer")
                .disallowBlockingCallsInside(NonBlockingClass.class.getName(), "inner")
        );
    }

    static class NonBlockingClass {

        String inner() {
            try {
                //if this trips BlockHound, the test fails (inner not in the stacktrace)
                Thread.sleep(50);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "example";
        }

        String outer() {
            try {
                Thread.sleep(50);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            Thread.yield();
            return inner();
        }
    }

The NonBlockingClass.outer() method is allowed to block and all the methods called down the stack, except the inner() method which is called by the outer() method.

Custom blocking method callback

  • Builder#blockingMethodCallback(Consumer<BlockingMethod> consumer)

By default, BlockHound will throw an error when it detects a blocking call.
But you can implement your own logic by setting a callback.

Example:

builder.blockingMethodCallback(it -> {
    new Error(it.toString()).printStackTrace();
});

Here we dump the stacktrace instead of throwing the error, so that we do not alter an execution of the code.

Custom non-blocking thread predicate

  • Builder#nonBlockingThreadPredicate(Function<Predicate<Thread>, Predicate<Thread>> predicate)

If you integrate with exotic technologies, or implement your own thread pooling, you might want to mark those threads as non-blocking. Example:

builder.nonBlockingThreadPredicate(current -> {
    return current.or(it -> it.getName().contains("my-thread-"))
});

⚠️ Warning: do not ignore the current predicate unless you're absolutely sure you know what you're doing. Other integrations will not work if you override it instead of using Predicate#or.