The ErrorInfo List

Jakob Jenkov
Last update: 2014-05-26

As the AppException is propagated up the call stack, a list of ErrorInfo objects are built up inside it. In this text I will examine that list, and how to extract information from it.

The ErrorInfo list can contain more than one ErrorInfo object. So, how do you determine what type of error the AppException represents? What is the severity? What error message should you show to the user? Or write to the application log? I mean, given that you could have multiple severities and error types embedded in the different ErrorInfo objects, how do you determine the real type and severity of the error?

A ConfigFileLoader Example

Here is an example ErrorInfo list for an exception thrown during the loading of an important configuration file:

errorId contextId type severity cause
FileLoadError FileLoader client error FileNotFoundException
ConfigFileLoadError ConfigFileLoader internal fatal  

This ErrorInfo list originates from an AppException being thrown by a FileLoader component. This component only knows that the file loading error, is of type client (wrong path given), and of severity error.

The FileLoader component was called by a ComfigFileLoader component. The ConfigFileLoader component knows that if configuration file is not loaded correctly, the whole application will not work correctly. Therefore the ConfigFileLoader adds that information to the ErrorInfo list. It sets the error type to internal, since either the configuration file is missing, or the paths to the configuration file is wrong. This is an internal error. Additionally, the ConfigFileLoader sets the severity to fatal, since the application cannot run correctly without the configuration file.

So, which of the two ErrorInfo objects should we use, when notifying users and non-users?

The answer is: A combination of them all.

In the example above, it is the second ErrorInfo object (ConfigFileLoader) that contains most correct information about the severity and type of the error. However, the first ErrorInfo object contains more information about what failed (FileNotFoundException).

So, when it comes to severity and type, use the latest ErrorInfo object. When it comes to logging diagnostic information, use all the ErrorInfo objects.

A UserFileLoader Example

Here, I will look at a similar example to the one mentioned above:

errorId contextId type severity cause
FileLoadError FileLoader client error FileNotFoundException
UserFileLoadError UserFileLoader client warning  

The FileLoader component is called by the UserFileLoader, which loads files which the user of the application provides the paths for. Thus, if an error occurrs during the loading of that file, it is not an error in the application. It is most likely just the user providing an invalid file path, or the file pointed to has invalid content. As soon as the user points the application to an existing, valid file, the error will go away.

A StreamLoader Example

Imagine that the fileLoader() method calls a streamLoader() method underneath. The streamLoader() method is given a stream to load the file from. The streamLoader() method does not know where the stream is connected to, but the calling method most likely does. If the stream loading fails, here is an example of how the ErrorInfo list could look:

errorId contextId type severity cause parameters
StreamLoadError StreamLoader client error    
FileLoadError FileLoader        

Notice how the second ErrorInfo object does not update the severity field. It has no new information about the severity, so it leaves it set to whatever the first ErrorInfo object set it to.

Of course, in a real application, the fileLoader() method would probably be called from some other method, which may actually know more about the severity of the error. Like this:

errorId contextId type severity cause parameters
StreamLoadError StreamLoader client error    
FileLoadError FileLoader        
UserFileLoadError UserFileLoader   warning    

Notice, however, how the last ErrorInfo object does not know anything new about the errorType, so it leaves it untouched.

Extracting Severity and Error Type from the ErrorInfo List

The severity and error type of a given AppException is used to determine how to handle the exception, and which group of relevant parties to notify. Since the ErrorInfo list can contain multiple severity and error type codes, you need to somehow filter them down to one each. Otherwise, how do you know what to do about a given exception? I mean, if it is both listed as a warning and an error, and as a service and client error?

If you follow the rule of only setting the error type and severity if you actually know something new about it, you should use the latest severity and error type. Here is a code example:

int errorType = -1;

for(ErrorInfo errorInfo : e.getErrorInfoList()){

    if(errorInfo.getErrorType() != -1) {
        errorType = errorInfo.getErrorType();
    }
}

    
int severity  = -1;

for(ErrorInfo errorInfo : e.getErrorInfoList()){

    if(errorInfo.getSeverity() != -1) {
        severity = errorInfo.getSeverity();
    }

}

After the above two for-loops have executed, the errorType and severity variables will contain the latest set value of error type and severity.

You may decide to encapsulate the two loops in each their method (I probably would). You probably won't be able to measure the performance difference, since exceptions do not happen that often, and since the ErrorInfo list is probably not going to be that deep.

Extracting Error ID from the ErrorInfo List

Extracting the error ID and error descriptions from the ErrorInfo object list is a bit different than extracting error type and severity.

For error type and severity you are only interested in one value. For error ID and error descriptions you are interested in all the values. The complete error id is thus composed of all the error id's and context id's in ErrorInfo list.

Here is a code example method that illustrates how to extract the total error ID:

public static String extractErrorId(AppException e){
    StringBuilder builder = new StringBuilder();

    for(int i=e.getErrorInfoList().size()-1; i>=0; i--){

        ErrorInfo errorInfo = e.getErrorInfoList().get(i);

        builder.append(errorInfo.getContextId());
        builder.append(":");
        builder.append(errorInfo.getErrorId());

        if(i>0){
            builder.append("/");
        }
    }

    return builder.toString();
}

This method creates a total error ID which is made up of all the context ID's and error ID's of the ErrorInfo list.

Notice that the list is iterated backwards, from last to first ErrorInfo object. Remember, the ErrorInfo objects are added to the AppException on the way up the call stack. The path to the exception in context ID's and error ID's is thus bottom up in the list. By iterating the list in reverse order, the path is changed to top down, reflecting the context path from the top of the application down to the location of the error.

Here is an example total error ID:

UserFileLoader:FileLoadError/FileLoader:FileLoadError    

Extracting User Error Descriptions

The user should be given a simple error message when something fails in your application. If the error is caused by the users behaviour, and thus can be corrected by the user, tell the user what the error was, and how to correct it.

If, on the other hand, the error is caused by something which is out of the users hands, don't give the user too much detail. Just let the user know that the requested action failed, and that it has been logged and will be investigated. You can use pretty much the same user error message for all these types of errors, as giving the user internal error ID's, parameter information etc. is not going to help the user. On the contrary, you risk giving away information that can be used by hackers to break into your application.

Error Type Determines User Error Descriptions

You look at the errorType to determine if the error was caused by the user, or something else. If the error type is "client", that means that the client (the user) has used the application in a wrong way. By correcting the usage, the error will go away. In this case, tell the user what went wrong, and tell them how to correct their error.

If the errorType is either service or internal, then you all you do is to show the user a standard error message, saying that the error has been logged, and will be investigated.

Extracting Non-User Error Descriptions

The non-user error description is used by the application operators and developers to determine the cause of the error, and how to correct it. Therefore, it should be as detailed as possible. The non-user error description should therefore include all the ErrorInfo objects details, and not just one of ErrorInfo objects.

The full error description becomes quite large, and possibly messy and unstructured. Therefore, it might make sense to convert it to an XML structure, and log that full structure. Having an XML structure for the error details may also make it easier for tools to monitor the log, and extract the bits of information out of it, they need.

Here is an example XML structure for an error report:

<error>

  <fullErrorId>
    UserFileLoader:LoadError/FileLoader:FileLoadError
  </fullErrorId>

  <errorType>client</errorType>
  <severity>warning</severity>


  <errorInfoList>

    <errorInfo>
      <contextId>FileLoader</contextId>
      <errorId>FileLoadError</errorId>
      <errorType>client</errorType>
      <severity>error</severity>
      <errorDescription>
          The file was found, but could not be parsed correctly.
          The following error was found in the file: ...
      </errorDescription>
      <parameters>
        <parameter name="file">c:\data\myFile.txt</parameter>
      </parameters>
    </errorInfo>

    <errorInfo>
      <contextId>UserFileLoader</contextId>
      <errorId>FileLoadError</errorId>
      <errorType>client</errorType>
      <severity>warning</severity>
      <errorDescription>
          An error occurred during the processing of a file which
          the user requested the application to process.
      </errorDescription>
      <errorCorrection>
          Point to a file that actually exists.
      </errorCorrection>
      <parameters>
        <parameter name="file">c:\data\myFile.txt</parameter>
      </parameters>
    </errorInfo>

  </errorInfoList>

</error>

Jakob Jenkov

Featured Videos

Java ConcurrentMap + ConcurrentHashMap

Java Generics

Java ForkJoinPool

P2P Networks Introduction

















Close TOC
All Tutorial Trails
All Trails
Table of contents (TOC) for this tutorial trail
Trail TOC
Table of contents (TOC) for this tutorial
Page TOC
Previous tutorial in this tutorial trail
Previous
Next tutorial in this tutorial trail
Next