From 452224a201693ef9e5d6c89ff35b9e288ebf443b Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Thu, 16 Jul 2020 09:20:30 -0500 Subject: [PATCH] clarify scenario error handling logic --- .../engine/core/ScenarioErrorHandler.java | 83 +++++++++++++++++++ .../engine/core/script/Scenario.java | 34 ++------ 2 files changed, 90 insertions(+), 27 deletions(-) create mode 100644 engine-core/src/main/java/io/nosqlbench/engine/core/ScenarioErrorHandler.java diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/ScenarioErrorHandler.java b/engine-core/src/main/java/io/nosqlbench/engine/core/ScenarioErrorHandler.java new file mode 100644 index 000000000..f08bfc006 --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/ScenarioErrorHandler.java @@ -0,0 +1,83 @@ +package io.nosqlbench.engine.core; + +import io.nosqlbench.nb.api.errors.BasicError; +import org.graalvm.polyglot.PolyglotException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.script.ScriptException; + +/** + * This class is meant to consolidate the error handling logic for the varios types of errors + * that may bubble up from the layers within NoSQLBench. As a layered system, some of the included + * libraries tend to layer the exceptions beyond a point of recognizability. The logic in this + * class should do the following: + * + *
    + *
  1. Report an error in the most intelligible way to the user.
  2. + *
+ * + * That is all. When this error handler is invoked, it is a foregone conclusion that the scenario + * is not able to continue, else the error would have been trapped and handled internal to a lower-level + * class. It is the calling exception handler's responsibility to finally shut down the scenario + * cleanly and return appropriately. Thus, You should not throw errors from this class. You should only + * unwrap and explain errors, sending contents to the logfile as appropriate. + * + */ +public class ScenarioErrorHandler { + + private final static Logger logger = LoggerFactory.getLogger(ScenarioErrorHandler.class); + + public static void handle(String script, Throwable t, boolean wantsStackTraces) { + if (t instanceof ScriptException) { + handleScriptException(script, (ScriptException) t, wantsStackTraces); + } else if (t instanceof BasicError) { + handleBasicError((BasicError) t, wantsStackTraces); + } else if (t instanceof Exception){ + handleInternalError((Exception) t, wantsStackTraces); + } + } + + private static void handleInternalError(Exception e, boolean wantsStackTraces) { + String prefix = "internal error: "; + if (!e.getCause().getClass().getCanonicalName().contains("io.nosqlbench")) { + prefix = "Error from driver or included library: "; + } + + if (wantsStackTraces) { + logger.error(prefix + e.getMessage(),e); + } else { + logger.error(e.getMessage()); + logger.error("for the full stack trace, run with --show-stacktraces"); + } + } + + private static void handleScriptException(String script, ScriptException e, boolean wantsStackTraces) { + Throwable cause = e.getCause(); + if (cause instanceof PolyglotException) { + Throwable hostException = ((PolyglotException) cause).asHostException(); + if (hostException instanceof BasicError) { + handleBasicError((BasicError)hostException, wantsStackTraces); + } else { + handle(script,hostException, wantsStackTraces); + } + } else { + if (wantsStackTraces) { + logger.error("Unknown script exception:",e); + } else { + logger.error(e.getMessage()); + logger.error("for the full stack trace, run with --show-stacktraces"); + } + } + } + + private static void handleBasicError(BasicError e, boolean wantsStackTraces) { + if (wantsStackTraces) { + logger.error(e.getMessage(),e); + } else { + logger.error(e.getMessage()); + logger.error("for the full stack trace, run with --show-stacktraces"); + } + } + +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/script/Scenario.java b/engine-core/src/main/java/io/nosqlbench/engine/core/script/Scenario.java index 95802df3f..3a09e05a6 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/script/Scenario.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/script/Scenario.java @@ -19,16 +19,12 @@ import com.codahale.metrics.MetricRegistry; import com.oracle.truffle.js.scriptengine.GraalJSScriptEngine; import io.nosqlbench.engine.core.*; import io.nosqlbench.engine.core.metrics.PolyglotMetricRegistryBindings; -import io.nosqlbench.nb.api.errors.BasicError; import io.nosqlbench.engine.api.extensions.ScriptingPluginInfo; import io.nosqlbench.engine.api.metrics.ActivityMetrics; import io.nosqlbench.engine.core.metrics.NashornMetricRegistryBindings; import io.nosqlbench.engine.api.scripting.ScriptEnvBuffer; import jdk.nashorn.api.scripting.NashornScriptEngineFactory; -import org.graalvm.polyglot.Context; -import org.graalvm.polyglot.EnvironmentAccess; -import org.graalvm.polyglot.HostAccess; -import org.graalvm.polyglot.PolyglotAccess; +import org.graalvm.polyglot.*; import org.slf4j.LoggerFactory; import javax.script.*; @@ -61,17 +57,19 @@ public class Scenario implements Callable { private ScenarioLogger scenarioLogger; private ScriptParams scenarioScriptParams; private Engine engine = Engine.Graalvm; + private boolean wantsStackTraces=false; public enum Engine { Nashorn, Graalvm } - public Scenario(String name, Engine engine, String progressInterval, boolean wantsGraaljsCompatMode) { + public Scenario(String name, Engine engine, String progressInterval, boolean wantsGraaljsCompatMode, boolean wantsStackTraces) { this.name = name; this.engine = engine; this.progressInterval = progressInterval; this.wantsGraaljsCompatMode = wantsGraaljsCompatMode; + this.wantsStackTraces = wantsStackTraces; } public Scenario(String name, Engine engine) { @@ -218,28 +216,10 @@ public class Scenario implements Callable { } System.err.flush(); System.out.flush(); - } catch (ScriptException e) { - String diagname = "diag_" + System.currentTimeMillis() + ".js"; - try { - Path diagFilePath = Paths.get(scenarioLogger.getLogDir(), diagname); - Files.writeString(diagFilePath, script); - } catch (Exception ignored) { - } - String errorDesc = "Script error while running scenario:" + e.toString() + ", script content is at " + diagname; - e.printStackTrace(); - logger.error(errorDesc, e); - scenarioController.forceStopScenario(5000); - throw new RuntimeException("Script error while running scenario:" + e.getMessage(), e); - } catch (BasicError ue) { - logger.error(ue.getMessage()); - scenarioController.forceStopScenario(5000); - throw ue; - } catch (Exception o) { - String errorDesc = "Non-Script error while running scenario:" + o.getMessage(); - logger.error(errorDesc, o); - scenarioController.forceStopScenario(5000); - throw new RuntimeException("Non-Script error while running scenario:" + o.getMessage(), o); + } catch (Exception e) { + ScenarioErrorHandler.handle(script,e,wantsStackTraces); } finally { + scenarioController.forceStopScenario(5000); System.out.flush(); System.err.flush(); }