Include stacktrace in rendered exceptions
This commit includes the stacktrace into the structured exception rendering to ensure we can find the reason / cause for certain things quicker. This is enabled by default and is very verbose. Users can disable it via `rest.exception.stacktrace.skip = true|false` Closes #12239
This commit is contained in:
parent
03e15056c7
commit
ca20282e84
|
@ -43,7 +43,10 @@ import java.util.*;
|
|||
*/
|
||||
public class ElasticsearchException extends RuntimeException implements ToXContent {
|
||||
|
||||
public static final String REST_EXCEPTION_SKIP_CAUSE = "rest.exception.skip_cause";
|
||||
public static final String REST_EXCEPTION_SKIP_CAUSE = "rest.exception.cause.skip";
|
||||
public static final String REST_EXCEPTION_SKIP_STACK_TRACE = "rest.exception.stacktrace.skip";
|
||||
private static final boolean REST_EXCEPTION_SKIP_STACK_TRACE_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 SHARD_HEADER_KEY = "es.shard";
|
||||
private static final String RESOURCE_HEADER_TYPE_KEY = "es.resource.type";
|
||||
|
@ -270,6 +273,9 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
|
|||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
@ -286,7 +292,7 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
|
|||
*/
|
||||
protected final void causeToXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
final Throwable cause = getCause();
|
||||
if (cause != null && params.paramAsBoolean(REST_EXCEPTION_SKIP_CAUSE, false) == false) {
|
||||
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);
|
||||
|
@ -342,6 +348,9 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
|
|||
toXContent(builder, params, ex.getCause());
|
||||
builder.endObject();
|
||||
}
|
||||
if (params.paramAsBoolean(REST_EXCEPTION_SKIP_STACK_TRACE, REST_EXCEPTION_SKIP_STACK_TRACE_DEFAULT) == false) {
|
||||
builder.field("stack_trace", ExceptionsHelper.stackTrace(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,10 +52,12 @@ import java.io.EOFException;
|
|||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class ESExceptionTests extends ESTestCase {
|
||||
private static final ToXContent.Params PARAMS = new ToXContent.MapParams(Collections.singletonMap(ElasticsearchException.REST_EXCEPTION_SKIP_STACK_TRACE, "true"));
|
||||
|
||||
@Test
|
||||
public void testStatus() {
|
||||
|
@ -144,7 +146,7 @@ public class ESExceptionTests extends ESTestCase {
|
|||
SearchPhaseExecutionException ex = new SearchPhaseExecutionException("search", "all shards failed", new ShardSearchFailure[]{failure, failure1});
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject();
|
||||
ex.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
ex.toXContent(builder, PARAMS);
|
||||
builder.endObject();
|
||||
String expected = "{\"type\":\"search_phase_execution_exception\",\"reason\":\"all shards failed\",\"phase\":\"search\",\"grouped\":true,\"failed_shards\":[{\"shard\":1,\"index\":\"foo\",\"node\":\"node_1\",\"reason\":{\"type\":\"test_query_parsing_exception\",\"reason\":\"foobar\",\"index\":\"foo\"}}]}";
|
||||
assertEquals(expected, builder.string());
|
||||
|
@ -159,7 +161,7 @@ public class ESExceptionTests extends ESTestCase {
|
|||
SearchPhaseExecutionException ex = new SearchPhaseExecutionException("search", "all shards failed", new ShardSearchFailure[]{failure, failure1, failure2});
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject();
|
||||
ex.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
ex.toXContent(builder, PARAMS);
|
||||
builder.endObject();
|
||||
String expected = "{\"type\":\"search_phase_execution_exception\",\"reason\":\"all shards failed\",\"phase\":\"search\",\"grouped\":true,\"failed_shards\":[{\"shard\":1,\"index\":\"foo\",\"node\":\"node_1\",\"reason\":{\"type\":\"test_query_parsing_exception\",\"reason\":\"foobar\",\"index\":\"foo\"}},{\"shard\":1,\"index\":\"foo1\",\"node\":\"node_1\",\"reason\":{\"type\":\"test_query_parsing_exception\",\"reason\":\"foobar\",\"index\":\"foo1\"}}]}";
|
||||
assertEquals(expected, builder.string());
|
||||
|
@ -184,7 +186,7 @@ public class ESExceptionTests extends ESTestCase {
|
|||
ElasticsearchException ex = new SearchParseException(new TestSearchContext(), "foo", new XContentLocation(1,0));
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject();
|
||||
ex.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
ex.toXContent(builder, PARAMS);
|
||||
builder.endObject();
|
||||
|
||||
String expected = "{\"type\":\"search_parse_exception\",\"reason\":\"foo\",\"line\":1,\"col\":0}";
|
||||
|
@ -194,7 +196,7 @@ public class ESExceptionTests extends ESTestCase {
|
|||
ElasticsearchException ex = new ElasticsearchException("foo", new ElasticsearchException("bar", new IllegalArgumentException("index is closed", new RuntimeException("foobar"))));
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject();
|
||||
ex.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
ex.toXContent(builder, PARAMS);
|
||||
builder.endObject();
|
||||
|
||||
String expected = "{\"type\":\"exception\",\"reason\":\"foo\",\"caused_by\":{\"type\":\"exception\",\"reason\":\"bar\",\"caused_by\":{\"type\":\"illegal_argument_exception\",\"reason\":\"index is closed\",\"caused_by\":{\"type\":\"runtime_exception\",\"reason\":\"foobar\"}}}}";
|
||||
|
@ -209,7 +211,7 @@ public class ESExceptionTests extends ESTestCase {
|
|||
}
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject();
|
||||
ElasticsearchException.toXContent(builder, ToXContent.EMPTY_PARAMS, ex);
|
||||
ElasticsearchException.toXContent(builder, PARAMS, ex);
|
||||
builder.endObject();
|
||||
|
||||
String expected = "{\"type\":\"file_not_found_exception\",\"reason\":\"foo not found\"}";
|
||||
|
@ -220,7 +222,7 @@ public class ESExceptionTests extends ESTestCase {
|
|||
QueryParsingException ex = new TestQueryParsingException(new Index("foo"), 1, 2, "foobar", null);
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject();
|
||||
ElasticsearchException.toXContent(builder, ToXContent.EMPTY_PARAMS, ex);
|
||||
ElasticsearchException.toXContent(builder, PARAMS, ex);
|
||||
builder.endObject();
|
||||
String expected = "{\"type\":\"test_query_parsing_exception\",\"reason\":\"foobar\",\"index\":\"foo\",\"line\":1,\"col\":2}";
|
||||
assertEquals(expected, builder.string());
|
||||
|
@ -230,13 +232,13 @@ public class ESExceptionTests extends ESTestCase {
|
|||
ElasticsearchException ex = new RemoteTransportException("foobar", new FileNotFoundException("foo not found"));
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject();
|
||||
ElasticsearchException.toXContent(builder, ToXContent.EMPTY_PARAMS, ex);
|
||||
ElasticsearchException.toXContent(builder, PARAMS, ex);
|
||||
builder.endObject();
|
||||
|
||||
XContentBuilder otherBuilder = XContentFactory.jsonBuilder();
|
||||
|
||||
otherBuilder.startObject();
|
||||
ex.toXContent(otherBuilder, ToXContent.EMPTY_PARAMS);
|
||||
ex.toXContent(otherBuilder, PARAMS);
|
||||
otherBuilder.endObject();
|
||||
assertEquals(otherBuilder.string(), builder.string());
|
||||
assertEquals("{\"type\":\"file_not_found_exception\",\"reason\":\"foo not found\"}", builder.string());
|
||||
|
@ -248,7 +250,7 @@ public class ESExceptionTests extends ESTestCase {
|
|||
ex.addHeader("test_multi", "some value", "another value");
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject();
|
||||
ElasticsearchException.toXContent(builder, ToXContent.EMPTY_PARAMS, ex);
|
||||
ElasticsearchException.toXContent(builder, PARAMS, ex);
|
||||
builder.endObject();
|
||||
assertThat(builder.string(), Matchers.anyOf( // iteration order depends on platform
|
||||
equalTo("{\"type\":\"test_query_parsing_exception\",\"reason\":\"foobar\",\"index\":\"foo\",\"line\":1,\"col\":2,\"header\":{\"test_multi\":[\"some value\",\"another value\"],\"test\":\"some value\"}}"),
|
||||
|
|
|
@ -78,6 +78,7 @@ import java.nio.file.FileVisitor;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -525,7 +526,7 @@ public class ExceptionSerializationTests extends ESTestCase {
|
|||
try {
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject();
|
||||
x.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
x.toXContent(builder, new ToXContent.MapParams(Collections.singletonMap(ElasticsearchException.REST_EXCEPTION_SKIP_STACK_TRACE, "true")));
|
||||
builder.endObject();
|
||||
return builder.string();
|
||||
} catch (IOException e) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.elasticsearch.action.search;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.support.IndicesOptions;
|
||||
import org.elasticsearch.test.StreamsUtils;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
|
@ -28,6 +29,7 @@ import org.elasticsearch.test.ESTestCase;
|
|||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
|
@ -123,7 +125,7 @@ public class MultiSearchRequestTests extends ESTestCase {
|
|||
public void testResponseErrorToXContent() throws IOException {
|
||||
MultiSearchResponse response = new MultiSearchResponse(new MultiSearchResponse.Item[]{new MultiSearchResponse.Item(null, new IllegalStateException("foobar")), new MultiSearchResponse.Item(null, new IllegalStateException("baaaaaazzzz"))});
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
response.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
response.toXContent(builder, new ToXContent.MapParams(Collections.singletonMap(ElasticsearchException.REST_EXCEPTION_SKIP_STACK_TRACE, "true")));
|
||||
assertEquals("\"responses\"[{\"error\":{\"root_cause\":[{\"type\":\"illegal_state_exception\",\"reason\":\"foobar\"}],\"type\":\"illegal_state_exception\",\"reason\":\"foobar\"}},{\"error\":{\"root_cause\":[{\"type\":\"illegal_state_exception\",\"reason\":\"baaaaaazzzz\"}],\"type\":\"illegal_state_exception\",\"reason\":\"baaaaaazzzz\"}}]",
|
||||
builder.string());
|
||||
}
|
||||
|
|
|
@ -176,6 +176,7 @@ public class BytesRestResponseTests extends ESTestCase {
|
|||
|
||||
DetailedExceptionRestChannel(RestRequest request) {
|
||||
super(request, true);
|
||||
request.params().put(ElasticsearchException.REST_EXCEPTION_SKIP_STACK_TRACE, "true");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in New Issue