001package ca.uhn.fhir.rest.client.interceptor; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2016 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.ByteArrayInputStream; 024import java.io.IOException; 025import java.io.InputStream; 026import java.io.OutputStream; 027 028import org.apache.commons.io.IOUtils; 029import org.apache.commons.lang3.Validate; 030import org.apache.http.Header; 031import org.apache.http.HttpEntity; 032import org.apache.http.HttpEntityEnclosingRequest; 033import org.apache.http.HttpResponse; 034import org.apache.http.client.methods.HttpRequestBase; 035import org.apache.http.entity.HttpEntityWrapper; 036import org.slf4j.Logger; 037 038import ca.uhn.fhir.rest.client.IClientInterceptor; 039import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 040 041public class LoggingInterceptor implements IClientInterceptor { 042 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoggingInterceptor.class); 043 044 private Logger myLog = ourLog; 045 private boolean myLogRequestBody = false; 046 private boolean myLogRequestHeaders = false; 047 private boolean myLogRequestSummary = true; 048 private boolean myLogResponseBody = false; 049 private boolean myLogResponseHeaders = false; 050 private boolean myLogResponseSummary = true; 051 052 /** 053 * Constructor 054 */ 055 public LoggingInterceptor() { 056 super(); 057 } 058 059 /** 060 * Constructor 061 * 062 * @param theVerbose 063 * If set to true, all logging is enabled 064 */ 065 public LoggingInterceptor(boolean theVerbose) { 066 if (theVerbose) { 067 setLogRequestBody(true); 068 setLogRequestSummary(true); 069 setLogResponseBody(true); 070 setLogResponseSummary(true); 071 setLogRequestHeaders(true); 072 setLogResponseHeaders(true); 073 } 074 } 075 076 @Override 077 public void interceptRequest(HttpRequestBase theRequest) { 078 if (myLogRequestSummary) { 079 myLog.info("Client request: {}", theRequest); 080 } 081 082 if (myLogRequestHeaders) { 083 StringBuilder b = new StringBuilder(); 084 for (int i = 0; i < theRequest.getAllHeaders().length; i++) { 085 Header next = theRequest.getAllHeaders()[i]; 086 b.append(next.getName() + ": " + next.getValue()); 087 if (i + 1 < theRequest.getAllHeaders().length) { 088 b.append('\n'); 089 } 090 } 091 myLog.info("Client request headers:\n{}", b.toString()); 092 } 093 094 if (myLogRequestBody) { 095 if (theRequest instanceof HttpEntityEnclosingRequest) { 096 HttpEntity entity = ((HttpEntityEnclosingRequest) theRequest).getEntity(); 097 if (entity.isRepeatable()) { 098 try { 099 String content = IOUtils.toString(entity.getContent()); 100 myLog.info("Client request body:\n{}", content); 101 } catch (IllegalStateException e) { 102 myLog.warn("Failed to replay request contents (during logging attempt, actual FHIR call did not fail)", e); 103 } catch (IOException e) { 104 myLog.warn("Failed to replay request contents (during logging attempt, actual FHIR call did not fail)", e); 105 } 106 } 107 } 108 } 109 110 } 111 112 @Override 113 public void interceptResponse(HttpResponse theResponse) throws IOException { 114 if (myLogResponseSummary) { 115 String message = "HTTP " + theResponse.getStatusLine().getStatusCode() + " " + theResponse.getStatusLine().getReasonPhrase(); 116 myLog.info("Client response: {}", message); 117 } 118 119 if (myLogResponseHeaders) { 120 StringBuilder b = new StringBuilder(); 121 if (theResponse.getAllHeaders() != null) { 122 for (int i = 0; i < theResponse.getAllHeaders().length; i++) { 123 Header next = theResponse.getAllHeaders()[i]; 124 b.append(next.getName() + ": " + next.getValue()); 125 if (i + 1 < theResponse.getAllHeaders().length) { 126 b.append('\n'); 127 } 128 } 129 } 130 // if (theResponse.getEntity() != null && theResponse.getEntity().getContentEncoding() != null) { 131 // Header next = theResponse.getEntity().getContentEncoding(); 132 // b.append(next.getName() + ": " + next.getValue()); 133 // } 134 // if (theResponse.getEntity() != null && theResponse.getEntity().getContentType() != null) { 135 // Header next = theResponse.getEntity().getContentType(); 136 // b.append(next.getName() + ": " + next.getValue()); 137 // } 138 if (b.length() == 0) { 139 myLog.info("Client response headers: (none)"); 140 } else { 141 myLog.info("Client response headers:\n{}", b.toString()); 142 } 143 } 144 145 if (myLogResponseBody) { 146 HttpEntity respEntity = theResponse.getEntity(); 147 if (respEntity != null) { 148 final byte[] bytes; 149 try { 150 bytes = IOUtils.toByteArray(respEntity.getContent()); 151 } catch (IllegalStateException e) { 152 throw new InternalErrorException(e); 153 } 154 155 myLog.info("Client response body:\n{}", new String(bytes, "UTF-8")); 156 theResponse.setEntity(new MyEntityWrapper(respEntity, bytes)); 157 } else { 158 myLog.info("Client response body: (none)"); 159 } 160 } 161 } 162 163 /** 164 * Sets a logger to use to log messages (default is a logger with this class' name). This can be used to redirect 165 * logs to a differently named logger instead. 166 * 167 * @param theLogger 168 * The logger to use. Must not be null. 169 */ 170 public void setLogger(Logger theLogger) { 171 Validate.notNull(theLogger, "theLogger can not be null"); 172 myLog = theLogger; 173 } 174 175 /** 176 * Should a summary (one line) for each request be logged, containing the URL and other information 177 */ 178 public void setLogRequestBody(boolean theValue) { 179 myLogRequestBody = theValue; 180 } 181 182 /** 183 * Should headers for each request be logged, containing the URL and other information 184 */ 185 public void setLogRequestHeaders(boolean theValue) { 186 myLogRequestHeaders = theValue; 187 } 188 189 /** 190 * Should a summary (one line) for each request be logged, containing the URL and other information 191 */ 192 public void setLogRequestSummary(boolean theValue) { 193 myLogRequestSummary = theValue; 194 } 195 196 /** 197 * Should a summary (one line) for each request be logged, containing the URL and other information 198 */ 199 public void setLogResponseBody(boolean theValue) { 200 myLogResponseBody = theValue; 201 } 202 203 /** 204 * Should headers for each request be logged, containing the URL and other information 205 */ 206 public void setLogResponseHeaders(boolean theValue) { 207 myLogResponseHeaders = theValue; 208 } 209 210 /** 211 * Should a summary (one line) for each request be logged, containing the URL and other information 212 */ 213 public void setLogResponseSummary(boolean theValue) { 214 myLogResponseSummary = theValue; 215 } 216 217 private static class MyEntityWrapper extends HttpEntityWrapper { 218 219 private byte[] myBytes; 220 221 public MyEntityWrapper(HttpEntity theWrappedEntity, byte[] theBytes) { 222 super(theWrappedEntity); 223 myBytes = theBytes; 224 } 225 226 @Override 227 public InputStream getContent() throws IOException { 228 return new ByteArrayInputStream(myBytes); 229 } 230 231 @Override 232 public void writeTo(OutputStream theOutstream) throws IOException { 233 theOutstream.write(myBytes); 234 } 235 236 } 237 238}