Separate URL creation to enable proper logging

Context and Problem Statement

Fetchers are failing. The reason why they are failing needs to be investigated.

  • Claim 1: Knowing the URL which was used to query the fetcher eases debugging
  • Claim 2: Somehow logging the URL eases debugging (instead of showing it in the debugger only)

How to properly log the URL used for fetching?

Decision Drivers

Considered Options

  • Separate URL creation
  • Create URL when logging the URL
  • Include URL creation as statement before the stream creation in the try-with-resources block

Decision Outcome

Chosen option: "Separate URL creation", because comes out best (see below).

Pros and Cons of the Options

Separate URL creation

    URL urlForQuery;
    try {
        urlForQuery = getURLForQuery(query);
    } catch (URISyntaxException | MalformedURLException | FetcherException e) {
        throw new FetcherException(String.format("Search URI %s is malformed", query), e);
    }
    try (InputStream stream = getUrlDownload(complexQueryURL).asInputStream()) {
        ...
    } catch (IOException e) {
        throw new FetcherException("A network error occurred while fetching from " + urlForQuery.toString(), e);
    } catch (ParseException e) {
        throw new FetcherException("An internal parser error occurred while fetching from " + urlForQuery.toString(), e);
    }
  • Good, because exceptions thrown at method are directly caught
  • Good, because exceptions in different statements belong to different catch blocks
  • Good, because code to determine URL is written once
  • OK, because "Java by Comparison" does not state anything about it
  • Bad, because multiple try/catch statements are required
  • Bad, because this style seems to be uncommon to Java coders

Create URL when logging the URL

The "logging" is done when throwing the exception.

Example code:

    try (InputStream stream = getUrlDownload(getURLForQuery(query)).asInputStream()) {
        ...
    } catch (URISyntaxException | MalformedURLException | FetcherException e) {
        throw new FetcherException(String.format("Search URI %s is malformed", query), e);
    } catch (IOException e) {
        try {
            throw new FetcherException("A network error occurred while fetching from " + getURLForQuery(query), e);
        } catch (URISyntaxException | MalformedURLException uriSyntaxException) {
            // does not happen
            throw new FetcherException("A network error occurred", e);
        }
    } catch (ParseException e) {
        try {
            throw new FetcherException("An internal parser error occurred while fetching from " + getURLForQuery(query), e);
        } catch (URISyntaxException | MalformedURLException uriSyntaxException) {
            // does not happen
            throw new FetcherException("An internal parser error occurred", e);
        }
    }
  • Good, because code inside the try statement stays the same
  • OK, because "Java by Comparison" does not state anything about it
  • Bad, because an additional try/catch-block is added to each catch statement
  • Bad, because needs a throw statement in the URISyntaxException catch block (even though at this point the exception cannot be thrown), because Java otherwise misses a return statement.

Include URL creation as statement before the stream creation in the try-with-resources block

    try (URL urlForQuery = getURLForQuery(query); InputStream stream = urlForQuery.asInputStream()) {
        ...
    } catch (URISyntaxException | MalformedURLException | FetcherException e) {
        throw new FetcherException(String.format("Search URI %s is malformed", query), e);
    } catch (IOException e) {
        throw new FetcherException("A network error occurred while fetching from " + urlForQuery.toString(), e);
    } catch (ParseException e) {
        throw new FetcherException("An internal parser error occurred while fetching from " + urlForQuery.toString(), e);
    }
  • Good, because the single try/catch-block can be kept
  • Good, because logical flow is kept
  • Bad, because does not compile (because URL is not an AutoClosable)