Reporting file, function and line number in Apps Script

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

my logged message {"caller":"shared","line":"107","file":"Code"}

whereas running test4 & test5 - with a stack level of 2 passed to whereAmI(2), will give location information about who is calling shared().

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.

See  Google Apps Scripts snippets for more like this

For help and more information join our forumfollow the blogfollow me on twitter

You want to learn Google Apps Script?

Learning Apps Script, (and transitioning from VBA) are covered comprehensively in my my book, Going Gas - from VBA to Apps script, available All formats are available now from O'Reilly,Amazon and all good bookshops. You can also read a preview on O'Reilly

If you prefer Video style learning I also have two courses available. also published by O'Reilly.
Google Apps Script for Developers and Google Apps Script for Beginners.




Comments