4

I thrift IDL files you can define your own exception types that a service method may throw:

exception SampleException {
    1: list<string> failed
}

Whats the contract for seeing a proper exception message in the logs? I have seen log messages like this: Unhandled exception in foo.bar.Service my.package.SomeException: null. I guess where it says null this should be the exception message?

Unfortunately exceptions are not very well documented. Should I be adding a string message field for that to work? Is there any other convertion like name of the field?

JensG
  • 12,102
  • 4
  • 40
  • 51
reikje
  • 2,474
  • 2
  • 19
  • 35

1 Answers1

3

Thrift exceptions (in general)

Whats the contract for seeing a proper exception message in the logs?

No contract defines how the message is written into any log. Thrift is designed to be protocol-agnostic and transport-agnostic. For example, if the HTTP server you built writes something into his log files, this is completely outside of Thrift, it is a concern of your HTTP server only.

Should I be adding a string message field for that to work? Is there any other convention like name of the field?

No, there are neither conventions nor restrictions, other than what the language of your choice may impose. Technically an exception is just like a struct. As an example, the following IDL is taken from the ThriftTest.thrift file.

exception Xception2 {
  1: i32 errorCode,
  2: Xtruct struct_thing
}

The Xtruct used above is another struct nested into the exception. You see, you can virtually anything do with Thrift exceptions that is possible with structs as well. This enables you to send rich error information to the client, rather than just a string saying "Something ugly happened on the server" or the like.

The difference to struct lies in how the system treats them: Exceptions are serialized on the server, then re-raised on the client. Unexpected exceptions are

  • either caught and re-thrown as generic TApplicationException
  • or not caught at all

The latter behaviour is a somewhat language dependent thing which is about to change right now slowly from "not caught" into the "rethrow as generic TApplicationException" direction.

Unfortunately exceptions are not very well documented

This is what the Thrift Whitepaper has to say about exceptions:

2.4 Exceptions

Exceptions are syntactically and functionally equivalent to structs except that they are declared using the exception keyword instead of the struct keyword.

The generated objects inherit from an exception base class as appropriate in each target programming language, in order to seamlessly integrate with native exception handling in any given language. Again, the design emphasis is on making the code familiar to the application developer.

How do I make sure the Exception(string) CTOR is called?

I have seen log messages like this:

Unhandled exception in foo.bar.Service my.package.SomeException: null 

I guess where it says null this should be the exception message? [...] for defined exceptions that get generated to Scala/Java, what is the proper way to make sure this constructor is called: public Exception(String message).

Looks very much like "never", or more precise "no way". These are all the CTORs that Thrift (trunk version) generates out of the IDL above:

  public Xception() {
  }

  public Xception(
    int errorCode,
    String message)
  {
    this();
    this.errorCode = errorCode;
    setErrorCodeIsSet(true);
    this.message = message;
  }

  /**
   * Performs a deep copy on <i>other</i>.
   */
  public Xception(Xception other) {
    __isset_bitfield = other.__isset_bitfield;
    this.errorCode = other.errorCode;
    if (other.isSetMessage()) {
      this.message = other.message;
    }
  }

Since all generated exceptions derive from TException they have to call one of the parameterized CTORs to get the message and/or the cause down to the Exception base. But Xception obviously does not do this. The reason is probably that Thrift enables you to define anything as an exception member, and guarantees (or assumes) nothing. In other words, one cannot be sure that there is even a single field at the exception. Therefore you always end up with a null error message.

Bottom line (sort of TL;DR)

I think, generally speaking you are right and this behaviour should be changed. For now, the only workaround I see is to explicitly catch TException before they go into the log and repackage them using the toString() member function:

} catch (org.apache.thrift.TException te) {
  throw new Exception(te.toString());
}
JensG
  • 12,102
  • 4
  • 40
  • 51
  • 2
    sorry I didn't mean to step on anyones toes. That Thrift Whitepaper never came up when I searched for the exceptions documentation. – reikje May 27 '15 at 09:48
  • Oh, [we have a lot of documentation now](http://stackoverflow.com/a/20664706/499466). I agree that this was not always the case in the past, but especially the whitepaper exists since 2007 and is very prominently linked from the [home page](http://thrift.apache.org/). – JensG May 27 '15 at 09:54
  • 2
    I rephrase: for defined exceptions that get generated to Scala/Java, what is the proper way to make sure this constructor is called: `public Exception(String message)`. – reikje May 27 '15 at 09:58