Core: Pick inner most parse exception as root cause (#30270)
Just like `ElasticsearchException`, the inner most `XContentParseException` tends to contain the root cause of the exception and show be show to the user in the `root_cause` field. The effectively undoes most of the changes that #29373 made to the `root_cause` for parsing exceptions. The `type` field still changes from `parse_exception` to `x_content_parse_exception`, but this seems like a fairly safe change. `ElasticsearchWrapperException` *looks* tempting to implement this but the behavior isn't quite right. `ElasticsearchWrapperExceptions` are entirely unwrapped until the cause no longer `implements ElasticsearchWrapperException` but `XContentParseException` should be unwrapped until its cause is no longer an `XContentParseException` but no further. In other words, `ElasticsearchWrapperException` are unwrapped one step too far. Closes #30261
This commit is contained in:
parent
b9e1860f36
commit
99b98fab18
|
@ -31,6 +31,7 @@ import org.elasticsearch.common.io.stream.Writeable;
|
||||||
import org.elasticsearch.common.logging.LoggerMessageFormat;
|
import org.elasticsearch.common.logging.LoggerMessageFormat;
|
||||||
import org.elasticsearch.common.xcontent.ToXContentFragment;
|
import org.elasticsearch.common.xcontent.ToXContentFragment;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentParseException;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.index.Index;
|
import org.elasticsearch.index.Index;
|
||||||
import org.elasticsearch.index.shard.ShardId;
|
import org.elasticsearch.index.shard.ShardId;
|
||||||
|
@ -635,8 +636,25 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
|
||||||
public static ElasticsearchException[] guessRootCauses(Throwable t) {
|
public static ElasticsearchException[] guessRootCauses(Throwable t) {
|
||||||
Throwable ex = ExceptionsHelper.unwrapCause(t);
|
Throwable ex = ExceptionsHelper.unwrapCause(t);
|
||||||
if (ex instanceof ElasticsearchException) {
|
if (ex instanceof ElasticsearchException) {
|
||||||
|
// ElasticsearchException knows how to guess its own root cause
|
||||||
return ((ElasticsearchException) ex).guessRootCauses();
|
return ((ElasticsearchException) ex).guessRootCauses();
|
||||||
}
|
}
|
||||||
|
if (ex instanceof XContentParseException) {
|
||||||
|
/*
|
||||||
|
* We'd like to unwrap parsing exceptions to the inner-most
|
||||||
|
* parsing exception because that is generally the most interesting
|
||||||
|
* exception to return to the user. If that exception is caused by
|
||||||
|
* an ElasticsearchException we'd like to keep unwrapping because
|
||||||
|
* ElasticserachExceptions tend to contain useful information for
|
||||||
|
* the user.
|
||||||
|
*/
|
||||||
|
Throwable cause = ex.getCause();
|
||||||
|
if (cause != null) {
|
||||||
|
if (cause instanceof XContentParseException || cause instanceof ElasticsearchException) {
|
||||||
|
return guessRootCauses(ex.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return new ElasticsearchException[]{new ElasticsearchException(t.getMessage(), t) {
|
return new ElasticsearchException[]{new ElasticsearchException(t.getMessage(), t) {
|
||||||
@Override
|
@Override
|
||||||
protected String getExceptionName() {
|
protected String getExceptionName() {
|
||||||
|
|
|
@ -19,7 +19,11 @@
|
||||||
|
|
||||||
package org.elasticsearch;
|
package org.elasticsearch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception that is meant to be "unwrapped" when sent back to the user
|
||||||
|
* as an error because its is {@link #getCause() cause}, if non-null is
|
||||||
|
* <strong>always</strong> more useful to the user than the exception itself.
|
||||||
|
*/
|
||||||
public interface ElasticsearchWrapperException {
|
public interface ElasticsearchWrapperException {
|
||||||
|
|
||||||
Throwable getCause();
|
Throwable getCause();
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||||
import org.elasticsearch.common.xcontent.XContentLocation;
|
import org.elasticsearch.common.xcontent.XContentLocation;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentParseException;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.common.xcontent.XContentType;
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
import org.elasticsearch.discovery.DiscoverySettings;
|
import org.elasticsearch.discovery.DiscoverySettings;
|
||||||
|
@ -78,6 +79,7 @@ import static org.hamcrest.CoreMatchers.hasItem;
|
||||||
import static org.hamcrest.CoreMatchers.hasItems;
|
import static org.hamcrest.CoreMatchers.hasItems;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
import static org.hamcrest.Matchers.startsWith;
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
|
|
||||||
public class ElasticsearchExceptionTests extends ESTestCase {
|
public class ElasticsearchExceptionTests extends ESTestCase {
|
||||||
|
@ -124,13 +126,13 @@ public class ElasticsearchExceptionTests extends ESTestCase {
|
||||||
} else {
|
} else {
|
||||||
rootCauses = ElasticsearchException.guessRootCauses(randomBoolean() ? new RemoteTransportException("remoteboom", ex) : ex);
|
rootCauses = ElasticsearchException.guessRootCauses(randomBoolean() ? new RemoteTransportException("remoteboom", ex) : ex);
|
||||||
}
|
}
|
||||||
assertEquals(ElasticsearchException.getExceptionName(rootCauses[0]), "parsing_exception");
|
assertEquals("parsing_exception", ElasticsearchException.getExceptionName(rootCauses[0]));
|
||||||
assertEquals(rootCauses[0].getMessage(), "foobar");
|
assertEquals("foobar", rootCauses[0].getMessage());
|
||||||
|
|
||||||
ElasticsearchException oneLevel = new ElasticsearchException("foo", new RuntimeException("foobar"));
|
ElasticsearchException oneLevel = new ElasticsearchException("foo", new RuntimeException("foobar"));
|
||||||
rootCauses = oneLevel.guessRootCauses();
|
rootCauses = oneLevel.guessRootCauses();
|
||||||
assertEquals(ElasticsearchException.getExceptionName(rootCauses[0]), "exception");
|
assertEquals("exception", ElasticsearchException.getExceptionName(rootCauses[0]));
|
||||||
assertEquals(rootCauses[0].getMessage(), "foo");
|
assertEquals("foo", rootCauses[0].getMessage());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ShardSearchFailure failure = new ShardSearchFailure(
|
ShardSearchFailure failure = new ShardSearchFailure(
|
||||||
|
@ -146,20 +148,40 @@ public class ElasticsearchExceptionTests extends ESTestCase {
|
||||||
assertEquals(rootCauses.length, 2);
|
assertEquals(rootCauses.length, 2);
|
||||||
assertEquals(ElasticsearchException.getExceptionName(rootCauses[0]), "parsing_exception");
|
assertEquals(ElasticsearchException.getExceptionName(rootCauses[0]), "parsing_exception");
|
||||||
assertEquals(rootCauses[0].getMessage(), "foobar");
|
assertEquals(rootCauses[0].getMessage(), "foobar");
|
||||||
assertEquals(((ParsingException) rootCauses[0]).getLineNumber(), 1);
|
assertEquals(1, ((ParsingException) rootCauses[0]).getLineNumber());
|
||||||
assertEquals(((ParsingException) rootCauses[0]).getColumnNumber(), 2);
|
assertEquals(2, ((ParsingException) rootCauses[0]).getColumnNumber());
|
||||||
assertEquals(ElasticsearchException.getExceptionName(rootCauses[1]), "query_shard_exception");
|
assertEquals("query_shard_exception", ElasticsearchException.getExceptionName(rootCauses[1]));
|
||||||
assertEquals((rootCauses[1]).getIndex().getName(), "foo1");
|
assertEquals("foo1", rootCauses[1].getIndex().getName());
|
||||||
assertEquals(rootCauses[1].getMessage(), "foobar");
|
assertEquals("foobar", rootCauses[1].getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
final ElasticsearchException[] foobars = ElasticsearchException.guessRootCauses(new IllegalArgumentException("foobar"));
|
final ElasticsearchException[] foobars = ElasticsearchException.guessRootCauses(new IllegalArgumentException("foobar"));
|
||||||
assertEquals(foobars.length, 1);
|
assertEquals(foobars.length, 1);
|
||||||
assertTrue(foobars[0] instanceof ElasticsearchException);
|
assertThat(foobars[0], instanceOf(ElasticsearchException.class));
|
||||||
assertEquals(foobars[0].getMessage(), "foobar");
|
assertEquals("foobar", foobars[0].getMessage());
|
||||||
assertEquals(foobars[0].getCause().getClass(), IllegalArgumentException.class);
|
assertEquals(IllegalArgumentException.class, foobars[0].getCause().getClass());
|
||||||
assertEquals(foobars[0].getExceptionName(), "illegal_argument_exception");
|
assertEquals("illegal_argument_exception", foobars[0].getExceptionName());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
XContentParseException inner = new XContentParseException(null, "inner");
|
||||||
|
XContentParseException outer = new XContentParseException(null, "outer", inner);
|
||||||
|
final ElasticsearchException[] causes = ElasticsearchException.guessRootCauses(outer);
|
||||||
|
assertEquals(causes.length, 1);
|
||||||
|
assertThat(causes[0], instanceOf(ElasticsearchException.class));
|
||||||
|
assertEquals("inner", causes[0].getMessage());
|
||||||
|
assertEquals("x_content_parse_exception", causes[0].getExceptionName());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ElasticsearchException inner = new ElasticsearchException("inner");
|
||||||
|
XContentParseException outer = new XContentParseException(null, "outer", inner);
|
||||||
|
final ElasticsearchException[] causes = ElasticsearchException.guessRootCauses(outer);
|
||||||
|
assertEquals(causes.length, 1);
|
||||||
|
assertThat(causes[0], instanceOf(ElasticsearchException.class));
|
||||||
|
assertEquals("inner", causes[0].getMessage());
|
||||||
|
assertEquals("exception", causes[0].getExceptionName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue