All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mypurecloud.sdk.SLF4JInterceptor Maven / Gradle / Ivy

The newest version!
package com.mypurecloud.sdk;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import org.apache.http.Header;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.RequestLine;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.message.BasicRequestLine;
import org.apache.http.protocol.HttpContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 

A filter that logs both requests and responses to SLF4J. * *

Available detail levels

*
    *
  • NONE - don't log anything *
  • MINIMAL - only log the verb, url, and response code *
  • HEADERS - as above, but also log all the headers for both the request and response *
  • FULL - as above, but also log the full body for both the request and response */ public class SLF4JInterceptor implements HttpRequestInterceptor, HttpResponseInterceptor { private static final Logger LOGGER = LoggerFactory.getLogger(SLF4JInterceptor.class); // Attribute for tracking requests and responses private static final String SLF4J_REQUEST_DATA = "slf4j-request-data"; private volatile DetailLevel detailLevel; public SLF4JInterceptor() { this(DetailLevel.MINIMAL); } public SLF4JInterceptor(DetailLevel detailLevel) { this.detailLevel = detailLevel; } /** * The level of detail to log * *
      *
    • NONE - don't log anything *
    • MINIMAL - only log the verb, url, and response code *
    • HEADERS - as above, but also log all the headers for both the request and response *
    • FULL - as above, but also log the full body for both the request and response */ public static enum DetailLevel { NONE, MINIMAL, HEADERS, FULL } /** * @return the current detail level of the filter */ public DetailLevel getDetailLevel() { return detailLevel; } /** * Sets the detail level * @param detailLevel - the new detail level to use */ public void setDetailLevel(DetailLevel detailLevel) { this.detailLevel = detailLevel; } private static class RequestData { public final RequestLine requestLine; public final long startTime; private RequestData(RequestLine requestLine, long startTime) { this.requestLine = requestLine; this.startTime = startTime; } } @Override public void process(HttpRequest request, HttpContext context) throws HttpException, IOException { if (LOGGER.isDebugEnabled()) { RequestData requestData = new RequestData(request.getRequestLine(), System.currentTimeMillis()); context.setAttribute(SLF4J_REQUEST_DATA, requestData); logRequest(request); } } @Override public void process(HttpResponse response, HttpContext context) throws HttpException, IOException { if (LOGGER.isDebugEnabled()) { RequestLine requestLine; long tookMs; Object reqDataAttr = context.getAttribute(SLF4J_REQUEST_DATA); if (reqDataAttr == null || !(reqDataAttr instanceof RequestData)) { LOGGER.error("Could not determine the request associated with this response"); requestLine = new BasicRequestLine("", "", null); tookMs = -1; } else { RequestData requestData = (RequestData) reqDataAttr; requestLine = requestData.requestLine; tookMs = System.currentTimeMillis() - requestData.startTime; } logResponse(response, requestLine, tookMs); } } /** * Builds the log message for requests * *
           * >>>> GET http://api.example.com/endpoint >>>>
           * ---- HEADERS ----
           * Header-1: Value1
           * Header-2: Value2
           * ---- BODY (24-bytes) ----
           * Body body body body body
           * >>>> END >>>>
           * 
      * * @param request - the request to build a message for */ private void logRequest(HttpRequest request) throws IOException { if (detailLevel.compareTo(DetailLevel.MINIMAL) >= 0) { final StringBuilder messageBuilder = new StringBuilder(); // Log the verb and url String uriString = String.format(">>>> %s %s >>>>", request.getRequestLine().getMethod(), request.getRequestLine().getUri()); messageBuilder.append(uriString).append(System.lineSeparator()); // Add the headers if (detailLevel.compareTo(DetailLevel.HEADERS) >= 0) { messageBuilder.append("---- HEADERS ----").append(System.lineSeparator()); messageBuilder.append(formatHeaders(request.getAllHeaders())); // Add the request body if it exists if (detailLevel.compareTo(DetailLevel.FULL) >= 0) { // This is ugly, but it's the only way to access the body if (request instanceof HttpEntityEnclosingRequest && ((HttpEntityEnclosingRequest) request).getEntity() != null) { byte[] data = extractRequestBody((HttpEntityEnclosingRequest) request); messageBuilder.append(String.format("---- BODY (%d bytes) ----", data.length)).append(System.lineSeparator()); messageBuilder.append(new String(data)).append(System.lineSeparator()); } else { messageBuilder.append("---- NO BODY ----").append(System.lineSeparator()); } } messageBuilder.append(">>>> END >>>>").append(System.lineSeparator()); } LOGGER.debug(messageBuilder.toString()); } } /** * Builds the log message for responses * *
           * <<<< GET http://api.example.com/endpoint <<<<
           * 404 Not Found  (219 ms)
           * ---- HEADERS ----
           * Header-3: Value3
           * Header-4: Value4
           * ---- NO BODY ----
           * <<<< END <<<<
           * 
      * * @param response - the response to build a message for * @param request - the request line of the initial request for the response * @param tookMs - how long the request took, in milliseconds */ private void logResponse(HttpResponse response, RequestLine requestLine, long tookMs) throws IOException { if (detailLevel.compareTo(DetailLevel.MINIMAL) >= 0) { StringBuilder messageBuilder = new StringBuilder(); // Log the verb and url, along with the status code String uriString = String.format("<<<< %s %s <<<<", requestLine.getMethod(), requestLine.getUri()); messageBuilder.append(uriString).append(System.lineSeparator()); messageBuilder.append(String.format(" %d %s (%d ms)", response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase(), tookMs)) .append(System.lineSeparator()); // Append the headers if (detailLevel.compareTo(DetailLevel.HEADERS) >= 0) { messageBuilder.append("---- HEADERS ----").append(System.lineSeparator()); messageBuilder.append(formatHeaders(response.getAllHeaders())); // Add the response body if it exists if (detailLevel.compareTo(DetailLevel.FULL) >= 0) { // Write the log message if (response.getEntity() != null) { byte[] responseBody = extractResponseBody(response); messageBuilder.append(String.format("---- BODY (%d bytes) ----", responseBody.length)).append(System.lineSeparator()); messageBuilder.append(new String(responseBody)).append(System.lineSeparator()); } else { messageBuilder.append("---- NO BODY ----").append(System.lineSeparator()); } } messageBuilder.append("<<<< END <<<<").append(System.lineSeparator()); } LOGGER.debug(messageBuilder.toString()); } } /** * Extracts the body of a request, resetting the stream if necessary so * that the request behaves as if it were unchanged * * @return the body of the response */ private static final byte[] extractRequestBody(HttpEntityEnclosingRequest request) throws IOException { byte[] data = toByteArray(request.getEntity().getContent()); // Reset the response input stream if necessary if (!request.getEntity().isRepeatable()) { request.setEntity(new ByteArrayEntity(data, ContentType.get(request.getEntity()))); } return data; } /** * Extracts the body of a response, resetting the stream if necessary so * that the response behaves as if it were unchanged * * @return the body of the response */ private static final byte[] extractResponseBody(HttpResponse response) throws IOException { byte[] data = toByteArray(response.getEntity().getContent()); // Reset the response input stream if necessary if (!response.getEntity().isRepeatable()) { response.setEntity(new ByteArrayEntity(data, ContentType.get(response.getEntity()))); } return data; } /** * Reads an input stream into a byte array, then closes the stream * * @return an array containing all of the data from the stream */ private static final byte[] toByteArray(InputStream response) throws IOException { final int BUFFER_SIZE = 2048; // How many bytes to copy at once // Clone the stream by reading it into a byte array byte[] buffer = new byte[BUFFER_SIZE]; ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream(); try (InputStream stream = response) { int read; while ((read = stream.read(buffer)) != -1) { byteArrayStream.write(buffer, 0, read); } byteArrayStream.flush(); } return byteArrayStream.toByteArray(); } private static class HeaderComparator implements Comparator
      { @Override public int compare(Header a, Header b) { return a.getName().compareTo(b.getName()); } } /** * Formats an array of headers into a human-readable format * @param headers - the headers * @return a string containing all of the headers */ private static String formatHeaders(Header[] headers) { List
      sortedHeaders = Arrays.asList(headers); sortedHeaders.sort(new HeaderComparator()); StringBuilder sb = new StringBuilder(); for (Header header : sortedHeaders) { String headerString = String.format("%s: %s", header.getName(), header.getValue()); sb.append(headerString).append(System.lineSeparator()); } return sb.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy