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}