I have a function, called logInfoAsync
. Let's consider this function sends some information to a log server over the network. For the purposes of this question let's assume the function is implemented as follows:
const logInfoAsync = logger => message =>
new Promise( ( resolve, reject ) => {
setTimeout( () => {
//random fail reason.
if( message.length > 10 ){
reject( "We have angered the Gods. Message is too big!" );
return;
}
logger(message);
resolve();
}, 2000 );
})
Usage:
const myLog= logInfoAsync( console.log );
myLog("Hello world!") // Rejected: "We have angered the Gods. Message is too big!"
myLog("Hello") // Resolved: "Hello"
So far so good. We have a standard logger that sometimes works and sometimes it angers the Gods.
Now, let's assume that we have a series sequential async
computations:
const { Future } = require("Fluture");
const myLogF= Future.encaseP( logInfoAsync( console.log ) );
//request.get returns a Future and so does saveDB
request.get("http://mywebsite.com")
.chain( response => myLogF(`res is: ${res}`) )
.chain( saveDB )
.chain( ( ) => myLogF("Saved!") )
.fork(
err => console.log(`Something failed badly!: ${err}`),
( ) => console.log("Data saved to Olympus with great success!")
);
In this example, what happens if the logger angers the Gods? Well, We fail to save the data! Maybe the request went fine, and maybe the data was valid, but because the logger failed, we got screwed!
Now, a possible solution to this would be to use Fluture.bimap
after every log.
This would be horrible.
I don't want my logs to be more invasive than they already are and I most definitely do not what to litter my code with Promise styled try/catch
s.
Unfortunately for me, this is the only thing I can think off ... I think that the best option would be a backup logger, for example, console.error
that is used should the myLogF
fail, but I ideally I would want this to be invisible.
The application should not be aware it is being logged at all!
So, given this snippet, I have the following questions:
try/catch
)?I would make a tap
function specifically for Futures, eg:
const always = x => y => x
const tapF = f => x => f(x).fold(always(x), always(x))
This implementation of tapF
expects f
to return a Future, and it will force it to resolve with the original input value.
It can then be used for logging, for example:
request.get("http://mywebsite.com")
.chain( tapF(res => myLogF(`res is: ${res}`)) )
.chain( saveDB )
.chain( tapF(() => myLogF("Saved!")) )
Now the result of this expression is completely independent from what happens inside the tapF
functions.
I believe that should answer your first two questions. The last; "What is the most commonly used pattern for logs?", I'm not sure about. There are a few patterns out there, and two I can think of:
tapF
.