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.