From 778f7668255281ded99df191ef2d6979d0d77c1b Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Tue, 26 Nov 2024 15:37:50 +1100 Subject: [PATCH] fix: display JVM errors in dev mode --- .../echo/src/main/java/ftl/echo/Echo.java | 1 - .../block/ftl/deployment/ModuleProcessor.java | 50 +++++++++++++++++++ .../xyz/block/ftl/runtime/FTLRecorder.java | 2 +- .../xyz/block/ftl/runtime/HotReloadSetup.java | 6 ++- 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/examples/java/echo/src/main/java/ftl/echo/Echo.java b/examples/java/echo/src/main/java/ftl/echo/Echo.java index 7195437154..a73c468d53 100644 --- a/examples/java/echo/src/main/java/ftl/echo/Echo.java +++ b/examples/java/echo/src/main/java/ftl/echo/Echo.java @@ -3,7 +3,6 @@ import ftl.time.TimeClient; import xyz.block.ftl.Export; import xyz.block.ftl.Verb; -import xyz.block.ftl.VerbName; public class Echo { diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java index 5bc8cae3f8..324740b20e 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java @@ -13,6 +13,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Timer; +import java.util.TimerTask; import java.util.stream.Collectors; import org.jboss.jandex.DotName; @@ -21,6 +23,7 @@ import org.tomlj.TomlParseResult; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; +import io.quarkus.bootstrap.classloading.QuarkusClassLoader; import io.quarkus.deployment.IsDevelopment; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; @@ -33,6 +36,7 @@ import io.quarkus.deployment.builditem.RunTimeConfigBuilderBuildItem; import io.quarkus.deployment.builditem.ShutdownContextBuildItem; import io.quarkus.deployment.builditem.SystemPropertyBuildItem; +import io.quarkus.deployment.dev.RuntimeUpdatesProcessor; import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem; import io.quarkus.grpc.deployment.BindableServiceBuildItem; import io.quarkus.vertx.http.deployment.RequireSocketHttpBuildItem; @@ -46,6 +50,8 @@ import xyz.block.ftl.runtime.VerbRegistry; import xyz.block.ftl.runtime.config.FTLConfigSourceFactoryBuilder; import xyz.block.ftl.runtime.http.FTLHttpHandler; +import xyz.block.ftl.v1.language.Error; +import xyz.block.ftl.v1.language.ErrorList; public class ModuleProcessor { @@ -72,6 +78,50 @@ public SystemPropertyBuildItem moduleNameConfig(ApplicationInfoBuildItem applica return new SystemPropertyBuildItem("ftl.module.name", applicationInfoBuildItem.getName()); } + private static volatile Timer devModeProblemTimer; + + @BuildStep(onlyIf = IsDevelopment.class) + @Record(ExecutionTime.STATIC_INIT) + public void reportDevModeProblems(FTLRecorder recorder, OutputTargetBuildItem outputTargetBuildItem) { + if (devModeProblemTimer != null) { + return; + } + Path errorOutput = outputTargetBuildItem.getOutputDirectory().resolve(ERRORS_OUT); + devModeProblemTimer = new Timer("FTL Dev Mode Error Report", true); + devModeProblemTimer.schedule(new TimerTask() { + @Override + public void run() { + Throwable compileProblem = RuntimeUpdatesProcessor.INSTANCE.getCompileProblem(); + Throwable deploymentProblems = RuntimeUpdatesProcessor.INSTANCE.getDeploymentProblem(); + if (compileProblem != null || deploymentProblems != null) { + ErrorList.Builder builder = ErrorList.newBuilder(); + if (compileProblem != null) { + builder.addErrors( + Error.newBuilder().setLevel(Error.ErrorLevel.ERROR).setType(Error.ErrorType.COMPILER) + .setMsg(compileProblem.getMessage()).build()); + } + if (deploymentProblems != null) { + builder.addErrors( + Error.newBuilder().setLevel(Error.ErrorLevel.ERROR).setType(Error.ErrorType.FTL) + .setMsg(deploymentProblems.getMessage()).build()); + } + try (var out = Files.newOutputStream(errorOutput)) { + builder.build().writeTo(out); + } catch (IOException e) { + log.error("Failed to write error list", e); + } + } + } + }, 1000, 1000); + ((QuarkusClassLoader) Thread.currentThread().getContextClassLoader()).addCloseTask(new Runnable() { + @Override + public void run() { + devModeProblemTimer.cancel(); + devModeProblemTimer = null; + } + }); + } + @BuildStep ModuleNameBuildItem moduleName(ApplicationInfoBuildItem applicationInfoBuildItem, ApplicationArchivesBuildItem archivesBuildItem) throws IOException { diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java index f3c497777d..8b2ef9a4a8 100644 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java @@ -158,7 +158,7 @@ public Object extractParameter(ResteasyReactiveRequestContext context) { } public void startReloadTimer(ShutdownContext shutdownContext) { - Timer t = new Timer(); + Timer t = new Timer("FTL Hot Reload Timer", true); t.schedule(new TimerTask() { @Override public void run() { diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/HotReloadSetup.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/HotReloadSetup.java index 14dc297017..e8769e693b 100644 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/HotReloadSetup.java +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/HotReloadSetup.java @@ -1,11 +1,15 @@ package xyz.block.ftl.runtime; +import org.jboss.logging.Logger; + import io.quarkus.dev.spi.HotReplacementContext; import io.quarkus.dev.spi.HotReplacementSetup; public class HotReloadSetup implements HotReplacementSetup { static volatile HotReplacementContext context; + private static volatile String errorOutputPath; + private static final String ERRORS_OUT = "errors.pb"; @Override public void setupHotDeployment(HotReplacementContext hrc) { @@ -17,7 +21,7 @@ static void doScan() { try { context.doScan(false); } catch (Exception e) { - // ignore + Logger.getLogger(HotReloadSetup.class).error("Failed to scan for changes", e); } } }