Showing posts with label boost. Show all posts
Showing posts with label boost. Show all posts

Saturday, October 9, 2010

Damn Java Socket Exception Messages

When creating an error message you should think about what information would be useful for understanding what went wrong. This should especially be true if you are creating a library that is likely to be used by many other systems. Providing good error messages up front means that even if the programmers using the library do not check and customize the messages, the user will still get a reasonable result. For some use cases, such as scripting, it can also be useful because it may be a quick one-off program where the goal is to quickly automate a repetitive task or perform some analysis. In this case, having useful default error messages can speed up the initial development so you get your answer faster.

In my case, I was checking logs for a system that crawls pages and hence attempts to resolve and connect to thousands of hosts. This system logs the exceptions, but unfortunately did not provide a customized message when doing so. Analyzing the logs showed that the two most common error messages were: 1) a failure to resolve the hostname and 2) failing to connect to an HTTP server on the host. An example error message for the first case is java.net.UnknownHostException: some-host-that-does-not-exist. This message is quite useful as the exception name explains the problem and the message tells me the name of the host that could not be resolved. An example message for the second case is java.net.SocketTimeoutException: connect timed out. This message is explains the problem, but doesn't given me the crucial information of what it was trying to connect to.

Though this can easily be fixed in the application code, it is disappointing that the default message is so bad. I have noticed that my opinion of a programming language or technology seems to go down steadily the longer I am forced to use it at work. Is the grass greener on one of the other sides? How do other languages, or rather the networking libraries they provide, fair for this use case? I looked at 13 options to see how many would give a decent error message for both use cases. The results were not very encouraging. Only one option, Go, had reasonable messages for both. For the host not found case 4 options included the hostname. Only two options provided the host and port in the failed to connect case. The results are summarized in the table below the fold with links to the source code and raw error messages.

Friday, July 30, 2010

Damn boost::program_options

I was dismayed by the awful help screen on one of our internal tools. The help message looked something like:
$ ./a.out -h 
Allowed options:
  -h [ --help ]                                                               s
                                                                              h
                          ... skipping ...
                                                                              e
                                                                              d
  -c [ --config ] arg (=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) c
                                                                              o
                                                                              n
                                                                              f
                                                                              i
                                                                              g
                                                                              u
                                                                              r
                                                                              a
                                                                              t
                                                                              i
                                                                              o
                                                                              n
                                                                              f
                                                                              i
                                                                              l
                                                                              e
Of course, this was an internal tool that is no longer being maintained. Poking at the source I found it was based on boost::program_options and was just writing the options_description to stdout. I found that troubling as I have used and recommended this library many times. The root of the problem seemed to be that the default value was based on a file on the system specified by an environment variable. On my system that path is quite long and there was only room for a description that was one character wide. I put together a quick test program to illustrate the problem:
// g++ -I/opt/local/include -L/opt/local/lib -lboost_program_options-mt boostopt.cpp

#include <cstdlib>
#include <iostream>
#include <string>

#include <boost/program_options.hpp>

using namespace std;
using namespace boost::program_options;

int
main(int argc, char **argv) {

    char *file = getenv("CONFIG");
    string config((file == NULL) ? "-" : file);

    options_description desc("Allowed options");
    desc.add_options()
        ("help,h",     "show this help message, some additional text "
                       "that is here for no other reason than to make "
                       "the message wrap when the help is printed")
        ("config,c",   value<string>(&config)->default_value(config),
                       "configuration file")
    ;

    variables_map vm;
    try {
        store(parse_command_line(argc, argv, desc), vm);
        notify(vm);
    } catch (const std::exception &e) {
        cerr << "Error: " << e.what() << endl
             << endl << desc << endl;
        return 1;
    }

    if (vm.count("help")) {
        cerr << desc << endl;
        return 2;
    }

    return 0;
}
However, to my surprise when I ran the test program I could not reproduce the issue. The output when I supply a large default value was still sane, it just wraps the description to the next line. For example:
$ env CONFIG=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ./a.out -h
Allowed options:
  -h [ --help ]                         show this help message, some additional
                                        text that is here for no other reason 
                                        than to make the message wrap when the 
                                        help is printed
  -c [ --config ] arg (=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)
                                        configuration file
A little research showed the problem was fixed in boost 1.42.0. And sure enough, if I try with boost 1.41.0, like what the internal tool was using, I can easily reproduce the problem:
$ env CONFIG=aaa ./a.out -h
Allowed options:
  -h [ --help ]              show this help message, some additional text that 
                             is here for no other reason than to make the 
                             message wrap when the help is printed
  -c [ --config ] arg (=aaa) configuration file

$ env CONFIG=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ./a.out -h
Allowed options:
  -h [ --help ]                                                              sh
                                                                             ow
                          ... skipping ...
                                                                             te
                                                                             d
  -c [ --config ] arg (=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) co
                                                                             nf
                                                                             ig
                                                                             ur
                                                                             at
                                                                             io
                                                                             n 
                                                                             fi
                                                                             le

$ env CONFIG=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ./a.out -h 
Allowed options:
  -h [ --help ]                                                               s
                                                                              h
                          ... skipping ...
                                                                              e
                                                                              d
  -c [ --config ] arg (=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) c
                                                                              o
                                                                              n
                                                                              f
                                                                              i
                                                                              g
                                                                              u
                                                                              r
                                                                              a
                                                                              t
                                                                              i
                                                                              o
                                                                              n
                                                                              f
                                                                              i
                                                                              l
                                                                              e
And luckily for me, there isn't any work to fix the tool other than bump the version of boost it depends on.