Logging operators

Learn how to log operator events.

Logging operators

Both Uni and Multi offer a log operator that can be used to trace events as they flow through operators. Mutiny does not make any assumption on how logging is defined, and does not rely on any specific logging API.

Using a logging operator

The log method comes in 2 forms: one that takes an identifier and one that derives the identifier from the upstream class:

Multi.createFrom().items(1, 2, 3)
        .onItem().transform(n -> n * 10)
        .log()
        .subscribe().with(item -> System.out.println(">>> " + item));

Here the log operator traces all events between the onItem().transform(…​) operator and the subscriber, as in the following output:

11:01:48.709 [main] INFO Multi.MultiMapOp.0 - onSubscription()
11:01:48.711 [main] INFO Multi.MultiMapOp.0 - request(9223372036854775807)
11:01:48.711 [main] INFO Multi.MultiMapOp.0 - onItem(10)
>>> 10
11:01:48.711 [main] INFO Multi.MultiMapOp.0 - onItem(20)
>>> 20
11:01:48.711 [main] INFO Multi.MultiMapOp.0 - onItem(30)
>>> 30
11:01:48.711 [main] INFO Multi.MultiMapOp.0 - onCompletion()

There are a few things to note here:

  1. we are logging on a Multi, so the logging event is prefixed with Multi (and Uni in the case of a…​ Uni), and

  2. since we did not specify any identifier in the log method call, MultiMapOp has been derived from the preceding operator (non-qualified) class name, and

  3. since there can be multiple subscriptions an integer is appended to the identifier (0, 1, 2, …​).

Defining logging

What happens when events are being logged is defined with the Infrastructure class. Events are written by default to the standard console output in a format similar to:

[--> Multi.MultiMapOp.0 | onSubscription()
[--> Multi.MultiMapOp.0 | request(9223372036854775807)
[--> Multi.MultiMapOp.0 | onItem(10)
[--> Multi.MultiMapOp.0 | onItem(20)
[--> Multi.MultiMapOp.0 | onItem(30)
[--> Multi.MultiMapOp.0 | onCompletion()

The following is an example of configuring logging with SLF4J:

Infrastructure.setOperatorLogger((id, event, value, err) -> {
    Logger logger = LoggerFactory.getLogger(id);
    if (err != null) {
        logger.info(event + "(" + err.getClass() + "(" + err.getMessage() + "))");
    } else {
        if (value != null) {
            logger.info(event + "(" + value + ")");
        } else {
            logger.info(event + "()");
        }
    }
});
Note that this is only useful to do when embedding Mutiny in your own stack, some frameworks like Quarkus will already have defined the correct logging strategy.