How to format a StackTrace using Error.prepareStackTrace?

(on node version '4.2.1', v8 '4.5.103.35')

    var TE = class tError extends Error {
       constructor(message) {
          super(message);
          this.name = tError.name;
          Error.captureStackTrace(this, tError);
       }
       static prepareStackTrace() {
          console.log('run is prepareStackTrace');
          return 'MyPrepareStack'
       }
       get stack() {
          console('getter stack');
          return 'MyStack';
       }
    };
    
    TE.stackTraceLimit = 1;
    
    console.log('ErrorPrepare:', TE.prepareStackTrace);
    
    var e = new TE('MyError');
    console.log('ErrorName: ', e.name);
    console.log('ErrorStack: ', e.stack);
OUTPUT:

    D:\>node ./Error
    ErrorPrepare: prepareStackTrace() {
          console.log('run is prepareStackTrace');
          return 'MyPrepareStack'
       }
    ErrorName:  tError
    ErrorStack:  tError: MyError
        at Object.<anonymous> (D:\Error.js:23:9)
        at Module._compile (module.js:435:26)
        at Object.Module._extensions..js (module.js:442:10)
        at Module.load (module.js:356:32)
        at Function.Module._load (module.js:311:12)
        at Function.Module.runMain (module.js:467:10)
        at startup (node.js:134:18)
        at node.js:961:3

Using the native API's getters is not working for me.

How do you format the stackTrace using the native stackTrace API, Error.prepareStackTrace?

Upvotes: 4

Views: 4143

Answers (1)

AKUMA no ONI
AKUMA no ONI

Reputation: 11829

Your not inheriting from anything when using Error.prepareStackTrace.


About Error.prepareStackTrace


Error.prepareStackTrace is quite different from your typical JS API because it runs your code internally (or it might be better to say Natively). Really your writing the code in JavaScript, but its not a JavaScript API, and it's not a Node API, but rather, its a V8 JavaScript Engine API. Its not something that you can find in other engines.



Parts of the StackTrace


I remember when I first learned about Error.prepareStackTrace, I was really confused because the WebStorm hover widget showed it being a Callback Function. It didn't list all the available API methods I have listed below. Another thing that confused me, was when I read the V8 Documentation on StackTraces (which I suggest you do too if you want to really understand this topic), the docs mentioned CallSites, which threw me off, because I never heard of CallSites. But its important to know, that not only does prepareStackTrace grant access to an array of CallSites, the lines in the standard formatted JavaScript StackTrace that look like...

'at' /foo/foo.js:11:32
'at' /foo/bar.js:59:22

...are the CallSites returned by prepareStackTrace.

In the lines above, you can see 3-parts, all obtainable by the V8 CallSite API's getters.

  1. FilePath
  2. Line Number
  3. Column Number


Using Error.prepareStackTrace



export class Err extends Error {
  _stack?:string;
  constructor(message: string) {
    super(message);
    const _prepareStackTrace = Error.prepareStackTrace;

    Error.prepareStackTrace = (_, callsites) => {
      callsites.forEach(callsite => {
        callsite.getLineNumber();
        callsite.getColumnNumber();
      });
    };

    this._stack = this.stack;

    Error.prepareStackTrace = _prepareStackTrace;
  }
}
This is in TypeScript as that's how I have my editor setup rn, and I wanted to offer a working example. Often times people will do something like I have authored below. This allows the user to get their current position in there code using...
const pos = new Err('')._stack;

This assigns the current row | col in the code to pos.



A couple things to note when using Error.prepareStackTrace are:


  1. Error.prepareStackTrace looks like you would want to use a callback function with it, but really you want to use the Assignment Operator (=) to assign a function to Error.prepareStackTrace. The function you assign will work as if it were a callback.

  2. The function assigned to Error.prepareStackTrace should have two parameters. The first parameter is the Error Object & the second parameter is an array of CallSites. Typically developers call forEach((item, index)=>{ /* do stuff to item */}) on the CallSites parameter to access all available callsites, and do what ever is desired to them.

  3. There is another function associated with this subject, and that is the Error.captureStackTrace method. This is used by the V8 engine when a stackTrace needs to be fetched, but access to the method is not exclusive, as V8 JS developers have access to it.

  4. Error.prepareStackTrace & Error.captureStackTrace are both native V8 JavaScript functions, and are both available for use within the Node.js RTE (written during v16.2.0). Browsers do not support either function.

  5. As stated above, Error.prepareStackTrace is called by the V8 engine automatically. When that happens, the parameters of the function that is assigned to Error.prepareStackTrace are automatically passed the their arguments. The arguments include an Error Object, and that Error Object's Stack-Trace (This is also stated above, if your confused its because you jumped around, and did not read the list in order.).

  6. The stack returned by the snippet above can be manipulated by the following method calls, so long as the data is available. Just because the method is available does not mean that data is available for the method.


  • getThis: returns the value of this
  • getTypeName: returns the type of this as a string. This is the name of the function stored in the constructor field of this, if available, otherwise the object’s [[Class]] internal property.
  • getFunction: returns the current function
  • getFunctionName: returns the name of the current function, typically its name property. If a name property is not available an attempt is made to infer a name from the function’s context.
  • getMethodName: returns the name of the property of this or one of its prototypes that holds the current function
  • getFileName: if this function was defined in a script returns the name of the script
  • getLineNumber: if this function was defined in a script returns the current line number
  • getColumnNumber: if this function was defined in a script returns the current column number
  • getEvalOrigin: if this function was created using a call to eval returns a string representing the location where eval was called
  • isToplevel: is this a top-level invocation, that is, is this the global object?
  • isEval: does this call take place in code defined by a call to eval?
  • isNative: is this call in native V8 code?
  • isConstructor: is this a constructor call?
  • isAsync: is this an async call (i.e. await or Promise.all())?
  • isPromiseAll: is this an async call to Promise.all()?
  • getPromiseIndex: returns the index of the promise element that was followed in Promise.all() for async stack traces, or null if the CallSite is not a Promise.all() call.




LINKS:

  1. V8 Official Documentation - A must read for understanding this topic.

  2. Codota Code Examples - This is good for seeing how others use it.

  3. NPM Module that implements Error.prepareStackTrace for you.

Upvotes: 8

Related Questions