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:
parent
e0f8d88d5c
commit
f5542ed47f
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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\"}}"),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue