1

I am primarily from a Java background (and hence a Java mindset) so I'm not sure if I'm making any sense here.

I'm trying to find the correct way to log an error (thrown somewhere higher in the stack, caught via a lower-level generic catch block later) into the browser's web console, while displaying correct stacktrace information in a nice collapsible way. The following is an example, from the Slack chat web client running on Firefox:

Slack's error messages, with nice, collapsible stacktraces!

As you can see, by default only the error message is displayed (first log), with a collapsible arrow icon in front which, when clicked, expands the full stacktrace (second log). Pretty neat, I would say.

This is what I have tried:

Creating an error inside a nested function call:

h = () => TypeError("b"); g = () => h(); f = () => g(); e = f(); 

The resulting error object is now stored in e.

e, when passed to console.error(), does show a collapsed stacktrace, but apparently it reflects the invocation location of console.error() itself (the trace has just a single <anonymous> debugger eval code line), in addition to a non-collapsible copy of the correct stacktrace (containing the f, g and h functions):

Collapsed (default) view, which still shows the (correct) stacktrace:

'console.error(e)' collapsed; see the ugly stacktrace that doesn't collapse?

Expanded view, showing two stacktraces (with the now-expanded 'wrong' stacktrace at the bottom):

same thing, with the 'collapse' expanded; now we see two stacktraces (including the 'fake' one that had been collapsed earlier)

Alternatively, if I do something like:

h = () => { throw TypeError("b"); } g = () => h(); f = () => g(); e = f(); 

I just get a one-liner without a stacktrace:

uncaught throw, no stacktrace in Firefox

(Note.: Chrome seems to behave differently here, giving out something similar to console.error(e); in that case, both stacktraces are correct ones, but still there are two traces, one being non-collapsible.)

If I do the console.error() only using a message text, in-place (right where the error is supposed to originate):

h = () => { console.error("b"); } g = () => h(); f = () => g(); e = f(); 

I see the expected behaviour:

'console.error()' in-place, with the collapsible stacktrace expanded; just like Slack

However, obviously this is not a feasible solution because what I need is to log an arbitrary error (generated somewhere higher in the stack) at a lower-level (say, a master catch block in the original driver function).

h = () => { throw TypeError("b"); } g = () => h(); f = () => g(); try { e = f(); } catch (e) { // expecting to see an error log, with one nice, collapsible stacktrace console.error(e); } 

One of my colleagues suggested that the proper way to do this might be to override the default console.error() function with my own custom implementation, and that is how Slack might also be doing the magic. Is that the only workaround, or is there a more 'standardized' (and cross-browser) solution that I can use out of the box (preferably without relying on external libraries)?

1 Answer 1

1

(Note: Chrome and Firefox handle this differently).

You're catching the error at the top level and handling it there. In firefox, you see the stack, but it does not collapse.

Conceptually, it works like this. Imagine the carat is the movement of the error.

^ excution > ^ function > ^ function > ^ function > error thrown (no handling) // shows stack. ^ excution (handling) > function > function > function > error thrown // does not show stack (in Chrome), because you're handling up here, ^ excution > ^ function > ^ function > ^ function > error thrown, caught. Catch uses console.error // shows stack because the error is at a different layer in the stack 

Now look at an edited version of your example, where the handling is lower.

h = () => { try { throw TypeError("b"); } catch (e) { // expecting to see an error log, with one nice, collapsible stacktrace console.error(e); } } g = () => h(); f = () => g(); f(); 

This code displays the stack.

Your "catching" the error at the level where you want it handled. The point of putting it deeper into the calls, is so that you can handle the error affecting the code that called the function the threw in the way you wanted it to.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks @Steven! However, the code you provided, also displays two copies of the stacktrace, one of which is not collapsible (looks like I'm not allowed to add a snapshot here, but you can easily try it out on a browser; same behaviour in both FF and Chrome) which is not the behaviour I was expecting :(
Additionally, while your suggestion of handling the error in-place is indeed the proper way to do things, I'm trying to catch 'unexpected' generic errors that are not being handled higher in the stack (I already handle several error cases at higher levels, but the best way to handle some generic ones is to just throw them and later handle them at base components; since adding a handler for 'oops, a generic error occurred' in every nested try-catch block does not make sense :) )

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.