1

First of all, I know one way to catch uncaught errors is use process.on('uncaughtException', err, function() {});.

I want to know how I can pass more details, or more context to the error. I want to be able to get the variables used. I'm not trying to recover from the error, only get more details of my environment when the error happened before shutting down the process. Yeah, the stack trace is nice, but I'd like to know how to replicate the error.

For example, I have this function:

function doTheBatman(var1) {
  var var2 = 'whatever';
  // this causes an uncaught exception later in the code
}

On process.on, I want to be able to access var1 and var2.

process.on('uncaughtException', function(err) {
  // process.whatever doesn't contain any active variables
});
JM-AGMS
  • 1,670
  • 1
  • 15
  • 33
  • Are you aware you can [add an express route error handler](https://expressjs.com/en/guide/error-handling.html#writing-error-handlers)? It's basically overloading the route definition with an error-handling function. – Hodrobond Jul 27 '18 at 22:55
  • You're right, I updated my question. I also want to get variables that exist in a function out of the express context. – JM-AGMS Jul 27 '18 at 23:17
  • `I also want to get variables that exist in a function out of the express context` - which variables and contexts? The express error handler should give you access to the req, combine that with the stack trace and...I'm wondering what other variables you would need for diagnosis? – Hodrobond Jul 27 '18 at 23:22
  • In my testing, I'm unable to catch uncaught exceptions using `app.use(function(err...) {})` (I do have it defined at the very end) when they occur in a function that does not pass the req variables as an arugment, at least without using the `process.on` method I describe above. The variables can be anything passed as an argument or generated inside the function. – JM-AGMS Jul 27 '18 at 23:28
  • 1
    Sorry, I'm having quite a bit of difficulty picturing where the errors occur in relation to your express routes, and what is holding what data. Could you post some more code? – Hodrobond Jul 27 '18 at 23:32
  • Forget about Express, it's just made it more confusing. I updated my post again, should be clearer. – JM-AGMS Jul 27 '18 at 23:40
  • `doTheBatman`!!! Sorry, this isn't constructive, I just couldn't resist ^v^ – scniro Jul 28 '18 at 00:02

2 Answers2

1

A synchronous exception in an Express route handler will be caught by Express itself and the exception will be passed off the default Express error handler where you can catch it yourself and the exception context is passed to that default express error handler.

You can see this code inside of Express where a route handler gets called:

Layer.prototype.handle_request = function handle(req, res, next) {
  var fn = this.handle;

  if (fn.length > 3) {
    // not a standard request handler
    return next();
  }

  try {
    fn(req, res, next);
  } catch (err) {
    next(err);
  }
};

The call to next(err) will pass the exception object off to the default Express error handler (which you can install a handler for).


If your code is throwing an exception inside of an asynchronous callback, then that is more complicated to catch in action. If you're using regular async callbacks (not promises), then the only way I know of to catch those at a meaningful spot is to put a try/catch inside of every async callback so you can capture the local stack info.

If you use promises at the lowest level and only program your logic and asynchronous code flow use promise functionality, then an exception in a promise handler will automatically turn into a rejected promise and certain promise libraries (like the Bluebird library can be configured to give you a pretty full stack trace of where things went wrong). Promises have this advantage because every promise .then() or .catch() handler is automatically wrapped in a try/catch handler and those are turned into promise rejections which propagate up the chain to the first .catch() handler they find.


For the non-Express example you just added to your question, you will just have to put a try/catch somehwere in the local neighborhood to catch a local synchronous exception:

function doTheBatman(var1) {
  try {
      var var2 = 'whatever';
      // this causes an uncaught exception later in the code
  } catch(e) {
      console.log(e);
  }
}

You can even set a debugger breakpoint in the catch handler and then examine variables in that scope when it is hit. I don't know of any way to examine the local variables at the point of the exception from the uncaughtException handler. err.stack should give you the stack trace, but not variable values.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Thanks for your answer. I have my own reporting function that can accept an object of variables when an error happens, but I would have to know when the error is likely to happen. Putting `try...catch` every where on my code is impractical for me, but would technically work if I were to put the time and effort into doing it every time I write more code. I would rather fallback on `process.on` and pass variables if possible. – JM-AGMS Jul 28 '18 at 00:04
  • Well, `process.on('uncaughtException`, ...)` will give you the stack trace in `err.stack`, but not the variables in each scope of the stack trace. If you're trying to write a robust server, you have to catch exceptions before `process.on('uncaughtException`, ...)` anyway because at that point, you've lost the ability to handle the exception gracefully in an appropriate local context. Robust server code requires `try/catch` handlers around any code that has unchecked input or parameters or that can throw an exception. You either have to catch the exception locally or prevent the exception. – jfriend00 Jul 28 '18 at 00:08
  • That's true, and for ~99% of my code, I do my own error logic. However, I'm not perfect and I will sometimes screw something up, or my code fails to catch something. I have `process.on` as a last ditch resort to report errors. – JM-AGMS Jul 28 '18 at 00:15
  • @JM-AGMS - Well, it appears you're asking for a feature that does not exist. I'm just telling you how it works today. I understand why one would log `uncaughtException`, but that will give you a stack trace which may take a lot more debugging and probably some additional instrumentation to find out what caused the uncaughtException in the first place which should then ultimately result in adding more `try/catch` to your code as the long term solution. I too wish the `uncaughtException` error object would give you everything you might need to solve the problem, but it doesn't. – jfriend00 Jul 28 '18 at 00:34
1

Express/frameworks may offer more elegant/robust solutions, but if you're truly after what your question asks for... why not just capture variables outside of the functions scope? This is typically nasty and not considered best practice, but if you have a function that you know could be susceptive to failing often, perhaps the quick and dirty solution is what you need. You could always refine this later, but hopefully this demonstrates the approach...

var transactionVars = {};

function clearTransaction() {
  transactionVars = {};
}

function doTheBatman(var1) {
  transactionVars['var2'] = 'whatever';
  // [...] bunch of stuff, possibly blowing up...
  clearTransaction(); // we made it this far? cool, reset...
}

process.on('uncaughtException', function(err) {
  console.log(transactionVars['var2']); // whatever
});

Furthermore, if you want to really dirty (in case these two functions are in two files) you can always tack transactionVars on the global object.

This is essentially a poor mans event emitter pattern, which I would highly recommend refactoring into once you grasp the general flow of how this works...

scniro
  • 16,844
  • 8
  • 62
  • 106
  • haha, I suppose that could work and using it directly on `process` would save a step, redeclaring the variable again at the start of the function would also save another step. But its up there with defining `try...catch` (which would be a better practice) on all my code and is a lot of error handling to maintain. – JM-AGMS Jul 28 '18 at 00:32
  • @JM-AGMS yea, its up to you how meticulous you want to be with `try...catch` I feel like OCD level error handling is _okay_ and will serve you well. This approach is just about the total opposite, but if you find value in a global "catch all" error hander than affords you to inspect stateful variables, well, this would suffice – scniro Jul 28 '18 at 00:50