If you are using the Logger extensively, it’s sometimes hard to track where the log message is coming from. It would be useful to display line number, file name and function that provoked the log.

Here’s how

First we create a function that throws a deliberate error, then analyzes the call stack of that error to see where it was called from.

Here it is.

/**
 * identify the call stack
 * @param {Number} level level of call stack to report at (1 = the caller, 2 the callers caller etc..., 0 .. the whole stack
 * @return {object || array.object} location info - eg {caller:function,line:string,file:string};
 */
function whereAmI(level) {
  
  // by default this is 1 (meaning identify the line number that called this function) 2 would mean call the function 1 higher etc.
  level = typeof level === 'undefined' ? 1 : Math.abs(level);

  
  try {
    // throw a fake error
    var __y__ = __X_;  //x is undefined and will fail under use struct- ths will provoke an error so i can get the call stack
  }
  catch (err) {
    // return the error object so we know where we are
    var stack = err.stack.split('\n');
    if (!level) {
      // return an array of the entire stack
      return stack.slice(0,stack.length-1).map (function(d) {
        return deComposeMatch(d);
      });
    }
    else {
    
      // return the requested stack level 
      return deComposeMatch(stack[Math.min(level,stack.length-1)]);
    }

  }
  
  function deComposeMatch (where) {
    
    var file = /at\s(.*):/.exec(where);
    var line =/:(\d*)/.exec(where);
    var caller =/:.*\((.*)\)/.exec(where);
    

    return {caller:caller ? caller[1] :  'unknown' ,line: line ? line[1] : 'unknown',file: file ? file[1] : 'unknown'};

  }
  }

By default it will return the location from where it was called, so this –

function test2() {
  
  Logger.log('my logged message ' + JSON.stringify(whereAmI()));
  
  }

logs this

 my logged message {"caller":"test2","line":"120","file":"Code"}

Reporting a different stack level

Imagine you have some shared function that’s called from multiple functions, and you have a Logger.log() in your shared function. The example above will show location information about the shared function, but what you really want to log is where shared was called from (and maybe even higher up than that). You can do this by passing a stack level to whereAmI() and it will report back up the call stack as required.

this

function shared() {
  
  Logger.log('my logged message ' + JSON.stringify(whereAmI()));
  
  }

will give this

pre class=”lang:sh decode:true ” > my logged message function test4() { shared(); } function test5() { shared() } function shared() { Logger.log('my logged message ' + JSON.stringify(whereAmI(2))); }

Gives this

my logged message {"caller":"test4","line":"95","file":"Code"}
my logged message {"caller":"test5","line":"101","file":"Code"}

Getting the whole stack

You can also get the whole stack by passing level 0 – so this

function shared() {
  
  Logger.log('my logged message ' + JSON.stringify(whereAmI(0)));
  
  }

will return this when called from test4.

[
    {
        "caller": "whereAmI",
        "line": "123",
        "file": "Code"
    },
    {
        "caller": "shared",
        "line": "107",
        "file": "Code"
    },
    {
        "caller": "test4",
        "line": "95",
        "file": "Code"
    }
	]

This function is part of the Useful stuff library

Known issues

if you use this type of structure

function SomeClass () {
    this.someMethod = function () {
        Logger.log ('my message ' + JSON.stringify (cUseful.whereAmI(0)));
   };
   }

then call it like this

function test6() {
  new SomeClass().someMethod();
  }

The caller is not included in the stack.

[
    {
        "caller": "whereAmI",
        "line": "315",
        "file": "Code (cUseful)"
    },
    {
        "caller": "unknown",
        "line": "122",
        "file": "Code"
    },
    {
        "caller": "test6",
        "line": "127",
        "file": "Code"
    }
	]

Happy logging.

For more like this see Google Apps Scripts Snippets
For help and more information join our forum, follow the blog, follow me on twitter