Skip to main content

Habit-Firebug Saver: SmartLogger



How many of the web developers do not depend on Firebug or the chrome's console... Just wondering..

BTW, its plain fun to work with firebug, makes life a lot easier.. Its a different matter all together that the other browser that you have to develop for does not have a powerful enough tool. (Name deliberately avoided to avoid the eminent flame-war!) Yes the current versions have a quite powerful debug and development tools but (hopefully) few developers working on products still have to consider some 10 year old versions (namely 6, 6.5 and 7). Ah, the pain.. Anyways, we are not discussing that..

What we are talking about is the issues that we face when testing our changes to a thousand lines JavaScript code on multiple browsers, especially after we are accustomed to the ease of firebug. :)

I spent much of my time commenting my console.log() statements before I could dare to open the page in IE. Well, fear not, the days have passed! The pain drove me to write a logger object that can not only sense presence of console object but can do much more than that, like ability to assert, selective logging and more..

I call it the SmartLogger.


//Global Logger Object, for use during development, configurable logger.
var SmartLogger = function(options) {
 
 var sl = {}; // Logger Object
 
 // Accepting passed params.
 options = options || {};
 sl.enableLogger = options.enableLogger!==undefined?options.enableLogger:true;
 sl.enableAssert = options.enableAssert!==undefined?options.enableAssert:true;
 sl.loggerOutput = options.loggerOutput!==undefined?options.loggerOutput:undefined; //'console', 'alert', undefined
 sl.selectiveEnable = options.selectiveEnable!==undefined?options.selectiveEnable:'';
 sl.selectiveDisable = options.selectiveDisable!==undefined?options.selectiveDisable:'';
 
 // Logger properties
 sl.name = "SmartLogger";
 sl.whoami = function(){ return "SmartLogger_"+sl.enableLogger+"_"+sl.enableAssert+"_"+sl.loggerOutput+"_"+sl.selectiveEnable+"_"+sl.selectiveDisable;}
 sl.version = '0.7';
 
 // Checks if console object is defined. Checked only at the time of instantiation.
 var hasConsole = (typeof console === "object");
 
 // Checks if logging should be done to console.
 function logToConsole(){
  if (sl.loggerOutput){
   if (sl.loggerOutput === 'console') return true;
  } else {
   if(hasConsole) return true;
  }
  return false;
 }
 
 // Handles the logging intelligence
 function handleLogging(logMethod, logString, strId){
  if(!sLog(strId)) {return;}
  // Decides if to log and logs or alerts appropriately.
  if(sl.enableLogger){
   if (logToConsole()){ // && hasConsole
    if(hasConsole)console[logMethod](logString);
   } else {
    alert(logString);
   }
  }
 };
 
 // Handles the selective logging functionality
 function sLog(strId){
  var allowLog = true;
  if (sl.selectiveEnable) {
   allowLog = strId === sl.selectiveEnable;
  } else if (sl.selectiveDisable) {
   allowLog = !(strId === sl.selectiveDisable);
  }
  
  return allowLog;
 };
 
 // Returns a formatted object structure with current values to complete depth.
 function printString(obj, name, str, strEnd){
  var stringified;
  name = name?name:"Object",
  str = str?str:"";
  strEnd = strEnd?strEnd:"";
  stringified = str+name+" : {\n";
  for (var a in obj){
   if (typeof obj[a] === 'object'){
    stringified+= printString(obj[a],a,"\t",",");
   } else {
    stringified+= str+"\t"+a +" : "+obj[a]+",\n";
   }
  }
  stringified += str+"}"+strEnd+"\n";
  return stringified;
 };
 
 // Exposed methods of the object
 //log a string to console/alert
 sl.log = function(str, strId){
  handleLogging('log', str, strId);
 };
 
 //debug logging a string to console/alert
 sl.debug = function(str, strId){
  handleLogging('debug', str, strId);
 };
 
 //write an information string to console/alert
 sl.info = function(str, strId){
  handleLogging('info', str, strId);
 };
 
 //throw error string to console/alert
 sl.error = function(str, strId){
  handleLogging('error', str, strId);
 };
 
 //Assert an assumption
 sl.assert = function(str, strId){
  if(sl.enableAssert){
   handleLogging('log', 'Assumption: true', strId);
   if(!str){
    handleLogging('error', 'Assumption failed!', strId);
    debugger;
   }
  }
 };
 
 // Logs the formatted object structure with current values to console/alert
 sl.stringToConsole = function(obj, str){
  sl.log(printString(obj, str));
 };
 
 return sl;
};

var sl = new SmartLogger();

Features:

  • Multiple logging profiles can be maintained at the same time with different properties.
var sl = new SmartLogger();
var sl2 = new SmartLogger({selectiveEnable: 'block1'});
  • Proprieties can be set at the time of instantiation or even later.
var sl = new SmartLogger();
sl.loggerOutput = 'console';
var sl2 = new SmartLogger({loggerOutput: 'console'});
  • name, version number and whoami to identify the logger with a string of its current properties.
var sl = new SmartLogger();
sl.name // SmartLogger.
sl.version // 0.7
sl.whoamI() // Returns a string of its properties with the name of the object in a specific sequence:
// "SmartLogger_"+ enableLogger +"_"+ enableAssert+"_"+ loggerOutput+"_"+ selectiveEnable+"_"+ selectiveDisable;
// Example: SmartLogger_true_true_console__b
// We will see what these properties are in some time..
  • Enable or disable logging altogether: enableLogger controls if the statements should ever be logged.
var sl = new SmartLogger();
sl.log('gets logged');
sl.enableLogger = false;
sl.log('never gets logged');
  • Intelligently decides where the logging statements should go...
sl.loggerOutput = undefined; //default
/* Decides based on presence of 'console' object.
If console is present statements will be logged to console,
else like in case of IE, will be 'alerted' to the user.
Now at times this can get messy, with loads of log statements alerting on our face..
But wait, we have ways to handle that.*/

sl.loggerOutput = 'console';
// Plain instruction, no intelligence, all statements will always go to console.
// If console is not present statements will just be eaten-up.

sl.loggerOutput = 'alert';
// Another plain instruction, all statements will always be alerted.
// Will not bother to check if console exists or not.
  • Log formatted objects to console. Now you wont need that much with firebug but to see the entire contents of the object, well formatted you can just say stringToConsole.
// Just a sample object with unknown properties.
var obj = {prop1: 'value',functProp:function(){return "this is a function that returns me!";}, propObj:{prop2:'value2'}};
sl.stringToConsole(obj); // You say this.

// On console or in the alert prompt, you get this
Object : {
 prop1 : value,
 functProp : function () {
    return "this is a function that returns me!";
},
 propObj : {
  prop2 : value2,
 },
}

  • Assert your assumptions. Checks that the assumption is true, if yes, logs so. If assumption fails, will write out an error on the console and invoke the debugger so the user can check in the stack exactly where the assumption failed and why.
sl.assert(Obj.str===Obj2.str)
sl.assert(1==1); // logs 'Assumption: true' to console.
sl.assert(1==2); // Logs error 'Assumption: failed!' and invoke debugger to the assert line in SmartLogger.

//Now you can go and check in the stack and watch to panels to check value and call stack.
  • Has a wrapper for 4 of the logging APIs from firebug and adding new is not much of a task. What it already has:
    • log
    • debug
    • info
    • error
  • Has ability of selective logging.
Now this thing is a live saver. The properties selectiveEnable and selectiveDisable control what statements to log. While these are not mandatory inputs to all the wrappers but I suggest you set them always. These are logging context that can be used to selectively enable logs for only partial of the code, the code that currently interests you..

// Suppose we were working on a defect number 101 and now we are developing a functionality for
// automating welcome messages to users and are asked to urgently fix defect 203.
// Ah, complex scenario, but it will only help understand the purpose.

// When we are working on defect 101, we had the logger configured as statements as:
sl.log("reached here"); // worst way to log: who knows wheres here! but just an example.

// Now we are working on the functionality and
// we would not want those 10 logging statements added while we were working on the defect.
// We can remove them or simply enable the 'selective logger'!
sl.selectiveEnable = 'welcomer';
sl.log("fetching message", "welcomer");
// And voila, only the 'welcomer' messages will be logged.

// Now we get the next urgent defect.
sl.selectiveEnable = 'defect203';
sl.log("value in Obj1.str"+Obje1.str, "defect203");
// We get only the defect203 logs!

// Now some of our new changes depend on the changes we made in defect101, but we cant get the logs from those..
// What do we do? If someone did not enable selective logger and removed the statements, please add them back (:p),
// remove statements for 'welcomer' functionality. Or simply, disable 'welcomer' messages..!
sl.selectiveEnable = '';
sl.selectiveDisable = 'welcomer';
sl.log("value in Obj1.str"+Obje1.str, "defect203");
sl.log("value in Obj2.varInt1"+Obj2.varInt1, "defect101");
// Ha ha! Log statements for 'welcomer' gone and we get the rest!

While using the SmartLogger, I suggest you always pass the string identifier, so that you can control the logs at any point of time later.

What can you expect next in SmartLogger:

  • Use assert from firebug itself.
  • Check that the function exists in the logger before calling it.
  • Make the selective logger take arrays.
  • In stringToConsole, handle functions too to remove that glitch in no closing bracket.

Let me know what you think about the SmartLogger, if you would like any additions to its behaviour and also if you find any defects in the comments below.

Comments

  1. Looks interesting thing, bt :
    One simple question : utilities like firebug are known to make browser slow. What about smartLogger? What overhead we can expect???

    ReplyDelete
  2. It is supposed to be used with firebug/chrome developer tools as a layer between or when you otherwise use firebug, that is only during development..

    Yes, it will surely add a couple of function calls and a few microseconds to processing. That is the reason methods from firebug that deal with time or function calls, like 'console.profile()' or 'console.time()' are not included. But for testing/development it gives a lot more functionality than to worry about the added function call. :)

    ReplyDelete

Post a Comment

Popular posts from this blog

Using Docker and a Private Registry with VPN On Windows

Wasn’t that a very specific title? Docker has a very good documentation and reading that alone is enough for most of the straightforward tasks we might want to do. But as always some practical tasks are not straightforward, hence this blog. What we are going to see here today is how to setup docker toolbox on a Windows machine, make it work even when VPN is connected, make it talk to a private, insecure docker registry (that is why VPN) and configure it so it can run docker compose and see how we can set this config as a one-time activity. That’s quite a mouthful, but yes this is what we are going to do. All ready? Let us begin then.

Install Docker ToolboxGo and download the docker toolbox and install it. That should create a shortcut called “Docker Quickstart Terminal”. Run it. That should show you an error about virtualization.

Enable VirtualizationRestart your machine, enter the BIOS settings and enable virtualization. It may be under advanced settings. On this Laptop, it is under th…

Yet another packager for node

Yet another packager for node There are so many packaging systems for node already, or maybe not as many, so here I am presenting another way to package your applications into an self extracting executable that has no dependencies. Ah well, a few dependencies, like the processor architecture, and Linux operating system may be, but that is all. What is it? It is a modified shell script originally used to create self-extracting and installing applications for Linux platforms. What it does is, it creates a tarball which includes your code, the modules it depends on, the specific node binary it uses, and appends it to a script with the command to execute your code. It is essentially a binary merge of the files, the shell script and the tar.This is not something new, people have used such a system in the past to deliver applications for Linux, every time you see an obscenely large ‘.sh’ file (for being that, a shell file) that can install or execute an application without requiring any ot…

Redis Cluster: Fact Sheet (Not Just Issues)

Redis and the Redis clustering works very differently from the other data stores and data store clusters. The differences are not always as obvious and may come up as realizations down the line while using Redis, like what happened in our case. We are using a Redis cluster, with which, fortunately, we have not faced many issues so far. But that does not mean we will not and we shall need to be prepared.
Recently we were working on getting a Redis cluster up and working with docker compose and was enlightened to some of the differences which later led to disillusionment for me. Thought that there should be a ‘document of facts‘ on Redis and Redis cluster which people/myself can refer to. So I decided to create one, enjoy:
Redis is great as a single server. In a Redis cluster, all your masters behave as if they are simultaneously active (not sure if they all are masters at the same time technically, but they behave as such). Every master in a cluster knows every other master/node in the clu…