--- parent: Decision Records nav_order: 14 --- # 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 * Code should be easy to read * Include URL in the exception instead of logging in case an exception is thrown already (see ) ## 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 ```java 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: ```java 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 ```java 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`)