Skip to content

Instantly share code, notes, and snippets.

@vipyne
Last active October 25, 2019 19:18
Show Gist options
  • Save vipyne/4d653efa1a4ab4084ffed760c792b864 to your computer and use it in GitHub Desktop.
Save vipyne/4d653efa1a4ab4084ffed760c792b864 to your computer and use it in GitHub Desktop.
Proxies + stack traces = log all the things
Error.stackTraceLimit = Infinity;
function StalkTrace() {
this.name = '';
Error.captureStackTrace(this, StalkTrace);
}
function proxify(target, handler) {
if ("object" === typeof target ||
"function" === typeof target) {
return new Proxy(target, handler);
} else {
return target;
}
}
const handler = {
get: (target, key, receiver) => {
try { throw new StalkTrace('get').stack }
catch(e) {
console.log("get : ", target);
console.log(`%c get _${target instanceof Array ? target[key] : key}_ stalktrace: ${e}`, `background:#ABEBC6;`);
}
return Reflect.get(target, proxify(key, handler), receiver);
},
set: (target, key, value) => {
try { throw new StalkTrace('set').stack }
catch(e) {
console.log("set : ", target);
console.log(`%c set _${target instanceof Array ? target[key] : key}_ stalktrace: ${e}`, `background:#F9E79F;`);
}
return Reflect.set(target, proxify(key, handler), value);
},
apply: (target, thisArg, argumentsList) => {
const argList = argumentsList.map( arg => proxify(arg, handler));
try { throw new StalkTrace('apply').stack }
catch(e) { console.log(`%c apply _${target.toString().split("{")[0]}_ stalktrace: ${e}`, `background:yellow;`); }
return Reflect.apply(target, thisArg, argList);
},
getOwnPropertyDescriptor: (target, property) => {
try { throw new StalkTrace('getOwnPropertyDescriptor').stack }
catch(e) { console.log(`%c getOwnPropertyDescriptor _${property}_ stalktrace: ${e}`, `background:#CBC1FA;`); }
return Reflect.getOwnPropertyDescriptor(target, property);
},
defineProperty: (target, property, value) => {
try { throw new StalkTrace('defineProperty').stack }
catch(e) { console.log(`%c defineProperty _${property}_ stalktrace: ${e}`, `background:#CBC1FA;`); }
return Reflect.defineProperty(target, property, value);
},
// haven't used the rest of these, they are here for completion's sake
getPrototypeOf: (target, key, value) => {
try { throw new StalkTrace('getPrototypeOf').stack }
catch(e) { console.log(`%c getPrototypeOf _${key}_ stalktrace: ${e}`, `background:#C1DDFA;`); }
return Reflect.getPrototypeOf(target, proxify(key, handler), value);
},
setPrototypeOf: (target, key, value) => {
try { throw new StalkTrace('setPrototypeOf').stack }
catch(e) { console.log(`%c setPrototypeOf _${key}_ stalktrace: ${e}`, `background:#C1DDFA;`); }
return Reflect.setPrototypeOf(target, proxify(key, handler), value);
},
construct: (target, key) => {
try { throw new StalkTrace('construct').stack }
catch(e) { console.log(`%c construct _${key}_ stalktrace: ${e}`, `background:#C1DDFA;`); }
const keys = key.map( k => proxify(k, handler))
return Reflect.construct(target, keys);
// return Reflect.construct(target, key);
},
isExtensible: (target, key, value) => {
try { throw new StalkTrace('isExtensible').stack }
catch(e) { console.log(`%c isExtensible _${key}_ stalktrace: ${e}`, `background:#C1DDFA;`); }
return Reflect.isExtensible(target, proxify(key, handler), value);
},
preventExtensions: (target, key, value) => {
try { throw new StalkTrace('preventExtensions').stack }
catch(e) { console.log(`%c preventExtensions _${key}_ stalktrace: ${e}`, `background:#C1DDFA;`); }
return Reflect.preventExtensions(target, proxify(key, handler), value);
},
has: (target, key, value) => {
try { throw new StalkTrace('has').stack }
catch(e) { console.log(`%c has _${key}_ stalktrace: ${e}`, `background:#C1DDFA;`); }
return Reflect.has(target, proxify(key, handler), value);
},
deleteProperty: (target, key, value) => {
try { throw new StalkTrace('deleteProperty').stack }
catch(e) { console.log(`%c deleteProperty _${key}_ stalktrace: ${e}`, `background:#C1DDFA;`); }
return Reflect.deleteProperty(target, proxify(key, handler), value);
},
ownKeys: (target, key, value) => {
try { throw new StalkTrace('ownKeys').stack }
catch(e) { console.log(`%c ownKeys _${key}_ stalktrace: ${e}`, `background:#C1DDFA;`); }
return Reflect.ownKeys(target, proxify(key, handler), value);
}
}
module.exports = function(target) {
return new Proxy(target, handler);
}
// optional. override the browser's implementation stack trace. just here as an fyi.
Error.prepareStackTrace = (error, callsites) => {
console.log("error", error);
callsites.forEach((cs)=> {
console.log("getThis", cs.getThis());
console.log("getTypeName", cs.getTypeName());
console.log("getFunction", cs.getFunction());
console.log("getFunctionName", cs.getFunctionName());
console.log("getMethodName", cs.getMethodName());
console.log("getFileName", cs.getFileName());
console.log("getLineNumber", cs.getLineNumber());
console.log("getColumnNumber", cs.getColumnNumber());
console.log("getEvalOrigin", cs.getEvalOrigin());
console.log("isToplevel", cs.isToplevel());
console.log("isEval", cs.isEval());
console.log("isNative", cs.isNative());
console.log("isConstructor", cs.isConstructor());
console.log("isAsync", cs.isAsync());
console.log("isPromiseAll", cs.isPromiseAll());
console.log("getPromiseIndex", cs.getPromiseIndex());
// and probably do smarter stuff
})
}

Usage:

const stalk = require('stalktrace');

const objectToStalk = {};
stalk(objectToStalk);

const fun = stalk(() => { 
  console.log('I feel like someone is following me.');
});

Trace prints to browser console. Particularly useful for tracing programmatically created or called/applied functions. Yay metaprogramming.

Resources: https://v8.dev/docs/stack-trace-api https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment