From ca20282e84cf8920377c83160bdff4c828620d5d Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Tue, 14 Jul 2015 22:36:26 +0200 Subject: [PATCH] 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 --- .../elasticsearch/ElasticsearchException.java | 13 ++++++++++-- .../org/elasticsearch/ESExceptionTests.java | 20 ++++++++++--------- .../ExceptionSerializationTests.java | 3 ++- .../search/MultiSearchRequestTests.java | 4 +++- .../rest/BytesRestResponseTests.java | 1 + 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/ElasticsearchException.java b/core/src/main/java/org/elasticsearch/ElasticsearchException.java index 83be9f43ee6..ecc1bdbf99f 100644 --- a/core/src/main/java/org/elasticsearch/ElasticsearchException.java +++ b/core/src/main/java/org/elasticsearch/ElasticsearchException.java @@ -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)); + } } } diff --git a/core/src/test/java/org/elasticsearch/ESExceptionTests.java b/core/src/test/java/org/elasticsearch/ESExceptionTests.java index a4a6d28847a..6be1b7ccae2 100644 --- a/core/src/test/java/org/elasticsearch/ESExceptionTests.java +++ b/core/src/test/java/org/elasticsearch/ESExceptionTests.java @@ -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\"}}"), diff --git a/core/src/test/java/org/elasticsearch/ExceptionSerializationTests.java b/core/src/test/java/org/elasticsearch/ExceptionSerializationTests.java index 69d83b7bde7..b76dac80075 100644 --- a/core/src/test/java/org/elasticsearch/ExceptionSerializationTests.java +++ b/core/src/test/java/org/elasticsearch/ExceptionSerializationTests.java @@ -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) { diff --git a/core/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java b/core/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java index 97aee6ea3d6..cad56790631 100644 --- a/core/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java @@ -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()); } diff --git a/core/src/test/java/org/elasticsearch/rest/BytesRestResponseTests.java b/core/src/test/java/org/elasticsearch/rest/BytesRestResponseTests.java index 683229c87e8..20a20581c6d 100644 --- a/core/src/test/java/org/elasticsearch/rest/BytesRestResponseTests.java +++ b/core/src/test/java/org/elasticsearch/rest/BytesRestResponseTests.java @@ -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