CLI stack traces when debugging (#960)

Sometimes we get very short and unhelpful exceptions from deep in
pantheon ('null' for example).  This change makes it so that if
--logging is at DEBUG, TRACE, or ALL then the stack trace that caused
the exit will also be printed.

This is done by providing our own impl of defaultExceptionHandler that
is aware of the logLevel in PantheonCommand.
Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
Danno Ferrin 6 years ago committed by GitHub
parent 76366505fa
commit 0fcde8c89e
  1. 3
      pantheon/src/main/java/tech/pegasys/pantheon/Pantheon.java
  2. 5
      pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java
  3. 44
      pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java
  4. 4
      pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java

@ -13,7 +13,6 @@
package tech.pegasys.pantheon;
import static org.apache.logging.log4j.LogManager.getLogger;
import static picocli.CommandLine.defaultExceptionHandler;
import tech.pegasys.pantheon.cli.PantheonCommand;
import tech.pegasys.pantheon.cli.PantheonControllerBuilder;
@ -38,7 +37,7 @@ public final class Pantheon {
pantheonCommand.parse(
new RunLast().andExit(SUCCESS_EXIT_CODE),
defaultExceptionHandler().andExit(ERROR_EXIT_CODE),
pantheonCommand.exceptionHandler().andExit(ERROR_EXIT_CODE),
System.in,
args);
}

@ -18,7 +18,6 @@ import java.util.List;
import picocli.CommandLine;
import picocli.CommandLine.AbstractParseResultHandler;
import picocli.CommandLine.DefaultExceptionHandler;
import picocli.CommandLine.ExecutionException;
import picocli.CommandLine.Model.OptionSpec;
import picocli.CommandLine.ParseResult;
@ -27,13 +26,13 @@ class ConfigOptionSearchAndRunHandler extends AbstractParseResultHandler<List<Ob
private static final String DOCKER_CONFIG_LOCATION = "/etc/pantheon/pantheon.conf";
private final AbstractParseResultHandler<List<Object>> resultHandler;
private final DefaultExceptionHandler<List<Object>> exceptionHandler;
private final CommandLine.IExceptionHandler2<List<Object>> exceptionHandler;
private final String configFileOptionName;
private final boolean isDocker;
ConfigOptionSearchAndRunHandler(
final AbstractParseResultHandler<List<Object>> resultHandler,
final DefaultExceptionHandler<List<Object>> exceptionHandler,
final CommandLine.IExceptionHandler2<List<Object>> exceptionHandler,
final String configFileOptionName,
final boolean isDocker) {
this.resultHandler = resultHandler;

@ -70,8 +70,10 @@ import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import com.google.common.base.Suppliers;
import com.google.common.io.Resources;
import io.vertx.core.Vertx;
import io.vertx.core.json.DecodeException;
@ -82,7 +84,6 @@ import org.apache.logging.log4j.core.config.Configurator;
import picocli.CommandLine;
import picocli.CommandLine.AbstractParseResultHandler;
import picocli.CommandLine.Command;
import picocli.CommandLine.DefaultExceptionHandler;
import picocli.CommandLine.ExecutionException;
import picocli.CommandLine.ITypeConverter;
import picocli.CommandLine.Option;
@ -458,6 +459,39 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
"The address to which the privacy pre-compiled contract will be mapped to (default: ${DEFAULT-VALUE})")
private final Integer privacyPrecompiledAddress = Address.PRIVACY;
// Inner class so we can get to loggingLevel.
public class PantheonExceptionHandler
extends CommandLine.AbstractHandler<List<Object>, PantheonExceptionHandler>
implements CommandLine.IExceptionHandler2<List<Object>> {
@Override
public List<Object> handleParseException(final ParameterException ex, final String[] args) {
if (logLevel != null && Level.DEBUG.isMoreSpecificThan(logLevel)) {
ex.printStackTrace(err());
} else {
err().println(ex.getMessage());
}
if (!CommandLine.UnmatchedArgumentException.printSuggestions(ex, err())) {
ex.getCommandLine().usage(err(), ansi());
}
return returnResultOrExit(null);
}
@Override
public List<Object> handleExecutionException(
final ExecutionException ex, final CommandLine.ParseResult parseResult) {
return throwOrExit(ex);
}
@Override
protected PantheonExceptionHandler self() {
return this;
}
}
private Supplier<PantheonExceptionHandler> exceptionHandlerSupplier =
Suppliers.memoize(PantheonExceptionHandler::new);
public PantheonCommand(
final Logger logger,
final BlockImporter blockImporter,
@ -475,7 +509,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
public void parse(
final AbstractParseResultHandler<List<Object>> resultHandler,
final DefaultExceptionHandler<List<Object>> exceptionHandler,
final PantheonExceptionHandler exceptionHandler,
final InputStream in,
final String... args) {
@ -578,7 +612,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
metricsConfiguration(),
permissioningConfiguration);
} catch (Exception e) {
throw new ParameterException(this.commandLine, e.getMessage());
throw new ParameterException(this.commandLine, e.getMessage(), e);
}
}
@ -1028,4 +1062,8 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
public MetricsSystem getMetricsSystem() {
return metricsSystem;
}
public PantheonExceptionHandler exceptionHandler() {
return exceptionHandlerSupplier.get();
}
}

@ -36,7 +36,6 @@ import java.io.PrintStream;
import java.net.URI;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -51,7 +50,6 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import picocli.CommandLine;
import picocli.CommandLine.DefaultExceptionHandler;
import picocli.CommandLine.Help.Ansi;
import picocli.CommandLine.RunLast;
@ -159,7 +157,7 @@ public abstract class CommandTestAbstract {
// parse using Ansi.OFF to be able to assert on non formatted output results
pantheonCommand.parse(
new RunLast().useOut(outPrintStream).useAnsi(Ansi.OFF),
new DefaultExceptionHandler<List<Object>>().useErr(errPrintStream).useAnsi(Ansi.OFF),
pantheonCommand.exceptionHandler().useErr(errPrintStream).useAnsi(Ansi.OFF),
in,
args);
return pantheonCommand.spec;

Loading…
Cancel
Save