Background
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"
Problem
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!
Research
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!
Questions
So, given this snippet, I have the following questions:
- How would you keep the chain going if a log fails?
- How would you make log failure and recovery invisible to the application ( without littering it with the equivalent of (
try/catch
)? - What is the most commonly used pattern for logs?
I would make a
tap
function specifically for Futures, eg:This implementation of
tapF
expectsf
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:
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
.