From 7bcd2b6434d89a64cd3ab3a84c18c90015dce089 Mon Sep 17 00:00:00 2001 From: markiantorno Date: Mon, 8 Nov 2021 16:10:04 -0500 Subject: [PATCH] Adding changes for logging request and resp --- RELEASE_NOTES.md | 1 + .../hl7/fhir/r4/context/HTMLClientLogger.java | 56 +++++++++--------- .../r4/utils/client/FHIRToolingClient.java | 6 -- .../fhir/r4/utils/client/network/Client.java | 6 +- .../network/FhirLoggingInterceptor.java | 55 ++++++++++++++++++ .../client/network/FhirRequestBuilder.java | 41 +++---------- org.hl7.fhir.r5/pom.xml | 6 ++ .../hl7/fhir/r5/context/HTMLClientLogger.java | 58 +++++++++---------- .../r5/utils/client/FHIRToolingClient.java | 6 -- .../fhir/r5/utils/client/network/Client.java | 7 ++- .../network/FhirLoggingInterceptor.java | 55 ++++++++++++++++++ .../client/network/FhirRequestBuilder.java | 43 +++----------- .../r5/utils/client/network/ClientTest.java | 22 +++++++ org.hl7.fhir.utilities/pom.xml | 6 ++ .../fhir/utilities/ToolingClientLogger.java | 29 +++++----- 15 files changed, 243 insertions(+), 154 deletions(-) create mode 100644 org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirLoggingInterceptor.java create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirLoggingInterceptor.java diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e69de29bb..82f813c50 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -0,0 +1 @@ +* Updating client logger to log both req and resp \ No newline at end of file diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/HTMLClientLogger.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/HTMLClientLogger.java index 494d5ea6b..27b192dc0 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/HTMLClientLogger.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/HTMLClientLogger.java @@ -1,33 +1,33 @@ package org.hl7.fhir.r4.context; -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/FHIRToolingClient.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/FHIRToolingClient.java index c45db06a2..3c5ef33d7 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/FHIRToolingClient.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/FHIRToolingClient.java @@ -276,15 +276,9 @@ public class FHIRToolingClient { URI url = resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps); if (complex) { byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat())); - if (client.getLogger() != null) { - client.getLogger().logRequest("POST", url.toString(), null, body); - } result = client.issuePostRequest(url, body, getPreferredResourceFormat(), generateHeaders(), "POST " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG); } else { - if (client.getLogger() != null) { - client.getLogger().logRequest("GET", url.toString(), null, null); - } result = client.issueGetResourceRequest(url, getPreferredResourceFormat(), generateHeaders(), "GET " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG); } if (result.isUnsuccessfulRequest()) { diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/Client.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/Client.java index e8ffc8727..a39265c31 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/Client.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/Client.java @@ -19,6 +19,7 @@ public class Client { public static final String DEFAULT_CHARSET = "UTF-8"; private static final long DEFAULT_TIMEOUT = 5000; private ToolingClientLogger logger; + private FhirLoggingInterceptor fhirLoggingInterceptor; private int retryCount; private long timeout = DEFAULT_TIMEOUT; @@ -28,6 +29,7 @@ public class Client { public void setLogger(ToolingClientLogger logger) { this.logger = logger; + this.fhirLoggingInterceptor = new FhirLoggingInterceptor(logger); } public int getRetryCount() { @@ -167,7 +169,7 @@ public class Client { int retryCount, long timeout) throws IOException { return new FhirRequestBuilder(request) - .withLogger(logger) + .withLogger(fhirLoggingInterceptor) .withResourceFormat(resourceFormat) .withRetryCount(retryCount) .withMessage(message) @@ -183,7 +185,7 @@ public class Client { int retryCount, long timeout) throws IOException { return new FhirRequestBuilder(request) - .withLogger(logger) + .withLogger(fhirLoggingInterceptor) .withResourceFormat(resourceFormat) .withRetryCount(retryCount) .withMessage(message) diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirLoggingInterceptor.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirLoggingInterceptor.java new file mode 100644 index 000000000..9775308b5 --- /dev/null +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirLoggingInterceptor.java @@ -0,0 +1,55 @@ +package org.hl7.fhir.r4.utils.client.network; + +import okhttp3.*; +import org.hl7.fhir.utilities.ToolingClientLogger; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class FhirLoggingInterceptor implements Interceptor { + + private ToolingClientLogger logger; + + public FhirLoggingInterceptor(ToolingClientLogger logger) { + this.logger = logger; + } + + public FhirLoggingInterceptor setLogger(ToolingClientLogger logger) { + this.logger = logger; + return this; + } + + @NotNull + @Override + public Response intercept(@NotNull Interceptor.Chain chain) throws IOException { + // Log Request + Request request = chain.request(); + logger.logRequest(request.method(), request.url().toString(), new ArrayList<>(request.headers().names()), + request.body() != null ? request.body().toString().getBytes() : null); + + // Log Response + Response response = null; + response = chain.proceed(chain.request()); + + MediaType contentType = null; + byte[] bodyBytes = null; + if (response.body() != null) { + contentType = response.body().contentType(); + bodyBytes = response.body().bytes(); + } + + // Get Headers as List + List headerList = new ArrayList<>(); + Map> headerMap = response.headers().toMultimap(); + headerMap.keySet().forEach(key -> headerMap.get(key).forEach(value -> headerList.add(key + ":" + value))); + + logger.logResponse(Integer.toString(response.code()), headerList, bodyBytes); + + // Reading byte[] clears body. Need to recreate. + ResponseBody body = ResponseBody.create(bodyBytes, contentType); + return response.newBuilder().body(body).build(); + } +} diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirRequestBuilder.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirRequestBuilder.java index b34ce59dd..95729dfc7 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirRequestBuilder.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirRequestBuilder.java @@ -45,10 +45,11 @@ public class FhirRequestBuilder { * Time unit for {@link FhirRequestBuilder#timeout}. */ private TimeUnit timeoutUnit = TimeUnit.MILLISECONDS; + /** - * {@link ToolingClientLogger} for log output. + * {@link FhirLoggingInterceptor} for log output. */ - private ToolingClientLogger logger = null; + private FhirLoggingInterceptor logger = null; public FhirRequestBuilder(Request.Builder httpRequest) { this.httpRequest = httpRequest; @@ -160,7 +161,11 @@ public class FhirRequestBuilder { .build(); }; - return okHttpClient.newBuilder() + OkHttpClient.Builder builder = okHttpClient.newBuilder(); + if (logger != null) builder.addInterceptor(logger); + builder.addInterceptor(new RetryInterceptor(retryCount)); + + return builder.connectTimeout(timeout, timeoutUnit) .addInterceptor(new RetryInterceptor(retryCount)) .connectTimeout(timeout, timeoutUnit) .writeTimeout(timeout, timeoutUnit) @@ -189,7 +194,7 @@ public class FhirRequestBuilder { return this; } - public FhirRequestBuilder withLogger(ToolingClientLogger logger) { + public FhirRequestBuilder withLogger(FhirLoggingInterceptor logger) { this.logger = logger; return this; } @@ -228,7 +233,6 @@ public class FhirRequestBuilder { if (response.body() != null) { try { byte[] body = response.body().bytes(); - log(response.code(), response.headers(), body); resource = (T) getParser(format).parse(body); if (resource instanceof OperationOutcome && hasError((OperationOutcome) resource)) { error = (OperationOutcome) resource; @@ -255,7 +259,6 @@ public class FhirRequestBuilder { OperationOutcome error = null; try { byte[] body = response.body().bytes(); - log(response.code(), response.headers(), body); String contentType = response.header("Content-Type"); if (body != null) { if (contentType.contains(ResourceFormat.RESOURCE_XML.getHeader()) || contentType.contains("text/xml+fhir")) { @@ -301,30 +304,4 @@ public class FhirRequestBuilder { throw new EFhirClientException("Invalid format: " + format); } } - - /** - * Logs the given {@link Response}, using the current {@link ToolingClientLogger}. If the current - * {@link FhirRequestBuilder#logger} is null, no action is taken. - * - * @param responseCode HTTP response code - * @param responseHeaders {@link Headers} from response - * @param responseBody Byte array response - */ - protected void log(int responseCode, Headers responseHeaders, byte[] responseBody) { - if (logger != null) { - List headerList = new ArrayList<>(Collections.emptyList()); - Map> headerMap = responseHeaders.toMultimap(); - headerMap.keySet().forEach(key -> headerMap.get(key).forEach(value -> headerList.add(key + ":" + value))); - - try { - logger.logResponse(Integer.toString(responseCode), headerList, responseBody); - } catch (Exception e) { - System.out.println("Error parsing response body passed in to logger ->\n" + e.getLocalizedMessage()); - } - } -// else { // TODO fix logs -// System.out.println("Call to log HTTP response with null ToolingClientLogger set... are you forgetting to " + -// "initialize your logger?"); -// } - } } diff --git a/org.hl7.fhir.r5/pom.xml b/org.hl7.fhir.r5/pom.xml index df3d5fcff..02a5dbb7f 100644 --- a/org.hl7.fhir.r5/pom.xml +++ b/org.hl7.fhir.r5/pom.xml @@ -107,6 +107,12 @@ 4.9.0 true + + com.squareup.okhttp3 + logging-interceptor + 4.9.0 + true + diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/HTMLClientLogger.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/HTMLClientLogger.java index 4096a5dc0..2d955c4c6 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/HTMLClientLogger.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/HTMLClientLogger.java @@ -1,33 +1,33 @@ package org.hl7.fhir.r5.context; -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ @@ -42,7 +42,7 @@ import org.hl7.fhir.utilities.Utilities; public class HTMLClientLogger extends BaseLogger implements ToolingClientLogger { - private static final boolean DEBUG = false; + private static final boolean DEBUG = true; private PrintStream file; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java index ade033db5..9369070ed 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java @@ -308,15 +308,9 @@ public class FHIRToolingClient { URI url = resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps); if (complex) { byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat())); - if (client.getLogger() != null) { - client.getLogger().logRequest("POST", url.toString(), null, body); - } result = client.issuePostRequest(url, body, getPreferredResourceFormat(), generateHeaders(), "POST " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG); } else { - if (client.getLogger() != null) { - client.getLogger().logRequest("GET", url.toString(), null, null); - } result = client.issueGetResourceRequest(url, getPreferredResourceFormat(), generateHeaders(), "GET " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG); } if (result.isUnsuccessfulRequest()) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/Client.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/Client.java index f24e054fa..45a71b77b 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/Client.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/Client.java @@ -4,6 +4,7 @@ import okhttp3.Headers; import okhttp3.MediaType; import okhttp3.Request; import okhttp3.RequestBody; +import okhttp3.logging.HttpLoggingInterceptor; import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.utils.client.EFhirClientException; @@ -19,6 +20,7 @@ public class Client { public static final String DEFAULT_CHARSET = "UTF-8"; private static final long DEFAULT_TIMEOUT = 5000; private ToolingClientLogger logger; + private FhirLoggingInterceptor fhirLoggingInterceptor; private int retryCount; private long timeout = DEFAULT_TIMEOUT; @@ -28,6 +30,7 @@ public class Client { public void setLogger(ToolingClientLogger logger) { this.logger = logger; + this.fhirLoggingInterceptor = new FhirLoggingInterceptor(logger); } public int getRetryCount() { @@ -168,7 +171,7 @@ public class Client { int retryCount, long timeout) throws IOException { return new FhirRequestBuilder(request) - .withLogger(logger) + .withLogger(fhirLoggingInterceptor) .withResourceFormat(resourceFormat) .withRetryCount(retryCount) .withMessage(message) @@ -184,7 +187,7 @@ public class Client { int retryCount, long timeout) throws IOException { return new FhirRequestBuilder(request) - .withLogger(logger) + .withLogger(fhirLoggingInterceptor) .withResourceFormat(resourceFormat) .withRetryCount(retryCount) .withMessage(message) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirLoggingInterceptor.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirLoggingInterceptor.java new file mode 100644 index 000000000..d012b0acd --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirLoggingInterceptor.java @@ -0,0 +1,55 @@ +package org.hl7.fhir.r5.utils.client.network; + +import okhttp3.*; +import org.hl7.fhir.utilities.ToolingClientLogger; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class FhirLoggingInterceptor implements Interceptor { + + private ToolingClientLogger logger; + + public FhirLoggingInterceptor(ToolingClientLogger logger) { + this.logger = logger; + } + + public FhirLoggingInterceptor setLogger(ToolingClientLogger logger) { + this.logger = logger; + return this; + } + + @NotNull + @Override + public Response intercept(@NotNull Interceptor.Chain chain) throws IOException { + // Log Request + Request request = chain.request(); + logger.logRequest(request.method(), request.url().toString(), new ArrayList<>(request.headers().names()), + request.body() != null ? request.body().toString().getBytes() : null); + + // Log Response + Response response = null; + response = chain.proceed(chain.request()); + + MediaType contentType = null; + byte[] bodyBytes = null; + if (response.body() != null) { + contentType = response.body().contentType(); + bodyBytes = response.body().bytes(); + } + + // Get Headers as List + List headerList = new ArrayList<>(); + Map> headerMap = response.headers().toMultimap(); + headerMap.keySet().forEach(key -> headerMap.get(key).forEach(value -> headerList.add(key + ":" + value))); + + logger.logResponse(Integer.toString(response.code()), headerList, bodyBytes); + + // Reading byte[] clears body. Need to recreate. + ResponseBody body = ResponseBody.create(bodyBytes, contentType); + return response.newBuilder().body(body).build(); + } +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirRequestBuilder.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirRequestBuilder.java index 1e55afe54..ed6d0406e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirRequestBuilder.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirRequestBuilder.java @@ -45,10 +45,11 @@ public class FhirRequestBuilder { * Time unit for {@link FhirRequestBuilder#timeout}. */ private TimeUnit timeoutUnit = TimeUnit.MILLISECONDS; + /** - * {@link ToolingClientLogger} for log output. + * {@link FhirLoggingInterceptor} for log output. */ - private ToolingClientLogger logger = null; + private FhirLoggingInterceptor logger = null; public FhirRequestBuilder(Request.Builder httpRequest) { this.httpRequest = httpRequest; @@ -160,9 +161,11 @@ public class FhirRequestBuilder { .build(); }; - return okHttpClient.newBuilder() - .addInterceptor(new RetryInterceptor(retryCount)) - .connectTimeout(timeout, timeoutUnit) + OkHttpClient.Builder builder = okHttpClient.newBuilder(); + if (logger != null) builder.addInterceptor(logger); + builder.addInterceptor(new RetryInterceptor(retryCount)); + + return builder.connectTimeout(timeout, timeoutUnit) .writeTimeout(timeout, timeoutUnit) .readTimeout(timeout, timeoutUnit) .proxyAuthenticator(proxyAuthenticator) @@ -189,7 +192,7 @@ public class FhirRequestBuilder { return this; } - public FhirRequestBuilder withLogger(ToolingClientLogger logger) { + public FhirRequestBuilder withLogger(FhirLoggingInterceptor logger) { this.logger = logger; return this; } @@ -228,7 +231,6 @@ public class FhirRequestBuilder { if (response.body() != null) { try { byte[] body = response.body().bytes(); - log(response.code(), response.headers(), body); resource = (T) getParser(format).parse(body); if (resource instanceof OperationOutcome && hasError((OperationOutcome) resource)) { error = (OperationOutcome) resource; @@ -255,7 +257,6 @@ public class FhirRequestBuilder { OperationOutcome error = null; try { byte[] body = response.body().bytes(); - log(response.code(), response.headers(), body); String contentType = response.header("Content-Type"); if (body != null) { if (contentType.contains(ResourceFormat.RESOURCE_XML.getHeader()) || contentType.contains("text/xml+fhir")) { @@ -301,30 +302,4 @@ public class FhirRequestBuilder { throw new EFhirClientException("Invalid format: " + format); } } - - /** - * Logs the given {@link Response}, using the current {@link ToolingClientLogger}. If the current - * {@link FhirRequestBuilder#logger} is null, no action is taken. - * - * @param responseCode HTTP response code - * @param responseHeaders {@link Headers} from response - * @param responseBody Byte array response - */ - protected void log(int responseCode, Headers responseHeaders, byte[] responseBody) { - if (logger != null) { - List headerList = new ArrayList<>(Collections.emptyList()); - Map> headerMap = responseHeaders.toMultimap(); - headerMap.keySet().forEach(key -> headerMap.get(key).forEach(value -> headerList.add(key + ":" + value))); - - try { - logger.logResponse(Integer.toString(responseCode), headerList, responseBody); - } catch (Exception e) { - System.out.println("Error parsing response body passed in to logger ->\n" + e.getLocalizedMessage()); - } - } -// else { // TODO fix logs -// System.out.println("Call to log HTTP response with null ToolingClientLogger set... are you forgetting to " + -// "initialize your logger?"); -// } - } } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/client/network/ClientTest.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/client/network/ClientTest.java index 67a70457b..cfca89294 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/client/network/ClientTest.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/client/network/ClientTest.java @@ -4,9 +4,11 @@ import okhttp3.HttpUrl; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; +import org.hl7.fhir.r5.context.HTMLClientLogger; import org.hl7.fhir.r5.formats.JsonParser; import org.hl7.fhir.r5.model.*; import org.junit.jupiter.api.*; +import org.mockito.Mockito; import java.io.IOException; import java.net.URI; @@ -143,4 +145,24 @@ class ClientTest { Assertions.assertArrayEquals(payload, recordedRequest.getBody().readByteArray(), "POST request payload does not match send data."); } + + @Test + @DisplayName("Testing the logger works.") + void test_logger() throws IOException, URISyntaxException, InterruptedException { + byte[] payload = ByteUtils.resourceToByteArray(patient, true, false); + server.enqueue( + new MockResponse() + .setResponseCode(200) + .setBody(new String(payload)) + ); + HTMLClientLogger mockLogger = Mockito.mock(HTMLClientLogger.class); + client.setLogger(mockLogger); + client.issuePostRequest(new URI(serverUrl.toString()), payload, + "xml", null, TIMEOUT); + server.takeRequest(); + Mockito.verify(mockLogger, Mockito.times(1)) + .logRequest(Mockito.anyString(), Mockito.anyString(), Mockito.anyList(), Mockito.any()); + Mockito.verify(mockLogger, Mockito.times(1)) + .logResponse(Mockito.anyString(), Mockito.anyList(), Mockito.any()); + } } \ No newline at end of file diff --git a/org.hl7.fhir.utilities/pom.xml b/org.hl7.fhir.utilities/pom.xml index da1f9d39e..f725b97a3 100644 --- a/org.hl7.fhir.utilities/pom.xml +++ b/org.hl7.fhir.utilities/pom.xml @@ -86,6 +86,12 @@ ${validator_test_case_version} test + + com.squareup.okhttp3 + logging-interceptor + 4.9.0 + compile + diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/ToolingClientLogger.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/ToolingClientLogger.java index 7bcbefb3a..daa6e739f 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/ToolingClientLogger.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/ToolingClientLogger.java @@ -1,5 +1,5 @@ -package org.hl7.fhir.utilities; - +package org.hl7.fhir.utilities; + /* Copyright (c) 2011+, HL7, Inc. All rights reserved. @@ -28,17 +28,16 @@ package org.hl7.fhir.utilities; POSSIBILITY OF SUCH DAMAGE. */ - - - -import java.util.List; - -public interface ToolingClientLogger { - - public void logRequest(String method, String url, List headers, byte[] body); - public void logResponse(String outcome, List headers, byte[] body); - public String getLastId(); - public void clearLastId(); - - + + + +import java.util.List; + +public interface ToolingClientLogger { + + void logRequest(String method, String url, List headers, byte[] body); + void logResponse(String outcome, List headers, byte[] body); + String getLastId(); + void clearLastId(); + } \ No newline at end of file