Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interceptors: provide exception class when there is no message #526

Closed

Conversation

martinklepsch
Copy link

@martinklepsch martinklepsch commented Jun 30, 2017

Some exceptions don't have an exception message which leads to (unhelpful) exception messages like

Interceptor Exception:

This change falls back to the exception class if there's no message, common cases could be arithmetic exceptions or (rand-nth []). With the proposed change the above would change to this:

Interceptor Exception: java.lang.IndexOutOfBoundsException

We also see Interceptor Exception: Interceptor Exception: ... lots of times, not sure if that is intended or not?

@martinklepsch martinklepsch changed the title provide exception class when there is no message Interceptors: provide exception class when there is no message Jun 30, 2017
@@ -30,7 +30,8 @@
(get interceptor :name (pr-str interceptor)))

(defn- throwable->ex-info [^Throwable t execution-id interceptor stage]
(ex-info (str "Interceptor Exception: " (.getMessage t))
(ex-info (str "Interceptor Exception: " (or (.getMessage t)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the intent to also fall-back to the exc class name if user supplied an empty message?
e.g: if the user did (SomeException. "") ?
In which case, perhaps need to do something like (non-empty (.getMessage t)).
Otherwise, looks fine.

@ohpauleez
Copy link
Member

Thanks for pulling this together! I'm going to address it in a few separate changes shortly.

@martinklepsch
Copy link
Author

Sounds great. Anything that improves these error messages would be a big improvement cutting down time to diagnose issues. :)

@ohpauleez
Copy link
Member

Just so you're aware. There is a lot more information in the ex-data than just in the log message. Often times what I do is setup the error handler interceptor to catch and format exceptions while I'm developing (adding to its patterns as business cases come up)

@ohpauleez
Copy link
Member

This improvement is now on master. Thanks so much!

@ohpauleez ohpauleez closed this Jul 12, 2017
@martinklepsch
Copy link
Author

Very nice, thanks!

@ohpauleez
Copy link
Member

Excellent to hear, you're very welcome!

@martinklepsch martinklepsch deleted the ex-message-fallback branch July 13, 2017 15:34
@martinklepsch
Copy link
Author

@ohpauleez I'm not sure how feasible this is but — following situation: I have a bunch of interceptors and they also carry some additional data. Now when an exception occurs I'd like to access the full interceptor to be able to inspect that data.

For this reason it would be handy to be able to obtain a reference to the first-throwing interceptor in the error handler. One way of doing that would be providing my own throwable->ex-info but maybe there are other approaches as well?

@ohpauleez
Copy link
Member

I'll look into it today. Certain feasible, just have to see: A.) Does it make sense to do that (vs something else, like giving better access to the context, so you know what interceptor fired based on that) B.) Do any changes to how data is packaged up break the error-handler interceptor C.) Should we advise developers against smugglng additional data in the interceptor itself :)

Could you elaborate on what kind of data you're smuggling in the interceptor and why you chose to do that over alternative options?

@martinklepsch
Copy link
Author

martinklepsch commented Jul 13, 2017

Sure.

  • I have a bunch of data structures that are turned into interceptors at runtime.
  • The interceptors are parameterized by values in those data structures (functions, strings, etc).
  • For this reason I have many interceptors with the same name (e.g. :parse) but different combinations of parameters.
  • Because of that any kind of debugging tool needs access to the originating data structure.

For interceptors that run without throwing I store originating data structure in the context under ::processed but for throwing interceptors that's not possible. Previously I replicated the chain's :queue and :stack using the originating data structures to always have access to them but then this turned out buggy and impractical for different reasons.

Hope this helps, if you want to discuss more also feel free to ping me in the Clojurians Slack (same nick).

EDIT Oh, and I vote against option C 😛

@martinklepsch
Copy link
Author

This would make serializing exceptions more tricky and so I'm not sure if that's a good path to go down but a simple/obvious solution could just store the entire interceptor in ex-data.

@martinklepsch
Copy link
Author

Hey @ohpauleez — was just wondering if you came up with any further thoughts on this?

Would be happy to come up with a PR but I guess code is the easier part here :)

@ohpauleez
Copy link
Member

Hi @martinklepsch -- Sorry it's taken me a bit to follow up here!

When you turn the data structures into Interceptors, have you considered programmatically generating names for them? This might help you when debugging and handling errors. For example, Vase requires names in the data structure (a very direct approach), but you might consider adding unique pieces instead of calling them all :parse.

Smuggling interceptors through to errors

In the past, I've had success smuggling the interceptor into the data of an ex-info generated exception. If you take this approach your "erroring/failing" interceptor needs to catch the core exception and rethrow it. You can get the "current" interceptor from the context by looking at the top of the stack, :io.pedestal.interceptor.chain/stack. ex-info is already designed for this sort of interaction:

(throw (ex-info "Some error message here" {:interceptor (peek (:io.pedestal.interceptor.chain/stack context))} original-exception-or-throwable)

@martinklepsch
Copy link
Author

Thanks @ohpauleez, np about the wait :)

Generating names programatically is something I considered but the configuration I hand to those interceptors is not encodable inside a keyword (e.g. some take URLs as parameters).

The advice with catching & rethrowing is good and I might use that. That said this still seems useful to be included in the library by default & as you mentioned you've used this hack yourself so seems you also came up with a situation where you needed it :)

@ohpauleez
Copy link
Member

ohpauleez commented Aug 3, 2017

@martinklepsch I haven't needed it since I introduced throwable->ex-info and the pattern-matching error interceptor. :)

That said, I appreciate the feedback, conversation, and support. We'll make sure the suggestion gets captured on the backlog. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants