Open Event Webapp allows event organizers to generate an event website by providing JSON data in the form of a zip file or an API endpoint. The generation of event website is a multi step process and takes some time to complete. In order to see the ongoing progress of the process and catch any errors, logging feature is a must. The challenging part was to send the log messages in real time about the tasks being performed in the generator instead of sending it after some time when the task is already finished.
To enable real time communication between the web server and the client, we use the Socket IO library. But before using that library for sending log messages from server to client, we have to design the architecture of the logging feature. The log statements can be of several types. It could be about the inception of a task, the successful completion of it or some error which occurred while performing the task. The log message object contains a field named type to show the type of the statements. We define three categories of logs messages:-
- INFO: Info statements give information about the task currently being performed by the webapp
- SUCCESS: Success statements give the information of a task being successfully completed
- ERROR: Error statements give information about a task failing to complete. These statements also contain a detailed error log
Along with the type of the statement, the object also contains information about the task. For all types of statements, there is a field called smallMessage containing short information about the task. For the ERROR statements where more information is required to see what went wrong, the message object has an additional field called largeMessage which holds detailed information about the event.
We also create a new file called buildlogger.js and define a function for creating log statements about the tasks being performed by generator and export it. The function creates a message object from the arguments received and then return it to the client under the buildLog event via the socket IO.
exports.addLog = function(type, smallMessage, socket, largeMessage) { var obj = {'type' : type, 'smallMessage' : smallMessage, 'largeMessage': largeMessage}; var emit = false; if (socket.constructor.name === 'Socket') { emit = true; } if (emit) { socket.emit('buildLog', obj); } };
Most of the steps of the generation process are defined in the generator.js file. So, we include the logging file there and call the addLog function for sending logs messages to the client. All the different steps like cleaning temporary folders, copying assets, fetching JSONs, creating the website directory, resizing images etc have multiple log statements for their inception and successful/erroneous completion. Below is an excerpt from the cleaning step.
var logger = require('./buildlogger.js'); async.series([ (done) => { console.log('CLEANING TEMPORARY FOLDERS\n'); logger.addLog('Info', 'Cleaning up the previously existing temporary folders', socket); fs.remove(distHelper.distPath + '/' + appFolder, (err) => { if(err !== null) { // Sending Error Message when the remove process failed logger.addLog('Error', 'Failed to clean up the previously existing temporary folders', socket, err); } // Success message denoting the completion of the step logger.addLog('Success', 'Successfully cleaned up the temporary folders', socket); done(null, 'clean'); }); } )]
But we have only done the server side work now. We also have to handle the message on the client side. We send the message object to the client under the event buildLog and set up a listener for that event to catch the sent message. After the message object is received on the client side, we extract the information out of that object and then display it on the website. We have a div having an id of buildLog for displaying the log information. The content of the message is dynamically added to it as soon as it is received from the server. All the client side logic is handled in the form.js file.
socket.on('buildLog', function(data) { var spanElem = $('<span></span>'); // will contain the info about type of statement var spanMess = $('<span></span>'); // will contain the actual message var aElem = $('<button></button>'); // Button to view the detailed error log var paragraph = $('<p></p>'); // Contain the whole statement var divElem; // Contain the detailed error log spanMess.text(data.smallMessage); spanElem.text(data.type.toUpperCase() + ':'); paragraph.append(spanElem); paragraph.append(spanMess); divElem.text(data.largeMessage); paragraph.append(aElem); paragraph.append(divElem); $('#buildLog').append(paragraph); // Div containing all the log messages };
This is how the logs look on the client side. They are loaded on the go in real time as and when the events occur.
Resources: