ably / ably-java

Java, Android, Clojure and Scala client library SDK for Ably realtime messaging service
https://ably.com/download
Apache License 2.0
86 stars 39 forks source link

Capture context rich error messages #621

Open sacOO7 opened 3 years ago

sacOO7 commented 3 years ago

┆Issue is synchronized with this Jira Task by Unito

sacOO7 commented 3 years ago

Proposed solution

https://github.com/ably/ably-java/blob/647575906f716598174f0fac3ee6a9fbb7f805d5/lib/src/main/java/io/ably/lib/types/ErrorInfo.java#L14 (as stated, ErrorInfo is an exception type encapsulating error information containing an Ably-specific error code and generic status code, but it's not a java exception ... yet)

mattheworiordan commented 3 years ago

I support this proposal, this is what I have done in Ruby, see https://github.com/ably/ably-ruby/blob/main/lib/ably/models/error_info.rb#L33-L57 and https://github.com/ably/ably-ruby/blob/main/lib/ably/exceptions.rb#L16.

@paddybyers @QuintinWillison WDYT?

In fact, shouldn't this be part of the spec, that where possible, we use a base exception class so that the underlying base exception itself is available.

QuintinWillison commented 3 years ago

We already have an AblyException class which encapsulates an ErrorInfo instance, which is an architectural choice - using composition - that had already been made (rightly or wrongly) in the past.

Modifying the inheritance tree above ErrorInfo (essentially widening its public interface) feels like a hammer to crack a nut, and is not the right place in the stack to do this.

There is already a well-known, idiomatic constructor on the base Exception class allowing us to pass a cause. Therefore this requirement may be solved by simply adding a constructor to our AblyException class that calls that.

Also, @sacOO7, can you please provide a breadcrumb link back to the original discussion that prompted you to create this issue so those passing by in future can gain more context as to the requirement. Thanks.

sacOO7 commented 3 years ago

Yes, I think we already have AblyException class. I was looking into the use cases where we use standalone ErrorInfo class and AblyException derived from ErrorInfo class. It seems at some of the places, we are not using AblyException class effectively (Like current case). I was also looking into the way exceptions are being handled.

Throwable is the superclass of all exceptions and errors. In a catch clause, it will not only catch all exceptions; it will also catch all errors. Errors are thrown by the JVM to indicate serious problems that are not intended to be handled by an application. Typical examples for that are the OutOfMemoryError or the StackOverflowError. Both are caused by situations that are outside of the control of the application and can’t be handled.

I am still digging more to find where and how AblyException and ErrorInfo classes are being used. As @QuintinWillison suggested, there is a strong possibility that we don't have to introduce a new base class for ErrorInfo. Current implementation should suffice for handling errors if we use it properly.

sacOO7 commented 3 years ago

So, I came up with findings after doing a deep analysis of current error handling implementation Observations are based on the following guidelines

1. Custom error handler shouldn't miss trail of actual error source

If you lose the stacktrace and message of the original exception, it will make it difficult to analyze the exceptional event that caused your exception. It becomes difficult while debugging the exact source of the issue The exception stacktrace is as important as error message

2. Code should not contain empty catch blocks.

Error caught is not clear it's not logged with a proper exception. You don’t know how the code will change in the future. Someone might remove the validation that prevented the exceptional event without recognizing that this creates a problem. Or the code that throws the exception gets changed and now throws multiple exceptions of the same class, and the calling code doesn’t prevent all of them.

3. Every exception should be logged using logger instead of System.err or println

When the logger is used for logging exception, it's easy to parse and read. Also, it's custom logger gives control over where to log the exception

4. Be specific about throwing and catching the exception

The more specific the exception that you throw is, the better. Always keep in mind that a coworker who doesn’t know your code (or maybe you in a few months) may need to call your method and handle the exception. And as a result, the caller of your method will be able to handle the exception better or avoid it with an additional check.

5. Throwables shouldn't be caught

Throwable is the superclass of all exceptions and errors. In a catch clause, it will not only catch all exceptions; it will also catch all errors. Errors are thrown by the JVM to indicate serious problems that are not intended to be handled by an application. Typical examples for that are the OutOfMemoryError or the StackOverflowError. Both are caused by situations that are outside of the control of the application and can’t be handled.

It seems we have code snippets that violate the above guidelines. Exceptions are supposed to be caught and well handled in order to get a clear idea about working/ non-working application behavior.

References -

  1. https://stackify.com/best-practices-exceptions-java/
  2. https://stackify.com/common-mistakes-handling-java-exception/
sacOO7 commented 3 years ago

https://github.com/ably/ably-java/blob/647575906f716598174f0fac3ee6a9fbb7f805d5/lib/src/main/java/io/ably/lib/transport/WebSocketTransport.java#L226

sacOO7 commented 3 years ago

https://github.com/ably/ably-java/blob/647575906f716598174f0fac3ee6a9fbb7f805d5/lib/src/main/java/io/ably/lib/types/ErrorInfo.java#L141

sacOO7 commented 3 years ago

https://github.com/ably/ably-java/blob/647575906f716598174f0fac3ee6a9fbb7f805d5/lib/src/main/java/io/ably/lib/http/HttpCore.java#L301

sacOO7 commented 3 years ago

https://github.com/ably/ably-java/blob/647575906f716598174f0fac3ee6a9fbb7f805d5/lib/src/main/java/io/ably/lib/realtime/ChannelStateListener.java#L62

https://github.com/ably/ably-java/blob/647575906f716598174f0fac3ee6a9fbb7f805d5/lib/src/main/java/io/ably/lib/types/MessageSerializer.java#L74

https://github.com/ably/ably-java/blob/647575906f716598174f0fac3ee6a9fbb7f805d5/lib/src/main/java/io/ably/lib/realtime/Presence.java#L362

and many other places

sacOO7 commented 3 years ago

https://github.com/ably/ably-java/blob/647575906f716598174f0fac3ee6a9fbb7f805d5/lib/src/main/java/io/ably/lib/http/Http.java#L49

sacOO7 commented 3 years ago

At some places, the only exception message is logged rather than the exception itself.

sacOO7 commented 3 years ago
QuintinWillison commented 3 years ago

Thanks for looking into this, @sacOO7. As explained elsewhere (internal), this is not high priority now so is going to remain in the backlog until some of the other higher priority issues have been completed.