Skip to content

Commit 26c256c

Browse files
authored
Print full exception when console is non-interactive (#88297)
The console logger truncates stack traces so a user is not bombarded with enormouse messages on startup. However, when the output is redirected to a file, there is no need to avoid large messages, and in fact it is a hinderance to debugging. This commit adds an internal flag to the console logging exception convert which disables the truncation when attached to a non-interactive console.
1 parent 51fa9e3 commit 26c256c

File tree

4 files changed

+42
-8
lines changed

4 files changed

+42
-8
lines changed

docs/changelog/88297.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 88297
2+
summary: Print full exception when console is non-interactive
3+
area: Infra/Core
4+
type: enhancement
5+
issues: []

server/src/main/java/org/elasticsearch/bootstrap/BootstrapInfo.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
package org.elasticsearch.bootstrap;
1010

1111
import org.apache.lucene.util.SetOnce;
12+
import org.elasticsearch.core.Nullable;
1213
import org.elasticsearch.core.SuppressForbidden;
1314

1415
import java.util.Dictionary;
@@ -50,8 +51,10 @@ public static boolean isSystemCallFilterInstalled() {
5051
}
5152

5253
/**
53-
* Returns a reference to a stream attached to Standard Output, iff we have determined that stdout is a console (tty)
54+
* Returns information about the console (tty) attached to the server process, or {@code null}
55+
* if no console is attached.
5456
*/
57+
@Nullable
5558
public static ConsoleLoader.Console getConsole() {
5659
return console.get();
5760
}
@@ -143,7 +146,7 @@ public static Dictionary<Object, Object> getSystemProperties() {
143146

144147
public static void init() {}
145148

146-
static void setConsole(ConsoleLoader.Console console) {
149+
static void setConsole(@Nullable ConsoleLoader.Console console) {
147150
BootstrapInfo.console.set(console);
148151
}
149152

server/src/main/java/org/elasticsearch/common/logging/ConsoleThrowablePatternConverter.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,25 @@
1414
import org.apache.logging.log4j.core.pattern.ConverterKeys;
1515
import org.apache.logging.log4j.core.pattern.PatternConverter;
1616
import org.apache.logging.log4j.core.pattern.ThrowablePatternConverter;
17+
import org.elasticsearch.bootstrap.BootstrapInfo;
1718
import org.elasticsearch.bootstrap.StartupException;
1819
import org.elasticsearch.common.inject.CreationException;
1920

2021
/**
21-
* Outputs a very short version of exceptions for the console, pointing to full log for details.
22+
* Outputs a very short version of exceptions for an interactive console, pointing to full log for details.
23+
*
24+
* <p> If a non-interactive console is attached, the full exception is always printed.
2225
*/
2326
@Plugin(name = "consoleException", category = PatternConverter.CATEGORY)
2427
@ConverterKeys({ "consoleException" })
2528
public class ConsoleThrowablePatternConverter extends ThrowablePatternConverter {
26-
private ConsoleThrowablePatternConverter(String[] options, Configuration config) {
29+
30+
// true if exceptions should be truncated, false if they should be delegated to the super class
31+
private final boolean enabled;
32+
33+
private ConsoleThrowablePatternConverter(String[] options, Configuration config, boolean enabled) {
2734
super("ConsoleThrowablePatternConverter", "throwable", options, config);
35+
this.enabled = enabled;
2836
}
2937

3038
/**
@@ -34,20 +42,26 @@ private ConsoleThrowablePatternConverter(String[] options, Configuration config)
3442
* @return instance of class.
3543
*/
3644
public static ConsoleThrowablePatternConverter newInstance(final Configuration config, final String[] options) {
37-
return new ConsoleThrowablePatternConverter(options, config);
45+
return newInstance(config, options, BootstrapInfo.getConsole() != null);
46+
}
47+
48+
// package private for tests
49+
static ConsoleThrowablePatternConverter newInstance(final Configuration config, final String[] options, boolean enabled) {
50+
return new ConsoleThrowablePatternConverter(options, config, enabled);
3851
}
3952

4053
@Override
4154
public void format(final LogEvent event, final StringBuilder toAppendTo) {
4255
Throwable error = event.getThrown();
43-
if (error == null) {
56+
if (enabled == false | error == null) {
4457
super.format(event, toAppendTo);
4558
return;
4659
}
4760
if (error instanceof StartupException e) {
4861
error = e.getCause();
4962
toAppendTo.append("\n\nElasticsearch failed to startup normally.\n\n");
5063
}
64+
5165
appendShortStacktrace(error, toAppendTo);
5266
if (error instanceof CreationException) {
5367
toAppendTo.append("There were problems initializing Guice. See log for more details.");

server/src/test/java/org/elasticsearch/common/logging/ConsoleThrowablePatternConverterTests.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,31 @@
2626
import static org.hamcrest.Matchers.not;
2727

2828
public class ConsoleThrowablePatternConverterTests extends ESTestCase {
29-
static final ConsoleThrowablePatternConverter converter = ConsoleThrowablePatternConverter.newInstance(null, null);
29+
static final ConsoleThrowablePatternConverter converter = ConsoleThrowablePatternConverter.newInstance(null, null, true);
3030

31-
String format(Throwable e) {
31+
String format(ConsoleThrowablePatternConverter converter, Throwable e) {
3232
LogEvent event = Log4jLogEvent.newBuilder().setThrown(e).build();
3333
var builder = new StringBuilder();
3434
converter.format(event, builder);
3535
return builder.toString();
3636
}
3737

38+
String format(Throwable e) {
39+
return format(converter, e);
40+
}
41+
3842
public void testNoException() {
3943
assertThat(format(null), emptyString());
4044
}
4145

46+
public void testDisabledPassthrough() {
47+
// mimic no interactive console
48+
var converter = ConsoleThrowablePatternConverter.newInstance(null, null, false);
49+
var e = new StartupException(new RuntimeException("a cause"));
50+
// the cause of StartupException is not extracted by the parent pattern converter
51+
assertThat(format(converter, e), allOf(containsString("StartupException: "), containsString("RuntimeException: a cause")));
52+
}
53+
4254
public void testStartupExceptionUnwrapped() {
4355
var e = new StartupException(new RuntimeException("an error"));
4456
assertThat(

0 commit comments

Comments
 (0)