1

I'm trying to figure out how to log a Mono<String> password with slf4j but it would always return a Monotype.

logger.info(login.getPassword()+" "+userRepository.findPasswordByUsername(login.getUsername()));

and

logger.info(login.getPassword()+" "+userRepository.findPasswordByUsername(login.getUsername()).toString());

the first 2 logging tries above return the literal password (from the request) and MonoNext

and ofc you cant use .block()

which just throws

"block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3"

yes i'm aware that i can pass a Subscriber / Consumer for onNext() like:

.subscribe(x -> System.out.println( x.toString()))

to get some output but how would i do that with a logger only, is there even a way ?

login represents a user retrieved from a request. The password is properly stored and encoded (Bycrypt) beforehand ofc (doesn't seem to be the issue).

Edit: to give more context userRepository.findPasswordByUsername("username") will return a Mono which i want to compare to another password as in:

passwordEncoder.matches( "userinputPW", userRepository.findPasswordByUsername("username") )

which is how you use a ByCryptEncoder in Spring and i can't .map() the Mono to a String (will obviously always return an Object and not a String as explained here https://stackoverflow.com/a/47180610/6414816 )

Using spring-boot-starter-webflux, spring-boot-starter-data-r2dbc, spring-boot-starter-security

What am i missing ? Thanks in Advance.

Bender
  • 72
  • 6
  • your password is represented as Mono class and not a string. From a quick search on the internet , Mono is a stream of 0..1 element and you can try login.getPassword().block() to get the string value While this might work I advice you to read more about Mono , Flux , java reactive programming – Chanfir Jan 26 '22 at 22:42

2 Answers2

1

that is correct you should not block in a reactive application, neither should you subscribe in this usercase, as your application is most likely a producer, and the calling client is the consumer that subscribes.

what you are looking for is the doOn operators, that handles side effects. Logging is a side effect, its something you want to do on the side without disturbing the current flow. For instance update something, increment something, or in your case write to a log.

what you want os probably the doOnSuccess operator

example (i have not chacked against a compiler since im on mobile), but something like this.

function Mono<Login> foobar(Login login) {
    return userRepository.findPasswordByUsername(login.getUsername)
                .doOnSuccess(pwd -> {
                    logger.info(login.getPassword() + " " + pwd);
                }).thenReturn(login);
}
Toerktumlare
  • 12,548
  • 3
  • 35
  • 54
  • I assume this is supposed to be a callback ? Any "sideeffect" call like .doOnNext() will only return an Object aswell .thenReturn("Value") will just emit "Value" after the Mono completes for the logger and for the db call too. – Bender Jan 28 '22 at 01:35
  • Is this a question? I dont understand your statement – Toerktumlare Jan 28 '22 at 08:02
  • your snippet is basically what i was trying and doOnSucceed() or doOnNext() will wait for the Mono too. you have to subscribe() this works: `public Disposable foobar(User login) { Mono userMono = userRepository.findPasswordByUsername(login.getUsername()); return userMono.subscribe(x -> logger.info("password: "+x));}` i'm at `logger.info("password: "+userRepository.findPasswordByUsername(login.getUsername()).share().block().toString() );` (compiler is fine with this and no exceptions thrown during runtime) albeit not very elegant but it works. – Bender Jan 28 '22 at 12:08
  • And as i wrote… you have not disclosed who is initiating the call. If this is a webflux service, and you have for instance a webpage calling an endpoint, the webpage is the subscriber, so you dont call subscribe, you return the mono to the client and the framework will handle the subscription. If your code on the other hand is consuming something else, aka. Your application initiates a call to another service and you app is the final consumer, then you must call subscribe. I assumed the first scenario – Toerktumlare Jan 28 '22 at 12:17
  • Remember if you post just a line of code, you will get an answer that will make certain asumptions. If you provide a poor question with not a full context, an answer will be given with the information you have presented. – Toerktumlare Jan 28 '22 at 12:19
  • your assumption is right (its a ServerResponse from a Handler) but no matter who will initiate the call if i call your piece of code without subscribing nothing will happen (no output), and i want to log (hence the logger) smth on the fly (the db call to userRepository) and not output this to a user and i was trying to make this clear i'm sorry and thx for the suggestion – Bender Jan 28 '22 at 12:32
-1

Since nobody came up with an answer yet,

logger.info("userName :" +userMono.map(user -> user.getUsername()).share().block());

.share() will allow to .block()

from the docs: "Prepare a Mono which shares this Mono result similar to Flux.shareNext().This will effectively turn this Mono into a hot task when the first Subscriber subscribes using subscribe() API. Further Subscriber will share the same Subscription and therefore the same result.It's worth noting this is an un-cancellable Subscription." works for me (even though it incorporates .block() )

userMono.subscribe(user -> logger.info("username: "+user.getUsername()));

will return a Disposable

Both will output the String value for the logger.

on a sidenote (which i wasn't aware of) there is an operator .log("callerForADefinedLogger").

Bender
  • 72
  • 6