Source: SelBlocksGlobal/sel-blocks-fx_xpi/chrome/content/extensions/logger.js

// Based on traditional SelBlocks, with clarifications and without unneeded Logger.trace().
"use strict";

// Following assignments is purely for JSDoc.
/** @namespace */
selblocks= selblocks;

/** LOG wrapper for SelBlocks-specific behavior
 * @class
   */
selblocks.Logger= function Logger() {};
/** Log an error. */
selblocks.Logger.prototype.error = function (msg) { this.logit("error", msg); };
/** Log a warning. */
selblocks.Logger.prototype.warn  = function (msg) { this.logit("warn", msg); };
/** Log at info level. */
selblocks.Logger.prototype.info  = function (msg) { this.logit("info", msg); };
/** Log at debug level. */
selblocks.Logger.prototype.debug = function (msg) { this.logit("debug", msg); };

/** Log at given level. */
selblocks.Logger.prototype.logit = function (logLevel, msg) {
  // Following LOG is a global object from Selenium IDE. It's different to selblocks.LOG.
  LOG[logLevel]("[" + $$.name + "] " + msg);  // call the Selenium logger
};

// ==================== Stack Tracer ====================
/** Generate stack trace.
 *  @param {Error|undefined} [err]
 *  @returns {Array} */
selblocks.Logger.prototype.genStackTrace = function(err)
{
  var e = err || new Error();
  var stackTrace = [];
  if (!e.stack)
    stackTrace.push("No stack trace, (Firefox only)");
  else {
    var funcCallPattern = /^\s*[A-Za-z0-9\-_\$]+\(/;
    var lines = e.stack.split("\n");
    for (var i=0; i < lines.length; i++) {
      if (lines[i].match(funcCallPattern))
        stackTrace.push(lines[i]);
    }
    if (!err)
      stackTrace.shift(); // remove the call to genStackTrace() itself
  }
  return stackTrace;
};

/** Log stack trace.
 * @param {Error|undefined} [err]
 */
selblocks.Logger.prototype.logStackTrace = function(err)
{
  var t = this.genStackTrace(err);
  if (!err)
    t.shift(); // remove the call to logStackTrace() itself
  this.warn("__Stack Trace__");
  for (var i = 0; i < t.length; i++) {
    this.warn("@@ " + t[i]);
  }
};

/** Describe the calling function.
*/
selblocks.Logger.prototype.descCaller = function()
{
  var t = this.genStackTrace(new Error());
  if (t.length == 0) return "no client function";
  t.shift(); // remove the call to descCaller() itself
  if (t.length == 0) return "no caller function";
  t.shift(); // remove the call to client function
  if (t.length == 0) return "undefined caller function";
  return "caller: " + t[0];
};

/** @member {Logger} */
selblocks.LOG = new selblocks.Logger();