001package ca.uhn.fhir.rest.client.interceptor; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2017 University Health Network 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import java.io.IOException; 024import java.io.InputStream; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028 029import org.apache.commons.io.IOUtils; 030import org.apache.commons.lang3.Validate; 031import org.slf4j.Logger; 032 033import ca.uhn.fhir.rest.client.IClientInterceptor; 034import ca.uhn.fhir.rest.client.api.IHttpRequest; 035import ca.uhn.fhir.rest.client.api.IHttpResponse; 036import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 037 038public class LoggingInterceptor implements IClientInterceptor { 039 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoggingInterceptor.class); 040 041 private Logger myLog = ourLog; 042 private boolean myLogRequestBody = false; 043 private boolean myLogRequestHeaders = false; 044 private boolean myLogRequestSummary = true; 045 private boolean myLogResponseBody = false; 046 private boolean myLogResponseHeaders = false; 047 private boolean myLogResponseSummary = true; 048 049 /** 050 * Constructor for client logging interceptor 051 */ 052 public LoggingInterceptor() { 053 super(); 054 } 055 056 /** 057 * Constructor for client logging interceptor 058 * 059 * @param theVerbose 060 * If set to true, all logging is enabled 061 */ 062 public LoggingInterceptor(boolean theVerbose) { 063 if (theVerbose) { 064 setLogRequestBody(true); 065 setLogRequestSummary(true); 066 setLogResponseBody(true); 067 setLogResponseSummary(true); 068 setLogRequestHeaders(true); 069 setLogResponseHeaders(true); 070 } 071 } 072 073 @Override 074 public void interceptRequest(IHttpRequest theRequest) { 075 if (myLogRequestSummary) { 076 myLog.info("Client request: {}", theRequest); 077 } 078 079 if (myLogRequestHeaders) { 080 StringBuilder b = headersToString(theRequest.getAllHeaders()); 081 myLog.info("Client request headers:\n{}", b.toString()); 082 } 083 084 if (myLogRequestBody) { 085 try { 086 String content = theRequest.getRequestBodyFromStream(); 087 if (content != null) { 088 myLog.info("Client request body:\n{}", content); 089 } 090 } catch (IllegalStateException e) { 091 myLog.warn("Failed to replay request contents (during logging attempt, actual FHIR call did not fail)", e); 092 } catch (IOException e) { 093 myLog.warn("Failed to replay request contents (during logging attempt, actual FHIR call did not fail)", e); 094 } 095 } 096 } 097 098 @Override 099 public void interceptResponse(IHttpResponse theResponse) throws IOException { 100 if (myLogResponseSummary) { 101 String message = "HTTP " + theResponse.getStatus() + " " + theResponse.getStatusInfo(); 102 myLog.info("Client response: {}", message); 103 } 104 105 if (myLogResponseHeaders) { 106 StringBuilder b = headersToString(theResponse.getAllHeaders()); 107 // if (theResponse.getEntity() != null && theResponse.getEntity().getContentEncoding() != null) { 108 // Header next = theResponse.getEntity().getContentEncoding(); 109 // b.append(next.getName() + ": " + next.getValue()); 110 // } 111 // if (theResponse.getEntity() != null && theResponse.getEntity().getContentType() != null) { 112 // Header next = theResponse.getEntity().getContentType(); 113 // b.append(next.getName() + ": " + next.getValue()); 114 // } 115 if (b.length() == 0) { 116 myLog.info("Client response headers: (none)"); 117 } else { 118 myLog.info("Client response headers:\n{}", b.toString()); 119 } 120 } 121 122 if (myLogResponseBody) { 123 //TODO: Use of a deprecated method should be resolved. 124 theResponse.bufferEntitity(); 125 InputStream respEntity = null; 126 try { 127 respEntity = theResponse.readEntity(); 128 if (respEntity != null) { 129 final byte[] bytes; 130 try { 131 bytes = IOUtils.toByteArray(respEntity); 132 } catch (IllegalStateException e) { 133 throw new InternalErrorException(e); 134 } 135 myLog.info("Client response body:\n{}", new String(bytes, "UTF-8")); 136 } else { 137 myLog.info("Client response body: (none)"); 138 } 139 } finally { 140 IOUtils.closeQuietly(respEntity); 141 } 142 } 143 } 144 145 private StringBuilder headersToString(Map<String, List<String>> theHeaders) { 146 StringBuilder b = new StringBuilder(); 147 if (theHeaders != null && !theHeaders.isEmpty()) { 148 Iterator<String> nameEntries = theHeaders.keySet().iterator(); 149 while(nameEntries.hasNext()) { 150 String key = nameEntries.next(); 151 Iterator<String> values = theHeaders.get(key).iterator(); 152 while(values.hasNext()) { 153 String value = values.next(); 154 b.append(key + ": " + value); 155 if (nameEntries.hasNext() || values.hasNext()) { 156 b.append('\n'); 157 } 158 } 159 } 160 } 161 return b; 162 } 163 164 /** 165 * Sets a logger to use to log messages (default is a logger with this class' name). This can be used to redirect 166 * logs to a differently named logger instead. 167 * 168 * @param theLogger 169 * The logger to use. Must not be null. 170 */ 171 public void setLogger(Logger theLogger) { 172 Validate.notNull(theLogger, "theLogger can not be null"); 173 myLog = theLogger; 174 } 175 176 /** 177 * Should a summary (one line) for each request be logged, containing the URL and other information 178 */ 179 public void setLogRequestBody(boolean theValue) { 180 myLogRequestBody = theValue; 181 } 182 183 /** 184 * Should headers for each request be logged, containing the URL and other information 185 */ 186 public void setLogRequestHeaders(boolean theValue) { 187 myLogRequestHeaders = theValue; 188 } 189 190 /** 191 * Should a summary (one line) for each request be logged, containing the URL and other information 192 */ 193 public void setLogRequestSummary(boolean theValue) { 194 myLogRequestSummary = theValue; 195 } 196 197 /** 198 * Should a summary (one line) for each request be logged, containing the URL and other information 199 */ 200 public void setLogResponseBody(boolean theValue) { 201 myLogResponseBody = theValue; 202 } 203 204 /** 205 * Should headers for each request be logged, containing the URL and other information 206 */ 207 public void setLogResponseHeaders(boolean theValue) { 208 myLogResponseHeaders = theValue; 209 } 210 211 /** 212 * Should a summary (one line) for each request be logged, containing the URL and other information 213 */ 214 public void setLogResponseSummary(boolean theValue) { 215 myLogResponseSummary = theValue; 216 } 217 218}