add curl format trace logging for requests and responses
This commit is contained in:
parent
e7fe397c39
commit
17a21f0272
|
@ -20,31 +20,111 @@
|
||||||
package org.elasticsearch.client;
|
package org.elasticsearch.client;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.apache.http.Header;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.HttpEntityEnclosingRequest;
|
||||||
import org.apache.http.HttpHost;
|
import org.apache.http.HttpHost;
|
||||||
import org.apache.http.RequestLine;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.StatusLine;
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
|
import org.apache.http.entity.BufferedHttpEntity;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class that exposes static methods to unify the way requests are logged
|
* Helper class that exposes static methods to unify the way requests are logged.
|
||||||
|
* Includes trace logging to log complete requests and responses in curl format.
|
||||||
*/
|
*/
|
||||||
public final class RequestLogger {
|
public final class RequestLogger {
|
||||||
|
|
||||||
|
private static final Log tracer = LogFactory.getLog("tracer");
|
||||||
|
|
||||||
private RequestLogger() {
|
private RequestLogger() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs a request that yielded a response
|
* Logs a request that yielded a response
|
||||||
*/
|
*/
|
||||||
public static void log(Log logger, String message, RequestLine requestLine, HttpHost host, StatusLine statusLine) {
|
public static void log(Log logger, String message, HttpUriRequest request, HttpHost host, HttpResponse httpResponse) {
|
||||||
logger.debug(message + " [" + requestLine.getMethod() + " " + host + requestLine.getUri() + "] [" + statusLine + "]");
|
logger.debug(message + " [" + request.getMethod() + " " + host + request.getRequestLine().getUri() +
|
||||||
|
"] [" + httpResponse.getStatusLine() + "]");
|
||||||
|
|
||||||
|
if (tracer.isTraceEnabled()) {
|
||||||
|
String requestLine;
|
||||||
|
try {
|
||||||
|
requestLine = buildTraceRequest(request, host);
|
||||||
|
} catch(IOException e) {
|
||||||
|
requestLine = "";
|
||||||
|
tracer.trace("error while reading request for trace purposes", e);
|
||||||
|
}
|
||||||
|
String responseLine;
|
||||||
|
try {
|
||||||
|
responseLine = buildTraceResponse(httpResponse);
|
||||||
|
} catch(IOException e) {
|
||||||
|
responseLine = "";
|
||||||
|
tracer.trace("error while reading response for trace purposes", e);
|
||||||
|
}
|
||||||
|
tracer.trace(requestLine + '\n' + responseLine);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs a request that failed
|
* Logs a request that failed
|
||||||
*/
|
*/
|
||||||
public static void log(Log logger, String message, RequestLine requestLine, HttpHost host, IOException e) {
|
public static void log(Log logger, String message, HttpUriRequest request, HttpHost host, IOException e) {
|
||||||
logger.debug(message + " [" + requestLine.getMethod() + " " + host + requestLine.getUri() + "]", e);
|
logger.debug(message + " [" + request.getMethod() + " " + host + request.getRequestLine().getUri() + "]", e);
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
String traceRequest;
|
||||||
|
try {
|
||||||
|
traceRequest = buildTraceRequest(request, host);
|
||||||
|
} catch (IOException e1) {
|
||||||
|
tracer.trace("error while reading request for trace purposes", e);
|
||||||
|
traceRequest = "";
|
||||||
|
}
|
||||||
|
tracer.trace(traceRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates curl output for given request
|
||||||
|
*/
|
||||||
|
static String buildTraceRequest(HttpUriRequest request, HttpHost host) throws IOException {
|
||||||
|
String requestLine = "curl -iX " + request.getMethod() + " '" + host + request.getRequestLine().getUri() + "'";
|
||||||
|
if (request instanceof HttpEntityEnclosingRequest) {
|
||||||
|
HttpEntityEnclosingRequest enclosingRequest = (HttpEntityEnclosingRequest) request;
|
||||||
|
if (enclosingRequest.getEntity() != null) {
|
||||||
|
requestLine += " -d '";
|
||||||
|
HttpEntity entity = new BufferedHttpEntity(enclosingRequest.getEntity());
|
||||||
|
enclosingRequest.setEntity(entity);
|
||||||
|
requestLine += EntityUtils.toString(entity) + "'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return requestLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates curl output for given response
|
||||||
|
*/
|
||||||
|
static String buildTraceResponse(HttpResponse httpResponse) throws IOException {
|
||||||
|
String responseLine = "# " + httpResponse.getStatusLine().toString();
|
||||||
|
for (Header header : httpResponse.getAllHeaders()) {
|
||||||
|
responseLine += "\n# " + header.getName() + ": " + header.getValue();
|
||||||
|
}
|
||||||
|
responseLine += "\n#";
|
||||||
|
HttpEntity entity = httpResponse.getEntity();
|
||||||
|
if (entity != null) {
|
||||||
|
entity = new BufferedHttpEntity(entity);
|
||||||
|
httpResponse.setEntity(entity);
|
||||||
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent()))) {
|
||||||
|
String line;
|
||||||
|
while( (line = reader.readLine()) != null) {
|
||||||
|
responseLine += "\n# " + line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return responseLine;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ public final class RestClient implements Closeable {
|
||||||
try {
|
try {
|
||||||
response = client.execute(connection.getHost(), request);
|
response = client.execute(connection.getHost(), request);
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
RequestLogger.log(logger, "request failed", request.getRequestLine(), connection.getHost(), e);
|
RequestLogger.log(logger, "request failed", request, connection.getHost(), e);
|
||||||
connectionPool.onFailure(connection);
|
connectionPool.onFailure(connection);
|
||||||
lastSeenException = addSuppressedException(lastSeenException, e);
|
lastSeenException = addSuppressedException(lastSeenException, e);
|
||||||
continue;
|
continue;
|
||||||
|
@ -108,11 +108,11 @@ public final class RestClient implements Closeable {
|
||||||
int statusCode = response.getStatusLine().getStatusCode();
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
//TODO make ignore status code configurable. rest-spec and tests support that parameter (ignore_missing)
|
//TODO make ignore status code configurable. rest-spec and tests support that parameter (ignore_missing)
|
||||||
if (statusCode < 300 || (request.getMethod().equals(HttpHead.METHOD_NAME) && statusCode == 404) ) {
|
if (statusCode < 300 || (request.getMethod().equals(HttpHead.METHOD_NAME) && statusCode == 404) ) {
|
||||||
RequestLogger.log(logger, "request succeeded", request.getRequestLine(), connection.getHost(), response.getStatusLine());
|
RequestLogger.log(logger, "request succeeded", request, connection.getHost(), response);
|
||||||
connectionPool.onSuccess(connection);
|
connectionPool.onSuccess(connection);
|
||||||
return new ElasticsearchResponse(request.getRequestLine(), connection.getHost(), response);
|
return new ElasticsearchResponse(request.getRequestLine(), connection.getHost(), response);
|
||||||
} else {
|
} else {
|
||||||
RequestLogger.log(logger, "request failed", request.getRequestLine(), connection.getHost(), response.getStatusLine());
|
RequestLogger.log(logger, "request failed", request, connection.getHost(), response);
|
||||||
String responseBody = null;
|
String responseBody = null;
|
||||||
if (response.getEntity() != null) {
|
if (response.getEntity() != null) {
|
||||||
responseBody = EntityUtils.toString(response.getEntity());
|
responseBody = EntityUtils.toString(response.getEntity());
|
||||||
|
|
|
@ -77,7 +77,7 @@ final class Sniffer {
|
||||||
try (CloseableHttpResponse response = client.execute(host, httpGet)) {
|
try (CloseableHttpResponse response = client.execute(host, httpGet)) {
|
||||||
StatusLine statusLine = response.getStatusLine();
|
StatusLine statusLine = response.getStatusLine();
|
||||||
if (statusLine.getStatusCode() >= 300) {
|
if (statusLine.getStatusCode() >= 300) {
|
||||||
RequestLogger.log(logger, "sniff failed", httpGet.getRequestLine(), host, statusLine);
|
RequestLogger.log(logger, "sniff failed", httpGet, host, response);
|
||||||
String responseBody = null;
|
String responseBody = null;
|
||||||
if (response.getEntity() != null) {
|
if (response.getEntity() != null) {
|
||||||
responseBody = EntityUtils.toString(response.getEntity());
|
responseBody = EntityUtils.toString(response.getEntity());
|
||||||
|
@ -85,11 +85,11 @@ final class Sniffer {
|
||||||
throw new ElasticsearchResponseException(httpGet.getRequestLine(), host, response.getStatusLine(), responseBody);
|
throw new ElasticsearchResponseException(httpGet.getRequestLine(), host, response.getStatusLine(), responseBody);
|
||||||
} else {
|
} else {
|
||||||
List<HttpHost> nodes = readHosts(response.getEntity());
|
List<HttpHost> nodes = readHosts(response.getEntity());
|
||||||
RequestLogger.log(logger, "sniff succeeded", httpGet.getRequestLine(), host, statusLine);
|
RequestLogger.log(logger, "sniff succeeded", httpGet, host, response);
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
RequestLogger.log(logger, "sniff failed", httpGet.getRequestLine(), host, e);
|
RequestLogger.log(logger, "sniff failed", httpGet, host, e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.client;
|
||||||
|
|
||||||
|
import com.carrotsearch.randomizedtesting.generators.RandomInts;
|
||||||
|
import org.apache.commons.codec.Charsets;
|
||||||
|
import org.apache.http.HttpEntityEnclosingRequest;
|
||||||
|
import org.apache.http.HttpHost;
|
||||||
|
import org.apache.http.ProtocolVersion;
|
||||||
|
import org.apache.http.client.methods.HttpHead;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.client.methods.HttpPut;
|
||||||
|
import org.apache.http.client.methods.HttpRequestBase;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.message.BasicHttpResponse;
|
||||||
|
import org.apache.http.message.BasicStatusLine;
|
||||||
|
import org.apache.lucene.util.LuceneTestCase;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
|
||||||
|
public class RequestLoggerTests extends LuceneTestCase {
|
||||||
|
|
||||||
|
public void testTraceRequest() throws IOException, URISyntaxException {
|
||||||
|
HttpHost host = new HttpHost("localhost", 9200, random().nextBoolean() ? "http" : "https");
|
||||||
|
URI uri = new URI("/index/type/_api");
|
||||||
|
|
||||||
|
HttpRequestBase request;
|
||||||
|
int requestType = RandomInts.randomIntBetween(random(), 0, 4);
|
||||||
|
switch(requestType) {
|
||||||
|
case 0:
|
||||||
|
request = new HttpGetWithEntity(uri);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
request = new HttpPost(uri);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
request = new HttpPut(uri);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
request = new HttpDeleteWithEntity(uri);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
request = new HttpHead(uri);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
String expected = "curl -iX " + request.getMethod() + " '" + host + uri + "'";
|
||||||
|
|
||||||
|
if (request instanceof HttpEntityEnclosingRequest && random().nextBoolean()) {
|
||||||
|
HttpEntityEnclosingRequest enclosingRequest = (HttpEntityEnclosingRequest) request;
|
||||||
|
String requestBody = "{ \"field\": \"value\" }";
|
||||||
|
enclosingRequest.setEntity(new StringEntity(requestBody, Charsets.UTF_8));
|
||||||
|
expected += " -d '" + requestBody + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
String traceRequest = RequestLogger.buildTraceRequest(request, host);
|
||||||
|
assertThat(traceRequest, equalTo(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testTraceResponse() throws IOException {
|
||||||
|
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
|
||||||
|
int statusCode = RandomInts.randomIntBetween(random(), 200, 599);
|
||||||
|
String reasonPhrase = "REASON";
|
||||||
|
BasicStatusLine statusLine = new BasicStatusLine(protocolVersion, statusCode, reasonPhrase);
|
||||||
|
String expected = "# " + statusLine.toString();
|
||||||
|
BasicHttpResponse httpResponse = new BasicHttpResponse(statusLine);
|
||||||
|
int numHeaders = RandomInts.randomIntBetween(random(), 0, 3);
|
||||||
|
for (int i = 0; i < numHeaders; i++) {
|
||||||
|
httpResponse.setHeader("header" + i, "value");
|
||||||
|
expected += "\n# header" + i + ": value";
|
||||||
|
}
|
||||||
|
expected += "\n#";
|
||||||
|
if (random().nextBoolean()) {
|
||||||
|
String responseBody = "{\n \"field\": \"value\"\n}";
|
||||||
|
httpResponse.setEntity(new StringEntity(responseBody, Charsets.UTF_8));
|
||||||
|
expected += "\n# {";
|
||||||
|
expected += "\n# \"field\": \"value\"";
|
||||||
|
expected += "\n# }";
|
||||||
|
}
|
||||||
|
|
||||||
|
String traceResponse = RequestLogger.buildTraceResponse(httpResponse);
|
||||||
|
assertThat(traceResponse, equalTo(expected));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue