Simplify ElasticsearchException rendering as a XContent (#22611)

This commit tries to simplify the way ElasticsearchException are rendered to xcontent. It adds some documentation and renames and merges some methods. Current behavior is preserved, the goal is to be more readable and centralize everything in the ElasticsearchException class.
This commit is contained in:
Tanguy Leroux 2017-01-17 15:44:49 +01:00 committed by GitHub
parent e0f8d88d5c
commit f5542ed47f
27 changed files with 170 additions and 163 deletions

View File

@ -22,6 +22,7 @@ package org.elasticsearch;
import org.elasticsearch.action.support.replication.ReplicationOperation;
import org.elasticsearch.cluster.action.shard.ShardStateAction;
import org.elasticsearch.common.CheckedFunction;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
@ -36,13 +37,15 @@ import org.elasticsearch.transport.TcpTransport;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static java.util.Collections.unmodifiableMap;
import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_UUID_NA_VALUE;
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
@ -56,19 +59,19 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
static final Version UNKNOWN_VERSION_ADDED = Version.fromId(0);
/**
* Passed in the {@link Params} of {@link #toXContent(XContentBuilder, org.elasticsearch.common.xcontent.ToXContent.Params, Throwable)}
* Passed in the {@link Params} of {@link #generateThrowableXContent(XContentBuilder, Params, Throwable)}
* to control if the {@code caused_by} element should render. Unlike most parameters to {@code toXContent} methods this parameter is
* internal only and not available as a URL parameter.
*/
public static final String REST_EXCEPTION_SKIP_CAUSE = "rest.exception.cause.skip";
/**
* Passed in the {@link Params} of {@link #toXContent(XContentBuilder, org.elasticsearch.common.xcontent.ToXContent.Params, Throwable)}
* Passed in the {@link Params} of {@link #generateThrowableXContent(XContentBuilder, Params, Throwable)}
* to control if the {@code stack_trace} element should render. Unlike most parameters to {@code toXContent} methods this parameter is
* internal only and not available as a URL parameter. Use the {@code error_trace} parameter instead.
*/
public static final String REST_EXCEPTION_SKIP_STACK_TRACE = "rest.exception.stacktrace.skip";
public static final boolean REST_EXCEPTION_SKIP_STACK_TRACE_DEFAULT = true;
public static final boolean REST_EXCEPTION_SKIP_CAUSE_DEFAULT = false;
private static final boolean REST_EXCEPTION_SKIP_CAUSE_DEFAULT = false;
private static final String INDEX_HEADER_KEY = "es.index";
private static final String INDEX_HEADER_KEY_UUID = "es.index_uuid";
private static final String SHARD_HEADER_KEY = "es.shard";
@ -160,6 +163,10 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
return headers.get(key);
}
protected Map<String, List<String>> getHeaders() {
return headers;
}
/**
* Returns the rest status code associated with this exception.
*/
@ -257,64 +264,56 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
Throwable ex = ExceptionsHelper.unwrapCause(this);
if (ex != this) {
toXContent(builder, params, this);
generateThrowableXContent(builder, params, this);
} else {
builder.field(TYPE, getExceptionName());
builder.field(REASON, getMessage());
for (String key : headers.keySet()) {
if (key.startsWith("es.")) {
List<String> values = headers.get(key);
xContentHeader(builder, key.substring("es.".length()), values);
}
}
innerToXContent(builder, params);
renderHeader(builder, params);
if (params.paramAsBoolean(REST_EXCEPTION_SKIP_STACK_TRACE, REST_EXCEPTION_SKIP_STACK_TRACE_DEFAULT) == false) {
builder.field(STACK_TRACE, ExceptionsHelper.stackTrace(this));
}
innerToXContent(builder, params, this, getExceptionName(), getMessage(), headers, getCause());
}
return builder;
}
/**
* Renders additional per exception information into the xcontent
*/
protected void innerToXContent(XContentBuilder builder, Params params) throws IOException {
causeToXContent(builder, params);
}
protected static void innerToXContent(XContentBuilder builder, Params params,
Throwable throwable, String type, String message, Map<String, List<String>> headers,
Throwable cause) throws IOException {
builder.field(TYPE, type);
builder.field(REASON, message);
/**
* Renders a cause exception as xcontent
*/
protected void causeToXContent(XContentBuilder builder, Params params) throws IOException {
final Throwable cause = getCause();
if (cause != null && params.paramAsBoolean(REST_EXCEPTION_SKIP_CAUSE, REST_EXCEPTION_SKIP_CAUSE_DEFAULT) == false) {
builder.field(CAUSED_BY);
builder.startObject();
toXContent(builder, params, cause);
builder.endObject();
}
}
protected final void renderHeader(XContentBuilder builder, Params params) throws IOException {
boolean hasHeader = false;
Set<String> customHeaders = new HashSet<>();
for (String key : headers.keySet()) {
if (key.startsWith("es.")) {
continue;
headerToXContent(builder, key.substring("es.".length()), headers.get(key));
} else {
customHeaders.add(key);
}
if (hasHeader == false) {
builder.startObject(HEADER);
hasHeader = true;
}
List<String> values = headers.get(key);
xContentHeader(builder, key, values);
}
if (hasHeader) {
if (throwable instanceof ElasticsearchException) {
ElasticsearchException exception = (ElasticsearchException) throwable;
exception.metadataToXContent(builder, params);
}
if (params.paramAsBoolean(REST_EXCEPTION_SKIP_CAUSE, REST_EXCEPTION_SKIP_CAUSE_DEFAULT) == false) {
if (cause != null) {
builder.field(CAUSED_BY);
builder.startObject();
generateThrowableXContent(builder, params, cause);
builder.endObject();
}
}
if (customHeaders.isEmpty() == false) {
builder.startObject(HEADER);
for (String header : customHeaders) {
headerToXContent(builder, header, headers.get(header));
}
builder.endObject();
}
if (params.paramAsBoolean(REST_EXCEPTION_SKIP_STACK_TRACE, REST_EXCEPTION_SKIP_STACK_TRACE_DEFAULT) == false) {
builder.field(STACK_TRACE, ExceptionsHelper.stackTrace(throwable));
}
}
private void xContentHeader(XContentBuilder builder, String key, List<String> values) throws IOException {
private static void headerToXContent(XContentBuilder builder, String key, List<String> values) throws IOException {
if (values != null && values.isEmpty() == false) {
if (values.size() == 1) {
builder.field(key, values.get(0));
@ -329,25 +328,73 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
}
/**
* Static toXContent helper method that also renders non {@link org.elasticsearch.ElasticsearchException} instances as XContent.
* Renders additional per exception information into the XContent
*/
public static void toXContent(XContentBuilder builder, Params params, Throwable ex) throws IOException {
ex = ExceptionsHelper.unwrapCause(ex);
if (ex instanceof ElasticsearchException) {
((ElasticsearchException) ex).toXContent(builder, params);
protected void metadataToXContent(XContentBuilder builder, Params params) throws IOException {
}
/**
* Static toXContent helper method that renders {@link org.elasticsearch.ElasticsearchException} or {@link Throwable} instances
* as XContent, delegating the rendering to {@link #toXContent(XContentBuilder, Params)}
* or {@link #innerToXContent(XContentBuilder, Params, Throwable, String, String, Map, Throwable)}.
*
* This method is usually used when the {@link Throwable} is rendered as a part of another XContent object.
*/
public static void generateThrowableXContent(XContentBuilder builder, Params params, Throwable t) throws IOException {
t = ExceptionsHelper.unwrapCause(t);
if (t instanceof ElasticsearchException) {
((ElasticsearchException) t).toXContent(builder, params);
} else {
builder.field(TYPE, getExceptionName(ex));
builder.field(REASON, ex.getMessage());
if (ex.getCause() != null) {
builder.field(CAUSED_BY);
innerToXContent(builder, params, t, getExceptionName(t), t.getMessage(), emptyMap(), t.getCause());
}
}
/**
* Render any exception as a xcontent, encapsulated within a field or object named "error". The level of details that are rendered
* depends on the value of the "detailed" parameter: when it's false only a simple message based on the type and message of the
* exception is rendered. When it's true all detail are provided including guesses root causes, cause and potentially stack
* trace.
*
* This method is usually used when the {@link Exception} is rendered as a full XContent object.
*/
public static void generateFailureXContent(XContentBuilder builder, Params params, @Nullable Exception e, boolean detailed)
throws IOException {
// No exception to render as an error
if (e == null) {
builder.field(ERROR, "unknown");
return;
}
// Render the exception with a simple message
if (detailed == false) {
String message = "No ElasticsearchException found";
Throwable t = e;
for (int counter = 0; counter < 10 && t != null; counter++) {
if (t instanceof ElasticsearchException) {
message = t.getClass().getSimpleName() + "[" + t.getMessage() + "]";
break;
}
t = t.getCause();
}
builder.field(ERROR, message);
return;
}
// Render the exception with all details
final ElasticsearchException[] rootCauses = ElasticsearchException.guessRootCauses(e);
builder.startObject(ERROR);
{
builder.startArray(ROOT_CAUSE);
for (ElasticsearchException rootCause : rootCauses) {
builder.startObject();
toXContent(builder, params, ex.getCause());
rootCause.toXContent(builder, new DelegatingMapParams(singletonMap(REST_EXCEPTION_SKIP_CAUSE, "true"), params));
builder.endObject();
}
if (params.paramAsBoolean(REST_EXCEPTION_SKIP_STACK_TRACE, REST_EXCEPTION_SKIP_STACK_TRACE_DEFAULT) == false) {
builder.field(STACK_TRACE, ExceptionsHelper.stackTrace(ex));
}
builder.endArray();
}
generateThrowableXContent(builder, params, e);
builder.endObject();
}
/**
@ -877,22 +924,6 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
return null;
}
public static void renderException(XContentBuilder builder, Params params, Exception e) throws IOException {
builder.startObject(ERROR);
final ElasticsearchException[] rootCauses = ElasticsearchException.guessRootCauses(e);
builder.field(ROOT_CAUSE);
builder.startArray();
for (ElasticsearchException rootCause : rootCauses) {
builder.startObject();
rootCause.toXContent(builder, new ToXContent.DelegatingMapParams(
Collections.singletonMap(ElasticsearchException.REST_EXCEPTION_SKIP_CAUSE, "true"), params));
builder.endObject();
}
builder.endArray();
ElasticsearchException.toXContent(builder, params, e);
builder.endObject();
}
// lower cases and adds underscores to transitions in a name
private static String toUnderscoreCase(String value) {
StringBuilder sb = new StringBuilder();

View File

@ -105,7 +105,7 @@ public final class TaskOperationFailure implements Writeable, ToXContent {
if (reason != null) {
builder.field("reason");
builder.startObject();
ElasticsearchException.toXContent(builder, params, reason);
ElasticsearchException.generateThrowableXContent(builder, params, reason);
builder.endObject();
}
return builder;

View File

@ -207,7 +207,7 @@ public class IndicesShardStoresResponse extends ActionResponse implements ToXCon
builder.field(Fields.ALLOCATED, allocationStatus.value());
if (storeException != null) {
builder.startObject(Fields.STORE_EXCEPTION);
ElasticsearchException.toXContent(builder, params, storeException);
ElasticsearchException.generateThrowableXContent(builder, params, storeException);
builder.endObject();
}
return builder;

View File

@ -63,7 +63,7 @@ public class BulkItemResponse implements Streamable, StatusToXContentObject {
builder.field(Fields._ID, failure.getId());
builder.field(Fields.STATUS, failure.getStatus().getStatus());
builder.startObject(Fields.ERROR);
ElasticsearchException.toXContent(builder, params, failure.getCause());
ElasticsearchException.generateThrowableXContent(builder, params, failure.getCause());
builder.endObject();
}
builder.endObject();
@ -173,7 +173,7 @@ public class BulkItemResponse implements Streamable, StatusToXContentObject {
builder.field(ID_FIELD, id);
}
builder.startObject(CAUSE_FIELD);
ElasticsearchException.toXContent(builder, params, cause);
ElasticsearchException.generateThrowableXContent(builder, params, cause);
builder.endObject();
builder.field(STATUS_FIELD, status.getStatus());
return builder;

View File

@ -137,7 +137,7 @@ public class MultiGetResponse extends ActionResponse implements Iterable<MultiGe
builder.field(Fields._INDEX, failure.getIndex());
builder.field(Fields._TYPE, failure.getType());
builder.field(Fields._ID, failure.getId());
ElasticsearchException.renderException(builder, params, failure.getFailure());
ElasticsearchException.generateFailureXContent(builder, params, failure.getFailure(), true);
builder.endObject();
} else {
GetResponse getResponse = response.getResponse();

View File

@ -84,7 +84,7 @@ public final class SimulateDocumentBaseResult implements SimulateDocumentResult
if (failure == null) {
ingestDocument.toXContent(builder, params);
} else {
ElasticsearchException.renderException(builder, params, failure);
ElasticsearchException.generateFailureXContent(builder, params, failure, true);
}
builder.endObject();
return builder;

View File

@ -19,7 +19,6 @@
package org.elasticsearch.action.ingest;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
@ -99,10 +98,10 @@ class SimulateProcessorResult implements Writeable, ToXContent {
if (failure != null && ingestDocument != null) {
builder.startObject("ignored_error");
ElasticsearchException.renderException(builder, params, failure);
ElasticsearchException.generateFailureXContent(builder, params, failure, true);
builder.endObject();
} else if (failure != null) {
ElasticsearchException.renderException(builder, params, failure);
ElasticsearchException.generateFailureXContent(builder, params, failure, true);
}
if (ingestDocument != null) {

View File

@ -156,7 +156,7 @@ public class MultiSearchResponse extends ActionResponse implements Iterable<Mult
for (Item item : items) {
builder.startObject();
if (item.isFailure()) {
ElasticsearchException.renderException(builder, params, item.getFailure());
ElasticsearchException.generateFailureXContent(builder, params, item.getFailure(), true);
builder.field(Fields.STATUS, ExceptionsHelper.status(item.getFailure()).getStatus());
} else {
item.getResponse().innerToXContent(builder, params);

View File

@ -103,6 +103,7 @@ public class SearchPhaseExecutionException extends ElasticsearchException {
return shardFailures;
}
@Override
public Throwable getCause() {
Throwable cause = super.getCause();
if (cause == null) {
@ -131,7 +132,7 @@ public class SearchPhaseExecutionException extends ElasticsearchException {
}
@Override
protected void innerToXContent(XContentBuilder builder, Params params) throws IOException {
protected void metadataToXContent(XContentBuilder builder, Params params) throws IOException {
builder.field("phase", phaseName);
final boolean group = params.paramAsBoolean("group_shard_failures", true); // we group by default
builder.field("grouped", group); // notify that it's grouped
@ -144,15 +145,20 @@ public class SearchPhaseExecutionException extends ElasticsearchException {
builder.endObject();
}
builder.endArray();
super.innerToXContent(builder, params);
}
@Override
protected void causeToXContent(XContentBuilder builder, Params params) throws IOException {
if (super.getCause() != null) {
// if the cause is null we inject a guessed root cause that will then be rendered twice so wi disable it manually
super.causeToXContent(builder, params);
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
Throwable ex = ExceptionsHelper.unwrapCause(this);
if (ex != this) {
generateThrowableXContent(builder, params, this);
} else {
// We don't have a cause when all shards failed, but we do have shards failures so we can "guess" a cause
// (see {@link #getCause()}). Here, we use super.getCause() because we don't want the guessed exception to
// be rendered twice (one in the "cause" field, one in "failed_shards")
innerToXContent(builder, params, this, getExceptionName(), getMessage(), getHeaders(), super.getCause());
}
return builder;
}
@Override

View File

@ -161,7 +161,7 @@ public class ShardSearchFailure implements ShardOperationFailedException {
if (cause != null) {
builder.field("reason");
builder.startObject();
ElasticsearchException.toXContent(builder, params, cause);
ElasticsearchException.generateThrowableXContent(builder, params, cause);
builder.endObject();
}
return builder;

View File

@ -125,7 +125,7 @@ public class DefaultShardOperationFailedException implements ShardOperationFaile
if (reason != null) {
builder.field("reason");
builder.startObject();
ElasticsearchException.toXContent(builder, params, reason);
ElasticsearchException.generateThrowableXContent(builder, params, reason);
builder.endObject();
}
return builder;

View File

@ -28,7 +28,6 @@ import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Streamable;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
@ -375,7 +374,7 @@ public class ReplicationResponse extends ActionResponse {
builder.field(_NODE, nodeId);
builder.field(REASON);
builder.startObject();
ElasticsearchException.toXContent(builder, params, cause);
ElasticsearchException.generateThrowableXContent(builder, params, cause);
builder.endObject();
builder.field(STATUS, status);
builder.field(PRIMARY, primary);

View File

@ -133,7 +133,7 @@ public class MultiTermVectorsResponse extends ActionResponse implements Iterable
builder.field(Fields._INDEX, failure.getIndex());
builder.field(Fields._TYPE, failure.getType());
builder.field(Fields._ID, failure.getId());
ElasticsearchException.renderException(builder, params, failure.getCause());
ElasticsearchException.generateFailureXContent(builder, params, failure.getCause(), true);
builder.endObject();
} else {
TermVectorsResponse getResponse = response.getResponse();

View File

@ -262,7 +262,7 @@ public class NodeAllocationResult implements ToXContent, Writeable, Comparable<N
}
if (storeException != null) {
builder.startObject("store_exception");
ElasticsearchException.toXContent(builder, params, storeException);
ElasticsearchException.generateThrowableXContent(builder, params, storeException);
builder.endObject();
}
}

View File

@ -95,12 +95,11 @@ public class ParsingException extends ElasticsearchException {
}
@Override
protected void innerToXContent(XContentBuilder builder, Params params) throws IOException {
protected void metadataToXContent(XContentBuilder builder, Params params) throws IOException {
if (lineNumber != UNKNOWN_POSITION) {
builder.field("line", lineNumber);
builder.field("col", columnNumber);
}
super.innerToXContent(builder, params);
}
@Override

View File

@ -73,9 +73,8 @@ public class CircuitBreakingException extends ElasticsearchException {
}
@Override
protected void innerToXContent(XContentBuilder builder, Params params) throws IOException {
protected void metadataToXContent(XContentBuilder builder, Params params) throws IOException {
builder.field("bytes_wanted", bytesWanted);
builder.field("bytes_limit", byteLimit);
super.innerToXContent(builder, params);
}
}

View File

@ -22,7 +22,6 @@ package org.elasticsearch.index.query;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.Index;
import org.elasticsearch.rest.RestStatus;
@ -60,11 +59,6 @@ public class QueryShardException extends ElasticsearchException {
return RestStatus.BAD_REQUEST;
}
@Override
protected void innerToXContent(XContentBuilder builder, Params params) throws IOException {
super.innerToXContent(builder, params);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);

View File

@ -31,7 +31,10 @@ import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException;
import java.util.Collections;
import static java.util.Collections.singletonMap;
import static org.elasticsearch.ElasticsearchException.REST_EXCEPTION_SKIP_STACK_TRACE;
import static org.elasticsearch.ElasticsearchException.REST_EXCEPTION_SKIP_STACK_TRACE_DEFAULT;
public class BytesRestResponse extends RestResponse {
@ -89,7 +92,7 @@ public class BytesRestResponse extends RestResponse {
this.content = BytesArray.EMPTY;
this.contentType = TEXT_CONTENT_TYPE;
} else {
try (final XContentBuilder builder = convert(channel, status, e)) {
try (final XContentBuilder builder = build(channel, status, e)) {
this.content = builder.bytes();
this.contentType = builder.contentType().mediaType();
}
@ -116,49 +119,25 @@ public class BytesRestResponse extends RestResponse {
private static final Logger SUPPRESSED_ERROR_LOGGER = ESLoggerFactory.getLogger("rest.suppressed");
private static XContentBuilder convert(RestChannel channel, RestStatus status, Exception e) throws IOException {
XContentBuilder builder = channel.newErrorBuilder().startObject();
if (e == null) {
builder.field("error", "unknown");
} else if (channel.detailedErrorsEnabled()) {
final ToXContent.Params params;
if (channel.request().paramAsBoolean("error_trace", !ElasticsearchException.REST_EXCEPTION_SKIP_STACK_TRACE_DEFAULT)) {
params = new ToXContent.DelegatingMapParams(
Collections.singletonMap(ElasticsearchException.REST_EXCEPTION_SKIP_STACK_TRACE, "false"), channel.request());
private static XContentBuilder build(RestChannel channel, RestStatus status, Exception e) throws IOException {
ToXContent.Params params = channel.request();
if (params.paramAsBoolean("error_trace", !REST_EXCEPTION_SKIP_STACK_TRACE_DEFAULT)) {
params = new ToXContent.DelegatingMapParams(singletonMap(REST_EXCEPTION_SKIP_STACK_TRACE, "false"), params);
} else if (e != null) {
Supplier<?> messageSupplier = () -> new ParameterizedMessage("path: {}, params: {}",
channel.request().rawPath(), channel.request().params());
if (status.getStatus() < 500) {
SUPPRESSED_ERROR_LOGGER.debug(messageSupplier, e);
} else {
if (status.getStatus() < 500) {
SUPPRESSED_ERROR_LOGGER.debug(
(Supplier<?>) () -> new ParameterizedMessage("path: {}, params: {}",
channel.request().rawPath(), channel.request().params()), e);
} else {
SUPPRESSED_ERROR_LOGGER.warn(
(Supplier<?>) () -> new ParameterizedMessage("path: {}, params: {}",
channel.request().rawPath(), channel.request().params()), e);
}
params = channel.request();
SUPPRESSED_ERROR_LOGGER.warn(messageSupplier, e);
}
ElasticsearchException.renderException(builder, params, e);
} else {
builder.field("error", simpleMessage(e));
}
XContentBuilder builder = channel.newErrorBuilder().startObject();
ElasticsearchException.generateFailureXContent(builder, params, e, channel.detailedErrorsEnabled());
builder.field("status", status.getStatus());
builder.endObject();
return builder;
}
/*
* Builds a simple error string from the message of the first ElasticsearchException
*/
private static String simpleMessage(Throwable t) throws IOException {
int counter = 0;
Throwable next = t;
while (next != null && counter++ < 10) {
if (t instanceof ElasticsearchException) {
return next.getClass().getSimpleName() + "[" + next.getMessage() + "]";
}
next = next.getCause();
}
return "No ElasticsearchException found";
}
}

View File

@ -87,8 +87,7 @@ public class ScriptException extends ElasticsearchException {
}
@Override
protected void innerToXContent(XContentBuilder builder, Params params) throws IOException {
super.innerToXContent(builder, params);
protected void metadataToXContent(XContentBuilder builder, Params params) throws IOException {
builder.field("script_stack", scriptStack);
builder.field("script", script);
builder.field("lang", lang);

View File

@ -607,7 +607,7 @@ public class ScriptService extends AbstractComponent implements Closeable, Clust
try (XContentBuilder builder = JsonXContent.contentBuilder()) {
builder.prettyPrint();
builder.startObject();
ElasticsearchException.toXContent(builder, ToXContent.EMPTY_PARAMS, e);
ElasticsearchException.generateThrowableXContent(builder, ToXContent.EMPTY_PARAMS, e);
builder.endObject();
logger.warn("failed to load/compile script [{}]: {}", scriptNameExt.v1(), builder.string());
} catch (IOException ioe) {

View File

@ -72,12 +72,11 @@ public class SearchParseException extends SearchContextException {
}
@Override
protected void innerToXContent(XContentBuilder builder, Params params) throws IOException {
protected void metadataToXContent(XContentBuilder builder, Params params) throws IOException {
if (lineNumber != UNKNOWN_POSITION) {
builder.field("line", lineNumber);
builder.field("col", columnNumber);
}
super.innerToXContent(builder, params);
}
/**

View File

@ -230,7 +230,7 @@ public final class TaskResult implements Writeable, ToXContent {
private static BytesReference toXContent(Exception error) throws IOException {
try (XContentBuilder builder = XContentFactory.contentBuilder(Requests.INDEX_CONTENT_TYPE)) {
builder.startObject();
ElasticsearchException.toXContent(builder, ToXContent.EMPTY_PARAMS, error);
ElasticsearchException.generateThrowableXContent(builder, ToXContent.EMPTY_PARAMS, error);
builder.endObject();
return builder.bytes();
}

View File

@ -253,7 +253,7 @@ public class ESExceptionTests extends ESTestCase {
}
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
ElasticsearchException.toXContent(builder, PARAMS, ex);
ElasticsearchException.generateThrowableXContent(builder, PARAMS, ex);
builder.endObject();
String expected = "{\"type\":\"file_not_found_exception\",\"reason\":\"foo not found\"}";
@ -264,7 +264,7 @@ public class ESExceptionTests extends ESTestCase {
ParsingException ex = new ParsingException(1, 2, "foobar", null);
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
ElasticsearchException.toXContent(builder, PARAMS, ex);
ElasticsearchException.generateThrowableXContent(builder, PARAMS, ex);
builder.endObject();
String expected = "{\"type\":\"parsing_exception\",\"reason\":\"foobar\",\"line\":1,\"col\":2}";
assertEquals(expected, builder.string());
@ -274,7 +274,7 @@ public class ESExceptionTests extends ESTestCase {
ElasticsearchException ex = new RemoteTransportException("foobar", new FileNotFoundException("foo not found"));
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
ElasticsearchException.toXContent(builder, PARAMS, ex);
ElasticsearchException.generateThrowableXContent(builder, PARAMS, ex);
builder.endObject();
XContentBuilder otherBuilder = XContentFactory.jsonBuilder();
@ -292,7 +292,7 @@ public class ESExceptionTests extends ESTestCase {
ex.addHeader("test_multi", "some value", "another value");
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
ElasticsearchException.toXContent(builder, PARAMS, ex);
ElasticsearchException.generateThrowableXContent(builder, PARAMS, ex);
builder.endObject();
assertThat(builder.string(), Matchers.anyOf( // iteration order depends on platform
equalTo("{\"type\":\"parsing_exception\",\"reason\":\"foobar\",\"line\":1,\"col\":2,\"header\":{\"test_multi\":[\"some value\",\"another value\"],\"test\":\"some value\"}}"),

View File

@ -19,6 +19,7 @@
package org.elasticsearch;
import org.apache.lucene.util.Constants;
import org.elasticsearch.action.RoutingMissingException;
import org.elasticsearch.action.support.broadcast.BroadcastShardOperationFailedException;
import org.elasticsearch.cluster.block.ClusterBlockException;
@ -67,7 +68,9 @@ public class ElasticsearchExceptionTests extends ESTestCase {
// in the JSON. Since the stack can be large, it only checks the beginning of the JSON.
assertExceptionAsJson(e, true, startsWith("{\"type\":\"exception\",\"reason\":\"foo\"," +
"\"caused_by\":{\"type\":\"illegal_state_exception\",\"reason\":\"bar\"," +
"\"stack_trace\":\"java.lang.IllegalStateException: bar"));
"\"stack_trace\":\"java.lang.IllegalStateException: bar" +
(Constants.WINDOWS ? "\\r\\n" : "\\n") +
"\\tat org.elasticsearch."));
}
public void testToXContentWithHeaders() throws IOException {

View File

@ -152,7 +152,7 @@ public class MultiSearchTemplateResponse extends ActionResponse implements Itera
for (Item item : items) {
if (item.isFailure()) {
builder.startObject();
ElasticsearchException.renderException(builder, params, item.getFailure());
ElasticsearchException.generateFailureXContent(builder, params, item.getFailure(), true);
builder.endObject();
} else {
item.getResponse().toXContent(builder, params);

View File

@ -486,7 +486,7 @@ public abstract class BulkByScrollTask extends CancellableTask {
status.toXContent(builder, params);
} else {
builder.startObject();
ElasticsearchException.toXContent(builder, params, exception);
ElasticsearchException.generateThrowableXContent(builder, params, exception);
builder.endObject();
}
return builder;

View File

@ -348,7 +348,7 @@ public abstract class ScrollableHitSource implements Closeable {
builder.field("reason");
{
builder.startObject();
ElasticsearchException.toXContent(builder, params, reason);
ElasticsearchException.generateThrowableXContent(builder, params, reason);
builder.endObject();
}
builder.endObject();