001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.rest.client;
014
015import static org.apache.juneau.internal.StringUtils.*;
016
017import java.io.*;
018import java.util.logging.*;
019
020import org.apache.http.*;
021import org.apache.http.client.methods.*;
022import org.apache.http.util.*;
023
024/**
025 * Specialized interceptor for logging calls to a log file.
026 *
027 * <p>
028 * Causes a log entry to be created that shows all the request and response headers and content at the end of the
029 * request.
030 *
031 * <p>
032 * Use the {@link RestClientBuilder#logTo(Level, Logger)} and {@link RestCall#logTo(Level, Logger)} methods to create
033 * instances of this class.
034 */
035public class RestCallLogger extends RestCallInterceptor {
036
037   /**
038    * Default HTTP request logger.
039    * <p>
040    * Logs outgoing HTTP requests to the <code>org.apache.juneau.rest.client</code> logger at <jsf>WARNING</jsf> level.
041    */
042   public static final RestCallLogger DEFAULT = new RestCallLogger(Level.WARNING, Logger.getLogger("org.apache.juneau.rest.client"));
043
044   private Level level;
045   private Logger log;
046
047   /**
048    * Constructor.
049    *
050    * @param level The log level to log messages at.
051    * @param log The logger to log to.
052    */
053   protected RestCallLogger(Level level, Logger log) {
054      this.level = level;
055      this.log = log;
056   }
057
058   @Override /* RestCallInterceptor */
059   public void onInit(RestCall restCall) {
060      if (log.isLoggable(level))
061         restCall.captureResponse();
062   }
063
064   @Override /* RestCallInterceptor */
065   public void onConnect(RestCall restCall, int statusCode, HttpRequest req, HttpResponse res) {
066      // Do nothing.
067   }
068
069   @Override /* RestCallInterceptor */
070   public void onRetry(RestCall restCall, int statusCode, HttpRequest req, HttpResponse res, Exception ex) {
071      if (log.isLoggable(level)) {
072         if (ex == null)
073            log.log(level, format("Call to {0} returned {1}.  Will retry.", req.getRequestLine().getUri(), statusCode));
074         else
075            log.log(level, format("Call to {0} caused exception {1}.  Will retry.", req.getRequestLine().getUri(), ex.getLocalizedMessage()), ex);
076      }
077   }
078
079   @Override /* RestCallInterceptor */
080   public void onClose(RestCall restCall) throws RestCallException {
081      try {
082         if (log.isLoggable(level)) {
083            String output = restCall.getCapturedResponse();
084            StringBuilder sb = new StringBuilder();
085            HttpUriRequest req = restCall.getRequest();
086            HttpResponse res = restCall.getResponse();
087            if (req != null) {
088               sb.append("\n=== HTTP Call (outgoing) =======================================================");
089
090               sb.append("\n=== REQUEST ===\n").append(req);
091               sb.append("\n---request headers---");
092               for (Header h : req.getAllHeaders())
093                  sb.append("\n\t").append(h);
094               if (req instanceof HttpEntityEnclosingRequestBase) {
095                  sb.append("\n---request entity---");
096                  HttpEntityEnclosingRequestBase req2 = (HttpEntityEnclosingRequestBase)req;
097                  HttpEntity e = req2.getEntity();
098                  if (e == null)
099                     sb.append("\nEntity is null");
100                  else {
101                     if (e.getContentType() != null)
102                        sb.append("\n").append(e.getContentType());
103                     if (e.getContentEncoding() != null)
104                        sb.append("\n").append(e.getContentEncoding());
105                     if (e.isRepeatable()) {
106                        try {
107                           sb.append("\n---request content---\n").append(EntityUtils.toString(e));
108                        } catch (Exception ex) {
109                           throw new RuntimeException(ex);
110                        }
111                     }
112                  }
113               }
114            }
115            if (res != null) {
116               sb.append("\n=== RESPONSE ===\n").append(res.getStatusLine());
117               sb.append("\n---response headers---");
118               for (Header h : res.getAllHeaders())
119                  sb.append("\n\t").append(h);
120               sb.append("\n---response content---\n").append(output);
121               sb.append("\n=== END ========================================================================");
122            }
123            log.log(level, sb.toString());
124         }
125      } catch (IOException e) {
126         log.log(Level.SEVERE, e.getLocalizedMessage(), e);
127      }
128   }
129}