com.hotelbeds.distribution.hotel_api_sdk.helpers.LoggingRequestInterceptor.java Source code

Java tutorial

Introduction

Here is the source code for com.hotelbeds.distribution.hotel_api_sdk.helpers.LoggingRequestInterceptor.java

Source

package com.hotelbeds.distribution.hotel_api_sdk.helpers;

/*
 * #%L
 * HotelAPI SDK
 * %%
 * Copyright (C) 2015 - 2016 HOTELBEDS TECHNOLOGY, S.L.U.
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 2.1 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
 * #L%
 */

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

import org.jooq.lambda.Unchecked;

import com.hotelbeds.distribution.hotel_api_sdk.HotelApiClient;
import com.hotelbeds.hotelapimodel.auto.util.AssignUtils;
import com.hotelbeds.hotelapimodel.auto.util.ObjectJoiner;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Connection;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.internal.http.HttpEngine;
import okio.Buffer;
import okio.BufferedSource;

@Data
@Slf4j
/**
 * An OkHttp interceptor that logs information about the requests and responses depending on the log level set.
 *
 * INFO logs just the most basic information about the request and response. DEBUG adds headers information TRACE shows the request, if present, and
 * response bodies, beautifying them as JSON objects if they match.
 *
 * Inspired by: https://github.com/square/okhttp/blob/master/okhttp-logging-interceptor/src/main/java/okhttp3/logging/HttpLoggingInterceptor.java
 */
public final class LoggingRequestInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        if (!log.isInfoEnabled()) {
            return chain.proceed(request);
        } else {
            final RequestBody requestBody = request.body();
            final boolean hasRequestBody = requestBody != null;
            final Connection connection = chain.connection();
            final Protocol protocol = connection != null ? connection.protocol() : Protocol.HTTP_1_1;
            final StringBuilder requestInformation = new StringBuilder("Request: ");
            requestInformation.append(
                    ObjectJoiner.join(" ", protocol.toString().toUpperCase(), request.method(), request.url()));
            long requestBodySize = -1;
            if (hasRequestBody) {
                requestBodySize = requestBody.contentLength();
                requestInformation.append(", body:");
                requestInformation.append(requestBodySize);
                requestInformation.append(" bytes");
            }
            log.info(requestInformation.toString());

            if (log.isDebugEnabled()) {
                // If the request has a body, sometimes these headers are not present, so let's make them explicit
                if (hasRequestBody) {
                    if (requestBody.contentType() != null) {
                        logHeader(HotelApiClient.CONTENT_TYPE_HEADER, requestBody.contentType().toString());
                    }
                    if (requestBodySize != -1) {
                        logHeader(HotelApiClient.CONTENT_LENGTH_HEADER, Long.toString(requestBodySize));
                    }
                }
                // Log the other headers
                for (String header : request.headers().names()) {
                    if (!HotelApiClient.CONTENT_TYPE_HEADER.equalsIgnoreCase(header)
                            && !HotelApiClient.CONTENT_LENGTH_HEADER.equalsIgnoreCase(header)) {
                        for (String value : request.headers().values(header)) {
                            logHeader(header, value);
                        }
                    }
                }
                if (log.isTraceEnabled() && hasRequestBody) {
                    Supplier<Buffer> requestBufferSupplier = Unchecked.supplier(() -> {
                        Buffer buffer = new Buffer();
                        requestBody.writeTo(buffer);
                        return buffer;
                    });
                    logBody(requestBufferSupplier, requestBody.contentType(), request.headers());
                }
            }
            final long requestStart = System.nanoTime();
            final Response response = chain.proceed(request);
            final long totalRequestTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - requestStart);

            final ResponseBody responseBody = response.body();
            final long contentLength = responseBody.contentLength();

            log.info("Response: {}", ObjectJoiner.join(" ", response.code(), response.message()));
            if (contentLength >= 0) {
                log.info("  {}: {}", HotelApiClient.CONTENT_LENGTH_HEADER, contentLength);
            }
            log.info("  Request took {} ms", totalRequestTime);

            if (log.isDebugEnabled()) {
                for (String header : response.headers().names()) {
                    for (String value : response.headers().values(header)) {
                        logHeader(header, value);
                    }
                }
                if (log.isTraceEnabled() && HttpEngine.hasBody(response)) {
                    MediaType contentType = responseBody.contentType();
                    Supplier<Buffer> responseBufferSupplier = Unchecked.supplier(() -> {
                        BufferedSource source = responseBody.source();
                        source.request(Long.MAX_VALUE);
                        return source.buffer().clone();
                    });
                    logBody(responseBufferSupplier, contentType, response.headers());
                }
            }
            return response;
        }
    }

    private void logBody(Supplier<Buffer> bufferSupplier, MediaType contentType, Headers headers) {
        if (bodyEncoded(headers)) {
            log.trace("  Body: encoded, not shown");
        } else {
            Buffer buffer = bufferSupplier.get();
            Charset charset = AssignUtils.UTF8;
            if (contentType != null) {
                try {
                    charset = contentType.charset(AssignUtils.UTF8);
                } catch (UnsupportedCharsetException e) {
                    log.error("  Body: Could not be decoded {}", e.getMessage());
                }
            }
            String body = buffer.readString(charset);
            String bodyContentType = headers.get(HotelApiClient.CONTENT_TYPE_HEADER);
            if (bodyContentType != null
                    && bodyContentType.toLowerCase().startsWith(HotelApiClient.APPLICATION_JSON_HEADER)) {
                log.trace("  JSON Body: {}", writeJSON(body));
            } else {
                log.trace("  Body: {}", body);
            }
        }
    }

    private void logHeader(final String headerName, final String headerValue) {
        log.debug("  Header: {}: \"{}\"", headerName, headerValue);
    }

    private boolean bodyEncoded(Headers headers) {
        String contentEncoding = headers.get(HotelApiClient.CONTENT_ENCODING_HEADER);
        return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity");
    }

    public static String writeJSON(final Object object) {
        ObjectMapper mapper = null;
        String result = null;
        mapper = new ObjectMapper();
        try {
            if (object instanceof String) {
                Object json = mapper.readValue((String) object, Object.class);
                result = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(json);
            } else {
                result = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
            }
        } catch (final IOException e) {
            log.warn("Body is not a json object {}", e.getMessage());
        }
        return result;
    }

}