From f5c4cadbc539ac5d5998988a7b639288866e3ce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galder=20Zamarren=CC=83o?= Date: Tue, 17 Nov 2020 12:07:49 +0100 Subject: [PATCH] Add build-time min-level logging option #12938 * It enables log levels below that to be folded in native. * Runtime min-level has been removed (was deprecated already). --- .github/native-tests.json | 2 +- .../logging/LoggingResourceProcessor.java | 208 +++++++++++++++++- .../pkg/steps/NativeImageBuildStep.java | 1 + .../logging/CategoryBuildTimeConfig.java | 22 ++ .../runtime/logging/CategoryConfig.java | 5 +- .../runtime/logging/LogBuildTimeConfig.java | 21 ++ .../io/quarkus/runtime/logging/LogConfig.java | 9 - .../runtime/logging/LoggingSetupRecorder.java | 32 ++- docs/src/main/asciidoc/cdi-reference.adoc | 1 + docs/src/main/asciidoc/logging.adoc | 15 +- docs/src/main/asciidoc/mongodb-panache.adoc | 1 + docs/src/main/asciidoc/optaplanner.adoc | 1 + docs/src/main/asciidoc/security-jwt.adoc | 2 +- docs/src/main/asciidoc/vault-auth.adoc | 1 + .../application-form-auth.properties | 2 + .../src/test/resources/application.properties | 2 + ...t-start-config-named-datasource.properties | 2 + .../test/resources/test-logging.properties | 2 + ...websocket-sink-test-application.properties | 1 + .../src/main/resources/application.properties | 1 + .../src/main/resources/application.properties | 2 + .../src/main/resources/nopaging.properties | 1 + .../src/main/resources/nopaging.properties | 1 + .../src/main/resources/application.properties | 2 + .../logging-min-level-set/pom.xml | 122 ++++++++++ .../logging/minlevel/set/LoggingWitness.java | 44 ++++ .../set/above/LoggingMinLevelAbove.java | 31 +++ .../set/below/LoggingMinLevelBelow.java | 24 ++ .../bydefault/LoggingMinLevelByDefault.java | 31 +++ .../set/promote/LoggingMinLevelPromote.java | 31 +++ .../sub/LoggingMinLevelPromoteSub.java | 31 +++ .../src/main/resources/application.properties | 4 + .../it/logging/minlevel/set/Asserts.java | 46 ++++ .../set/LoggingMinLevelAboveTest.java | 37 ++++ .../set/LoggingMinLevelBelowTest.java | 27 +++ .../set/LoggingMinLevelByDefaultTest.java | 40 ++++ .../set/LoggingMinLevelPromoteSubTest.java | 36 +++ .../set/LoggingMinLevelPromoteTest.java | 36 +++ .../set/NativeLoggingMinLevelAboveIT.java | 9 + .../set/NativeLoggingMinLevelBelowIT.java | 9 + .../set/NativeLoggingMinLevelByDefaultIT.java | 9 + .../set/NativeLoggingMinLevelPromoteIT.java | 9 + .../NativeLoggingMinLevelPromoteSubIT.java | 9 + .../minlevel/set/SetRuntimeLogLevels.java | 24 ++ .../logging-min-level-unset/pom.xml | 122 ++++++++++ .../minlevel/unset/LoggingWitness.java | 39 ++++ .../unset/above/LoggingMinLevelAbove.java | 38 ++++ .../bydefault/LoggingMinLevelByDefault.java | 38 ++++ .../unset/promote/LoggingMinLevelPromote.java | 37 ++++ .../it/logging/minlevel/unset/Asserts.java | 46 ++++ .../unset/LoggingMinLevelAboveTest.java | 46 ++++ .../unset/LoggingMinLevelByDefaultTest.java | 46 ++++ .../unset/LoggingMinLevelPromoteTest.java | 49 +++++ .../NativeLoggingMinLevelByDefaultIT.java | 9 + .../unset/NativeLoggingMinLevelPromoteIT.java | 9 + .../unset/NativeLoggingWarnResourceIT.java | 9 + .../minlevel/unset/SetRuntimeLogLevels.java | 22 ++ .../src/main/resources/application.properties | 1 + .../src/main/resources/application.properties | 1 + .../src/main/resources/application.properties | 1 + .../src/main/resources/application.properties | 3 + integration-tests/pom.xml | 2 + .../test/resources/application-tl.properties | 1 + .../application-vault-datasource.properties | 2 + .../src/main/resources/application.properties | 1 + .../application-vault-approle-wrap.properties | 1 + .../application-vault-approle.properties | 1 + ...ication-vault-client-token-wrap.properties | 1 + .../application-vault-kubernetes.properties | 1 + ...cation-vault-userpass-kvv1-wrap.properties | 1 + ...cation-vault-userpass-kvv2-wrap.properties | 1 + .../resources/application-vault.properties | 1 + 72 files changed, 1458 insertions(+), 17 deletions(-) create mode 100644 core/runtime/src/main/java/io/quarkus/runtime/logging/CategoryBuildTimeConfig.java create mode 100644 integration-tests/logging-min-level-set/pom.xml create mode 100644 integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/LoggingWitness.java create mode 100644 integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/above/LoggingMinLevelAbove.java create mode 100644 integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/below/LoggingMinLevelBelow.java create mode 100644 integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/bydefault/LoggingMinLevelByDefault.java create mode 100644 integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/promote/LoggingMinLevelPromote.java create mode 100644 integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/promote/sub/LoggingMinLevelPromoteSub.java create mode 100644 integration-tests/logging-min-level-set/src/main/resources/application.properties create mode 100644 integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/Asserts.java create mode 100644 integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelAboveTest.java create mode 100644 integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelBelowTest.java create mode 100644 integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelByDefaultTest.java create mode 100644 integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelPromoteSubTest.java create mode 100644 integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelPromoteTest.java create mode 100644 integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelAboveIT.java create mode 100644 integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelBelowIT.java create mode 100644 integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelByDefaultIT.java create mode 100644 integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelPromoteIT.java create mode 100644 integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelPromoteSubIT.java create mode 100644 integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/SetRuntimeLogLevels.java create mode 100644 integration-tests/logging-min-level-unset/pom.xml create mode 100644 integration-tests/logging-min-level-unset/src/main/java/io/quarkus/it/logging/minlevel/unset/LoggingWitness.java create mode 100644 integration-tests/logging-min-level-unset/src/main/java/io/quarkus/it/logging/minlevel/unset/above/LoggingMinLevelAbove.java create mode 100644 integration-tests/logging-min-level-unset/src/main/java/io/quarkus/it/logging/minlevel/unset/bydefault/LoggingMinLevelByDefault.java create mode 100644 integration-tests/logging-min-level-unset/src/main/java/io/quarkus/it/logging/minlevel/unset/promote/LoggingMinLevelPromote.java create mode 100644 integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/Asserts.java create mode 100644 integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/LoggingMinLevelAboveTest.java create mode 100644 integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/LoggingMinLevelByDefaultTest.java create mode 100644 integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/LoggingMinLevelPromoteTest.java create mode 100644 integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/NativeLoggingMinLevelByDefaultIT.java create mode 100644 integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/NativeLoggingMinLevelPromoteIT.java create mode 100644 integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/NativeLoggingWarnResourceIT.java create mode 100644 integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/SetRuntimeLogLevels.java diff --git a/.github/native-tests.json b/.github/native-tests.json index 40c97790b7e1a..1631dc108c772 100644 --- a/.github/native-tests.json +++ b/.github/native-tests.json @@ -95,7 +95,7 @@ { "category": "Misc1", "timeout": 65, - "test-modules": "maven jackson jsonb jsch jgit quartz qute consul-config" + "test-modules": "maven jackson jsonb jsch jgit quartz qute consul-config logging-min-level-unset logging-min-level-set" }, { "category": "Misc2", diff --git a/core/deployment/src/main/java/io/quarkus/deployment/logging/LoggingResourceProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/logging/LoggingResourceProcessor.java index 05c9d3c89e021..5e91d51af1ffb 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/logging/LoggingResourceProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/logging/LoggingResourceProcessor.java @@ -3,19 +3,24 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.logging.Handler; +import java.util.logging.Level; import java.util.stream.Collectors; import org.jboss.logmanager.EmbeddedConfigurator; +import org.objectweb.asm.Opcodes; import io.quarkus.bootstrap.logging.InitialConfigurator; +import io.quarkus.deployment.GeneratedClassGizmoAdaptor; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.ConsoleFormatterBannerBuildItem; +import io.quarkus.deployment.builditem.GeneratedClassBuildItem; import io.quarkus.deployment.builditem.LogCategoryBuildItem; import io.quarkus.deployment.builditem.LogConsoleFormatBuildItem; import io.quarkus.deployment.builditem.LogHandlerBuildItem; @@ -27,7 +32,19 @@ import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.deployment.metrics.MetricsCapabilityBuildItem; import io.quarkus.deployment.metrics.MetricsFactoryConsumerBuildItem; +import io.quarkus.deployment.pkg.steps.NativeBuild; +import io.quarkus.gizmo.AnnotationCreator; +import io.quarkus.gizmo.BranchResult; +import io.quarkus.gizmo.BytecodeCreator; +import io.quarkus.gizmo.ClassCreator; +import io.quarkus.gizmo.ClassOutput; +import io.quarkus.gizmo.FieldCreator; +import io.quarkus.gizmo.FieldDescriptor; +import io.quarkus.gizmo.MethodCreator; +import io.quarkus.gizmo.MethodDescriptor; +import io.quarkus.gizmo.ResultHandle; import io.quarkus.runtime.RuntimeValue; +import io.quarkus.runtime.logging.CategoryBuildTimeConfig; import io.quarkus.runtime.logging.LogBuildTimeConfig; import io.quarkus.runtime.logging.LogConfig; import io.quarkus.runtime.logging.LogMetricsHandlerRecorder; @@ -35,6 +52,15 @@ public final class LoggingResourceProcessor { + private static final String LOGMANAGER_LOGGER_CLASS_NAME = "io.quarkus.runtime.generated.Target_org_jboss_logmanager_Logger"; + private static final String LOGGING_LOGGER_CLASS_NAME = "io.quarkus.runtime.generated.Target_org_jboss_logging_Logger"; + private static final String LOGGER_NODE_CLASS_NAME = "io.quarkus.runtime.generated.Target_org_jboss_logmanager_LoggerNode"; + + private static final String MIN_LEVEL_COMPUTE_CLASS_NAME = "io.quarkus.runtime.generated.MinLevelCompute"; + private static final MethodDescriptor IS_MIN_LEVEL_ENABLED = MethodDescriptor.ofMethod(MIN_LEVEL_COMPUTE_CLASS_NAME, + "isMinLevelEnabled", + boolean.class, int.class, String.class); + @BuildStep void setupLogFilters(BuildProducer filters) { filters.produce(new LogCleanupFilterBuildItem("org.jboss.threads", "JBoss Threads version")); @@ -93,7 +119,7 @@ void miscSetup( @BuildStep @Record(ExecutionTime.RUNTIME_INIT) - LoggingSetupBuildItem setupLoggingRuntimeInit(LoggingSetupRecorder recorder, LogConfig log, + LoggingSetupBuildItem setupLoggingRuntimeInit(LoggingSetupRecorder recorder, LogConfig log, LogBuildTimeConfig buildLog, List handlerBuildItems, List namedHandlerBuildItems, List consoleFormatItems, Optional possibleBannerBuildItem) { @@ -111,7 +137,7 @@ LoggingSetupBuildItem setupLoggingRuntimeInit(LoggingSetupRecorder recorder, Log if (bannerBuildItem != null) { possibleSupplier = bannerBuildItem.getBannerSupplier(); } - recorder.initializeLogging(log, handlers, namedHandlers, + recorder.initializeLogging(log, buildLog, handlers, namedHandlers, consoleFormatItems.stream().map(LogConsoleFormatBuildItem::getFormatterValue).collect(Collectors.toList()), possibleSupplier); return new LoggingSetupBuildItem(); @@ -140,4 +166,182 @@ void registerMetrics(LogMetricsHandlerRecorder recorder, LogBuildTimeConfig log, logHandler.produce(new LogHandlerBuildItem(recorder.getLogHandler())); } } + + @BuildStep(onlyIf = NativeBuild.class) + void setUpMinLevelLogging(LogBuildTimeConfig log, + final BuildProducer generatedTraceLogger) { + ClassOutput output = new GeneratedClassGizmoAdaptor(generatedTraceLogger, false); + if (log.categories.isEmpty() || allMinLevelInfoOrHigher(log.categories)) { + generateDefaultLoggers(output); + } else { + generateCategoryMinLevelLoggers(log.categories, log.minLevel, output); + } + } + + private static boolean allMinLevelInfoOrHigher(Map categories) { + return categories.values().stream() + .allMatch(categoryConfig -> categoryConfig.minLevel.getLevel().intValue() >= org.jboss.logmanager.Level.INFO + .intValue()); + } + + private static void generateDefaultLoggers(ClassOutput output) { + generateDefaultLoggingLogger(output); + generateDefaultLoggerNode(output); + generateLogManagerLogger(output, LoggingResourceProcessor::generateMinLevelDefault); + } + + private static void generateCategoryMinLevelLoggers(Map categories, Level minLevel, + ClassOutput output) { + generateMinLevelCompute(categories, minLevel.toString(), output); + generateDefaultLoggerNode(output); + generateLogManagerLogger(output, LoggingResourceProcessor::generateMinLevelCheckCategory); + } + + private static BranchResult generateMinLevelCheckCategory(MethodCreator method, FieldDescriptor nameAliasDescriptor) { + final ResultHandle levelIntValue = getParamLevelIntValue(method); + final ResultHandle nameAlias = method.readInstanceField(nameAliasDescriptor, method.getThis()); + return method.ifTrue(method.invokeStaticMethod(IS_MIN_LEVEL_ENABLED, levelIntValue, nameAlias)); + } + + private static void generateMinLevelCompute(Map categories, String defaultMinLevelName, + ClassOutput output) { + try (ClassCreator cc = ClassCreator.builder().setFinal(true) + .className(MIN_LEVEL_COMPUTE_CLASS_NAME) + .classOutput(output).build()) { + + try (MethodCreator mc = cc.getMethodCreator(IS_MIN_LEVEL_ENABLED)) { + mc.setModifiers(Opcodes.ACC_STATIC); + + final ResultHandle level = mc.getMethodParam(0); + final ResultHandle name = mc.getMethodParam(1); + + BytecodeCreator current = mc; + for (Map.Entry entry : categories.entrySet()) { + final String category = entry.getKey(); + final int categoryLevelIntValue = entry.getValue().minLevel.getLevel().intValue(); + + ResultHandle equalsResult = current.invokeVirtualMethod( + MethodDescriptor.ofMethod(Object.class, "equals", boolean.class, Object.class), + name, current.load(category)); + + BranchResult equalsBranch = current.ifTrue(equalsResult); + try (BytecodeCreator false1 = equalsBranch.falseBranch()) { + ResultHandle startsWithResult = false1.invokeVirtualMethod( + MethodDescriptor.ofMethod(String.class, "startsWith", boolean.class, String.class), + name, false1.load(category)); + + BranchResult startsWithBranch = false1.ifTrue(startsWithResult); + + final BytecodeCreator startsWithTrue = startsWithBranch.trueBranch(); + final BranchResult levelCompareBranch = startsWithTrue.ifIntegerGreaterEqual(level, + startsWithTrue.load(categoryLevelIntValue)); + levelCompareBranch.trueBranch().returnValue(levelCompareBranch.trueBranch().load(true)); + levelCompareBranch.falseBranch().returnValue(levelCompareBranch.falseBranch().load(false)); + + current = startsWithBranch.falseBranch(); + } + + equalsBranch.trueBranch().returnValue(equalsBranch.trueBranch().load(true)); + } + + final ResultHandle infoLevelIntValue = getLogManagerLevelIntValue(defaultMinLevelName, current); + final BranchResult isInfoOrHigherBranch = current.ifIntegerGreaterEqual(level, infoLevelIntValue); + isInfoOrHigherBranch.trueBranch().returnValue(isInfoOrHigherBranch.trueBranch().load(true)); + isInfoOrHigherBranch.falseBranch().returnValue(isInfoOrHigherBranch.falseBranch().load(false)); + } + } + } + + private static void generateDefaultLoggerNode(ClassOutput output) { + try (ClassCreator cc = ClassCreator.builder().setFinal(true) + .className(LOGGER_NODE_CLASS_NAME) + .classOutput(output).build()) { + + AnnotationCreator targetClass = cc.addAnnotation("com.oracle.svm.core.annotate.TargetClass"); + targetClass.addValue("className", "org.jboss.logmanager.LoggerNode"); + + final MethodCreator isLoggableLevelMethod = cc.getMethodCreator("isLoggableLevel", boolean.class, int.class); + isLoggableLevelMethod.addAnnotation("com.oracle.svm.core.annotate.Alias"); + isLoggableLevelMethod.returnValue(isLoggableLevelMethod.load(false)); + } + } + + private static void generateLogManagerLogger(ClassOutput output, + BiFunction isMinLevelEnabledFunction) { + try (ClassCreator cc = ClassCreator.builder().setFinal(true) + .className(LOGMANAGER_LOGGER_CLASS_NAME) + .classOutput(output).build()) { + + AnnotationCreator targetClass = cc.addAnnotation("com.oracle.svm.core.annotate.TargetClass"); + targetClass.addValue("className", "org.jboss.logmanager.Logger"); + + FieldCreator nameAlias = cc.getFieldCreator("name", String.class); + nameAlias.addAnnotation("com.oracle.svm.core.annotate.Alias"); + + FieldCreator loggerNodeAlias = cc.getFieldCreator("loggerNode", LOGGER_NODE_CLASS_NAME); + loggerNodeAlias.addAnnotation("com.oracle.svm.core.annotate.Alias"); + + final MethodCreator isLoggableMethod = cc.getMethodCreator("isLoggable", boolean.class, + java.util.logging.Level.class); + isLoggableMethod.addAnnotation("com.oracle.svm.core.annotate.Substitute"); + + final ResultHandle levelIntValue = getParamLevelIntValue(isLoggableMethod); + + final BranchResult levelBranch = isMinLevelEnabledFunction.apply(isLoggableMethod, nameAlias.getFieldDescriptor()); + + final BytecodeCreator levelTrue = levelBranch.trueBranch(); + levelTrue.returnValue( + levelTrue.invokeVirtualMethod( + MethodDescriptor.ofMethod(LOGGER_NODE_CLASS_NAME, "isLoggableLevel", boolean.class, int.class), + levelTrue.readInstanceField(loggerNodeAlias.getFieldDescriptor(), levelTrue.getThis()), + levelIntValue)); + + final BytecodeCreator levelFalse = levelBranch.falseBranch(); + levelFalse.returnValue(levelFalse.load(false)); + } + } + + private static ResultHandle getParamLevelIntValue(MethodCreator method) { + final ResultHandle level = method.getMethodParam(0); + return method + .invokeVirtualMethod(MethodDescriptor.ofMethod(Level.class, "intValue", int.class), level); + } + + private static BranchResult generateMinLevelDefault(MethodCreator method, FieldDescriptor nameAliasDescriptor) { + final ResultHandle levelIntValue = getParamLevelIntValue(method); + final ResultHandle infoLevelIntValue = getLogManagerLevelIntValue("INFO", method); + return method.ifIntegerGreaterEqual(levelIntValue, infoLevelIntValue); + } + + private static ResultHandle getLogManagerLevelIntValue(String levelName, BytecodeCreator method) { + final ResultHandle infoLevel = method.readStaticField( + FieldDescriptor.of(org.jboss.logmanager.Level.class, levelName, org.jboss.logmanager.Level.class)); + return method + .invokeVirtualMethod(MethodDescriptor.ofMethod(Level.class, "intValue", int.class), infoLevel); + } + + private static void generateDefaultLoggingLogger(ClassOutput output) { + try (ClassCreator cc = ClassCreator.builder().setFinal(true) + .className(LOGGING_LOGGER_CLASS_NAME) + .classOutput(output).build()) { + + AnnotationCreator targetClass = cc.addAnnotation("com.oracle.svm.core.annotate.TargetClass"); + targetClass.addValue("className", "org.jboss.logging.Logger"); + + // Constant fold these methods to return false, + // since the build time log level is above this level. + generateFalseFoldMethod("isTraceEnabled", cc); + generateFalseFoldMethod("isDebugEnabled", cc); + } + } + + /** + * Generates a method that is constant-folded to always return false. + */ + private static void generateFalseFoldMethod(String name, ClassCreator cc) { + MethodCreator method = cc.getMethodCreator(name, boolean.class); + method.addAnnotation("com.oracle.svm.core.annotate.Substitute"); + method.addAnnotation("org.graalvm.compiler.api.replacements.Fold"); + method.returnValue(method.load(false)); + } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java index fad2f928d4276..a3ed9b2d6eeb3 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java @@ -168,6 +168,7 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa command.add( "-H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy$BySpaceAndTime"); //the default collection policy results in full GC's 50% of the time command.add("-H:+JNI"); + command.add("-H:+AllowFoldMethods"); command.add("-jar"); command.add(runnerJarName); diff --git a/core/runtime/src/main/java/io/quarkus/runtime/logging/CategoryBuildTimeConfig.java b/core/runtime/src/main/java/io/quarkus/runtime/logging/CategoryBuildTimeConfig.java new file mode 100644 index 0000000000000..3ac342ef05436 --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/logging/CategoryBuildTimeConfig.java @@ -0,0 +1,22 @@ +package io.quarkus.runtime.logging; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class CategoryBuildTimeConfig { + /** + * The minimum log level for this category. + * By default all categories are configured with INFO minimum level. + * + * To get runtime logging below INFO, e.g. DEBUG or TRACE, + * the minimum level has to be adjusted at build time, the right log level needs to be provided at runtime. + * + * As an example, to get TRACE logging, + * minimum level needs to be at TRACE and the runtime log level needs to match that. + * To get DEBUG logging, + * minimum level needs to be set at DEBUG or TRACE (as well as runtime log level). + */ + @ConfigItem(defaultValue = "inherit") + public InheritableLevel minLevel; +} diff --git a/core/runtime/src/main/java/io/quarkus/runtime/logging/CategoryConfig.java b/core/runtime/src/main/java/io/quarkus/runtime/logging/CategoryConfig.java index 9d5115d1c32bc..cb25fa55b14dc 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/logging/CategoryConfig.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/logging/CategoryConfig.java @@ -10,7 +10,10 @@ public class CategoryConfig { /** - * The log level level for this category + * The log level for this category. + * + * Note that to get log levels below INFO, + * the minimum level build time configuration option needs to be adjusted as well. */ @ConfigItem(defaultValue = "inherit") InheritableLevel level; diff --git a/core/runtime/src/main/java/io/quarkus/runtime/logging/LogBuildTimeConfig.java b/core/runtime/src/main/java/io/quarkus/runtime/logging/LogBuildTimeConfig.java index 74d00bcf15404..0c2b040cb2245 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/logging/LogBuildTimeConfig.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/logging/LogBuildTimeConfig.java @@ -1,5 +1,9 @@ package io.quarkus.runtime.logging; +import java.util.Map; +import java.util.logging.Level; + +import io.quarkus.runtime.annotations.ConfigDocSection; import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; @@ -12,4 +16,21 @@ public class LogBuildTimeConfig { */ @ConfigItem(name = "metrics.enabled", defaultValue = "false") public boolean metricsEnabled; + + /** + * The default minimum log level. + */ + @ConfigItem(defaultValue = "INFO") + public Level minLevel; + + /** + * Minimum logging categories. + *

+ * Logging is done on a per-category basis. Each category can be independently configured. + * A configuration which applies to a category will also apply to all sub-categories of that category, + * unless there is a more specific matching sub-category configuration. + */ + @ConfigItem(name = "category") + @ConfigDocSection + public Map categories; } diff --git a/core/runtime/src/main/java/io/quarkus/runtime/logging/LogConfig.java b/core/runtime/src/main/java/io/quarkus/runtime/logging/LogConfig.java index a32aa18da40c4..a03c7eacb375f 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/logging/LogConfig.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/logging/LogConfig.java @@ -33,15 +33,6 @@ public final class LogConfig { @ConfigItem(defaultValue = "INFO") public Level level; - /** - * The default minimum log level - * - * @deprecated this functionality was never implemented, it may be deleted or implemented in a future release. - */ - @ConfigItem(defaultValue = "INFO") - @Deprecated - public Level minLevel; - /** * Console logging. *

diff --git a/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java b/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java index 5e19392330d9d..7aa2e2fab4281 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java @@ -46,6 +46,8 @@ @Recorder public class LoggingSetupRecorder { + private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LoggingSetupRecorder.class); + private static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows"); /** @@ -75,11 +77,14 @@ public LoggingSetupRecorder() { public static void handleFailedStart() { LogConfig config = new LogConfig(); ConfigInstantiator.handleObject(config); - new LoggingSetupRecorder().initializeLogging(config, Collections.emptyList(), Collections.emptyList(), + LogBuildTimeConfig buildConfig = new LogBuildTimeConfig(); + ConfigInstantiator.handleObject(buildConfig); + new LoggingSetupRecorder().initializeLogging(config, buildConfig, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), null); } - public void initializeLogging(LogConfig config, final List>> additionalHandlers, + public void initializeLogging(LogConfig config, LogBuildTimeConfig buildConfig, + final List>> additionalHandlers, final List>> additionalNamedHandlers, final List>> possibleFormatters, final RuntimeValue>> possibleBannerSupplier) { @@ -130,6 +135,21 @@ public void initializeLogging(LogConfig config, final List entry : categories.entrySet()) { + final CategoryBuildTimeConfig buildCategory = isSubsetOf(entry.getKey(), buildConfig.categories); + final Level logLevel = entry.getValue().level.getLevel(); + final Level minLogLevel = buildCategory == null + ? buildConfig.minLevel + : buildCategory.minLevel.getLevel(); + + if (logLevel.intValue() < minLogLevel.intValue()) { + log.warnf("Log level %s for category '%s' set below minimum logging level %s, promoting it to %s", logLevel, + entry.getKey(), minLogLevel, minLogLevel); + + entry.getValue().level = InheritableLevel.of(minLogLevel.toString()); + } + } + for (Map.Entry entry : categories.entrySet()) { final String name = entry.getKey(); final Logger categoryLogger = logContext.getLogger(name); @@ -157,6 +177,14 @@ public void initializeLogging(LogConfig config, final List categories) { + return categories.entrySet().stream() + .filter(buildCategoryEntry -> categoryName.startsWith(buildCategoryEntry.getKey())) + .map(Entry::getValue) + .findFirst() + .orElse(null); + } + private static Map createNamedHandlers(LogConfig config, List>> possibleFormatters, ErrorManager errorManager, List filterElements) { diff --git a/docs/src/main/asciidoc/cdi-reference.adoc b/docs/src/main/asciidoc/cdi-reference.adoc index eeefd0654d6fd..2b4106dca252e 100644 --- a/docs/src/main/asciidoc/cdi-reference.adoc +++ b/docs/src/main/asciidoc/cdi-reference.adoc @@ -441,6 +441,7 @@ When using the dev mode (running `./mvnw clean compile quarkus:dev`), you can se by enabling additional logging via the following line in your `application.properties`. ---- +quarkus.log.category."io.quarkus.arc.processor".min-level=DEBUG quarkus.log.category."io.quarkus.arc.processor".level=DEBUG ---- diff --git a/docs/src/main/asciidoc/logging.adoc b/docs/src/main/asciidoc/logging.adoc index 335fdcf18c171..8c22f8f9353d2 100644 --- a/docs/src/main/asciidoc/logging.adoc +++ b/docs/src/main/asciidoc/logging.adoc @@ -128,9 +128,19 @@ for example, to set the default log level to `INFO` logging and include Hibernat [source, properties] ---- quarkus.log.level=INFO +quarkus.log.category."org.hibernate".min-level=DEBUG quarkus.log.category."org.hibernate".level=DEBUG ---- +Setting a log level below `INFO` requires the minimum log level to be adjusted, +either globally via the `quarkus.log.min-level` property or per-category as shown in the example above, +as well as adjusting the log level itself. + +Minimum logging level sets a floor level that Quarkus will be needed to potentially generate, +opening the door to optimization opportunities. +As an example, in native execution the minimum level enables lower level checks (e.g. `isTraceEnabled`) to be folded to `false`, +resulting in dead code elimination for code that will never to be executed. + All possible properties are listed in <>. NOTE: If you are adding these properties via command line make sure `"` is escaped. @@ -148,6 +158,7 @@ These can also be overridden by attaching a one or more named handlers to a cate |=== |Property Name|Default|Description |quarkus.log.category."".level|INFO footnote:[Some extensions may define customized default log levels for certain categories, in order to reduce log noise by default. Setting the log level in configuration will override any extension-defined log levels.]|The level to use to configure the category named ``. The quotes are necessary. +|quarkus.log.category."".min-level|INFO |The minimum logging level to use to configure the category named ``. The quotes are necessary. |quarkus.log.category."".use-parent-handlers|true|Specify whether or not this logger should send its output to its parent logger. |quarkus.log.category."".handlers=[]|empty footnote:[By default the configured category gets the same handlers attached as the one on the root logger.]|The names of the handlers that you want to attach to a specific category. |=== @@ -162,7 +173,8 @@ The root logger category is handled separately, and is configured via the follow [cols="> and learn how == How to check the errors in the logs == -Set `quarkus.log.category."io.quarkus.smallrye.jwt.runtime.auth.MpJwtValidator".level=TRACE` to see more details about the token verification or decryption errors. +Set `quarkus.log.category."io.quarkus.smallrye.jwt.runtime.auth.MpJwtValidator".level=TRACE` and `quarkus.log.category."io.quarkus.smallrye.jwt.runtime.auth.MpJwtValidator".min-level=TRACE` to see more details about the token verification or decryption errors. == Custom Factories diff --git a/docs/src/main/asciidoc/vault-auth.adoc b/docs/src/main/asciidoc/vault-auth.adoc index 0cd524547707c..37d6fd756b47d 100644 --- a/docs/src/main/asciidoc/vault-auth.adoc +++ b/docs/src/main/asciidoc/vault-auth.adoc @@ -334,6 +334,7 @@ quarkus.datasource.jdbc.url = jdbc:postgresql://:5432/mydatabase quarkus.vault.authentication.kubernetes.role=vault-quickstart-role +quarkus.log.category."io.quarkus.vault".min-level=DEBUG quarkus.log.category."io.quarkus.vault".level=DEBUG ---- diff --git a/extensions/elytron-security-properties-file/deployment/src/test/resources/application-form-auth.properties b/extensions/elytron-security-properties-file/deployment/src/test/resources/application-form-auth.properties index 5b1d3aa17e61e..c5d11ccd073d8 100644 --- a/extensions/elytron-security-properties-file/deployment/src/test/resources/application-form-auth.properties +++ b/extensions/elytron-security-properties-file/deployment/src/test/resources/application-form-auth.properties @@ -6,7 +6,9 @@ #quarkus.log.file.path=/tmp/trace.log #quarkus.log.file.level=TRACE #quarkus.log.file.format=%d{HH:mm:ss} %-5p [%c{2.}]] (%t) %s%e%n +#quarkus.log.category."io.quarkus.arc".min-level=TRACE #quarkus.log.category."io.quarkus.arc".level=TRACE +#quarkus.log.category."io.undertow.request.security".min-level=TRACE #quarkus.log.category."io.undertow.request.security".level=TRACE # Identities diff --git a/extensions/elytron-security-properties-file/deployment/src/test/resources/application.properties b/extensions/elytron-security-properties-file/deployment/src/test/resources/application.properties index 7548cdc43904e..1374708d9e230 100644 --- a/extensions/elytron-security-properties-file/deployment/src/test/resources/application.properties +++ b/extensions/elytron-security-properties-file/deployment/src/test/resources/application.properties @@ -12,5 +12,7 @@ quarkus.security.users.file.plain-text=true #quarkus.log.file.level=TRACE #quarkus.log.file.format=%d{HH:mm:ss} %-5p [%c{2.}]] (%t) %s%e%n # Set 2 categories (io.quarkus.smallrye.jwt, io.undertow.request.security) to TRACE level +#quarkus.log.category."io.quarkus.arc".min-level=TRACE #quarkus.log.category."io.quarkus.arc".level=TRACE +#quarkus.log.category."io.undertow.request.security".min-level=TRACE #quarkus.log.category."io.undertow.request.security".level=TRACE diff --git a/extensions/flyway/deployment/src/test/resources/migrate-at-start-config-named-datasource.properties b/extensions/flyway/deployment/src/test/resources/migrate-at-start-config-named-datasource.properties index 9ecf58480aa2c..516cdc7f038f6 100644 --- a/extensions/flyway/deployment/src/test/resources/migrate-at-start-config-named-datasource.properties +++ b/extensions/flyway/deployment/src/test/resources/migrate-at-start-config-named-datasource.properties @@ -1,4 +1,6 @@ +#quarkus.log.category."org.flywaydb.core".min-level=DEBUG #quarkus.log.category."org.flywaydb.core".level=DEBUG +#quarkus.log.category."io.quarkus.flyway".min-level=DEBUG #quarkus.log.category."io.quarkus.flyway".level=DEBUG quarkus.datasource.users.db-kind=h2 quarkus.datasource.users.username=sa diff --git a/extensions/micrometer/deployment/src/test/resources/test-logging.properties b/extensions/micrometer/deployment/src/test/resources/test-logging.properties index 6eed6ab2596da..5b3dd8a50f7c2 100644 --- a/extensions/micrometer/deployment/src/test/resources/test-logging.properties +++ b/extensions/micrometer/deployment/src/test/resources/test-logging.properties @@ -1,4 +1,6 @@ +#quarkus.log.category."io.quarkus.micrometer".min-level=DEBUG #quarkus.log.category."io.quarkus.micrometer".level=DEBUG quarkus.log.category."io.quarkus.bootstrap".level=INFO +#quarkus.log.category."io.quarkus.arc".min-level=DEBUG #quarkus.log.category."io.quarkus.arc".level=DEBUG quarkus.log.category."io.netty".level=INFO diff --git a/extensions/reactive-messaging-http/deployment/src/test/resources/websocket-sink-test-application.properties b/extensions/reactive-messaging-http/deployment/src/test/resources/websocket-sink-test-application.properties index 3dd3efa02b623..3659986c40464 100644 --- a/extensions/reactive-messaging-http/deployment/src/test/resources/websocket-sink-test-application.properties +++ b/extensions/reactive-messaging-http/deployment/src/test/resources/websocket-sink-test-application.properties @@ -5,4 +5,5 @@ mp.messaging.outgoing.ws-sink-with-serializer.connector=quarkus-websocket mp.messaging.outgoing.ws-sink-with-serializer.url=ws://localhost:${quarkus.http.test-port:8081}/ws-target-url mp.messaging.outgoing.ws-sink-with-serializer.serializer=io.quarkus.reactivemessaging.utils.ToUpperCaseSerializer +quarkus.log.category."io.quarkus.reactivemessaging".min-level=DEBUG quarkus.log.category."io.quarkus.reactivemessaging".level=DEBUG \ No newline at end of file diff --git a/integration-tests/bouncycastle-jsse/src/main/resources/application.properties b/integration-tests/bouncycastle-jsse/src/main/resources/application.properties index 6ccbc3eabd71e..4c0d0e07a2f40 100644 --- a/integration-tests/bouncycastle-jsse/src/main/resources/application.properties +++ b/integration-tests/bouncycastle-jsse/src/main/resources/application.properties @@ -7,6 +7,7 @@ quarkus.http.ssl.certificate.trust-store-password=password quarkus.http.ssl.client-auth=REQUIRED quarkus.native.additional-build-args=-H:IncludeResources=.*\\.jks +quarkus.log.category."org.bouncycastle.jsse".min-level=TRACE quarkus.log.category."org.bouncycastle.jsse".level=TRACE quarkus.log.file.enable=true quarkus.log.file.format=%C - %s%n diff --git a/integration-tests/flyway/src/main/resources/application.properties b/integration-tests/flyway/src/main/resources/application.properties index bd965e1ad1524..1745b11fe25c8 100644 --- a/integration-tests/flyway/src/main/resources/application.properties +++ b/integration-tests/flyway/src/main/resources/application.properties @@ -1,5 +1,7 @@ quarkus.log.console.level=DEBUG +quarkus.log.category."org.flywaydb.core".min-level=DEBUG quarkus.log.category."org.flywaydb.core".level=DEBUG +quarkus.log.category."io.quarkus.flyway".min-level=DEBUG quarkus.log.category."io.quarkus.flyway".level=DEBUG # Agroal config quarkus.datasource.db-kind=h2 diff --git a/integration-tests/hibernate-orm-panache/src/main/resources/nopaging.properties b/integration-tests/hibernate-orm-panache/src/main/resources/nopaging.properties index d15f255c940fa..b8014d9b7e9ca 100644 --- a/integration-tests/hibernate-orm-panache/src/main/resources/nopaging.properties +++ b/integration-tests/hibernate-orm-panache/src/main/resources/nopaging.properties @@ -5,4 +5,5 @@ quarkus.datasource.jdbc.max-size=8 quarkus.hibernate-orm.dialect=org.hibernate.dialect.H2Dialect quarkus.hibernate-orm.database.generation=drop-and-create +quarkus.log.category."org.hibernate.SQL".min-level=DEBUG quarkus.log.category."org.hibernate.SQL".level=DEBUG diff --git a/integration-tests/hibernate-reactive-panache/src/main/resources/nopaging.properties b/integration-tests/hibernate-reactive-panache/src/main/resources/nopaging.properties index 640a5e624b2fb..793dc408f0b25 100644 --- a/integration-tests/hibernate-reactive-panache/src/main/resources/nopaging.properties +++ b/integration-tests/hibernate-reactive-panache/src/main/resources/nopaging.properties @@ -5,6 +5,7 @@ quarkus.datasource.reactive.url=${postgres.reactive.url} quarkus.hibernate-orm.database.generation=drop-and-create +quarkus.log.category."org.hibernate.SQL".min-level=DEBUG quarkus.log.category."org.hibernate.SQL".level=DEBUG # this is required otherwise SQL logs are formatted on multiple lines and we can't match them quarkus.hibernate-orm.log.sql=false diff --git a/integration-tests/liquibase/src/main/resources/application.properties b/integration-tests/liquibase/src/main/resources/application.properties index 7ae39ec618134..c0a8c58abafea 100644 --- a/integration-tests/liquibase/src/main/resources/application.properties +++ b/integration-tests/liquibase/src/main/resources/application.properties @@ -13,5 +13,7 @@ quarkus.liquibase.database-change-log-table-name=TEST_LOG # Debug logging #quarkus.log.console.level=DEBUG +#quarkus.log.category."liquibase".min-level=DEBUG #quarkus.log.category."liquibase".level=DEBUG +#quarkus.log.category."io.quarkus.liquibase".min-level=DEBUG #quarkus.log.category."io.quarkus.liquibase".level=DEBUG diff --git a/integration-tests/logging-min-level-set/pom.xml b/integration-tests/logging-min-level-set/pom.xml new file mode 100644 index 0000000000000..98aad5422d961 --- /dev/null +++ b/integration-tests/logging-min-level-set/pom.xml @@ -0,0 +1,122 @@ + + + + quarkus-integration-tests-parent + io.quarkus + 999-SNAPSHOT + ../ + + 4.0.0 + + quarkus-integration-test-logging-min-level-set + Quarkus - Integration Tests - Logging when minimum level is set + + A integration test that verifies logging expectations when minimum logging level is set + + + + io.quarkus + quarkus-resteasy + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + io.quarkus + quarkus-resteasy-deployment + ${project.version} + pom + test + + + * + * + + + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + + + + + native + + + native + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + ${native.surefire.skip} + + + + maven-failsafe-plugin + + + default + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + + + + + + + + + + + + + + + + + + + + + + native + + + + + diff --git a/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/LoggingWitness.java b/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/LoggingWitness.java new file mode 100644 index 0000000000000..8e3bf78a6b138 --- /dev/null +++ b/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/LoggingWitness.java @@ -0,0 +1,44 @@ +package io.quarkus.it.logging.minlevel.set; + +import java.util.function.BiConsumer; + +import org.jboss.logging.Logger; + +public class LoggingWitness { + + public static boolean loggedError(String msg, Logger logger) { + return logged(msg, logger::error); + } + + public static boolean loggedWarn(String msg, Logger logger) { + return logged(msg, logger::warn); + } + + public static boolean loggedInfo(String msg, Logger logger) { + return logged(msg, logger::info); + } + + public static boolean loggedDebug(String msg, Logger logger) { + return logged(msg, logger::debug); + } + + public static boolean loggedTrace(String msg, Logger logger) { + return logged(msg, logger::trace); + } + + private static boolean logged(String msg, BiConsumer logFunction) { + final LoggingWitnessThrowable throwable = new LoggingWitnessThrowable(); + logFunction.accept(msg, throwable); + return throwable.logged; + } + + private static final class LoggingWitnessThrowable extends Throwable { + boolean logged; + + @Override + public StackTraceElement[] getStackTrace() { + logged = true; + return new StackTraceElement[] {}; + } + } +} diff --git a/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/above/LoggingMinLevelAbove.java b/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/above/LoggingMinLevelAbove.java new file mode 100644 index 0000000000000..7e8b33db36039 --- /dev/null +++ b/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/above/LoggingMinLevelAbove.java @@ -0,0 +1,31 @@ +package io.quarkus.it.logging.minlevel.set.above; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.jboss.logging.Logger; + +import io.quarkus.it.logging.minlevel.set.LoggingWitness; + +@Path("/log/above") +public class LoggingMinLevelAbove { + + static final Logger LOG = Logger.getLogger(LoggingMinLevelAbove.class); + + @GET + @Path("/not-info") + @Produces(MediaType.TEXT_PLAIN) + public boolean isNotInfo() { + return !LOG.isInfoEnabled() && !LoggingWitness.loggedInfo("should not print", LOG); + } + + @GET + @Path("/warn") + @Produces(MediaType.TEXT_PLAIN) + public boolean isWarn() { + return LoggingWitness.loggedWarn("warn message", LOG); + } + +} diff --git a/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/below/LoggingMinLevelBelow.java b/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/below/LoggingMinLevelBelow.java new file mode 100644 index 0000000000000..b2597bf482b9c --- /dev/null +++ b/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/below/LoggingMinLevelBelow.java @@ -0,0 +1,24 @@ +package io.quarkus.it.logging.minlevel.set.below; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.jboss.logging.Logger; + +import io.quarkus.it.logging.minlevel.set.LoggingWitness; + +@Path("/log/below") +public class LoggingMinLevelBelow { + + static final Logger LOG = Logger.getLogger(LoggingMinLevelBelow.class); + + @GET + @Path("/trace") + @Produces(MediaType.TEXT_PLAIN) + public boolean isTrace() { + return LOG.isTraceEnabled() && LoggingWitness.loggedTrace("trace-message", LOG); + } + +} diff --git a/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/bydefault/LoggingMinLevelByDefault.java b/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/bydefault/LoggingMinLevelByDefault.java new file mode 100644 index 0000000000000..e4055c810b6c2 --- /dev/null +++ b/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/bydefault/LoggingMinLevelByDefault.java @@ -0,0 +1,31 @@ +package io.quarkus.it.logging.minlevel.set.bydefault; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.jboss.logging.Logger; + +import io.quarkus.it.logging.minlevel.set.LoggingWitness; + +@Path("/log/bydefault") +public class LoggingMinLevelByDefault { + + static final Logger LOG = Logger.getLogger(LoggingMinLevelByDefault.class); + + @GET + @Path("/debug") + @Produces(MediaType.TEXT_PLAIN) + public boolean isDebug() { + return LOG.isDebugEnabled() && LoggingWitness.loggedDebug("debug message", LOG); + } + + @GET + @Path("/not-trace") + @Produces(MediaType.TEXT_PLAIN) + public boolean isNotTrace() { + return !LOG.isTraceEnabled() && !LoggingWitness.loggedTrace("should not print", LOG); + } + +} diff --git a/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/promote/LoggingMinLevelPromote.java b/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/promote/LoggingMinLevelPromote.java new file mode 100644 index 0000000000000..ddb251b3ee243 --- /dev/null +++ b/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/promote/LoggingMinLevelPromote.java @@ -0,0 +1,31 @@ +package io.quarkus.it.logging.minlevel.set.promote; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.jboss.logging.Logger; + +import io.quarkus.it.logging.minlevel.set.LoggingWitness; + +@Path("/log/promote") +public class LoggingMinLevelPromote { + + static final Logger LOG = Logger.getLogger(LoggingMinLevelPromote.class); + + @GET + @Path("/not-info") + @Produces(MediaType.TEXT_PLAIN) + public boolean isNotInfo() { + return !LOG.isInfoEnabled() && !LoggingWitness.loggedInfo("should not print", LOG); + } + + @GET + @Path("/error") + @Produces(MediaType.TEXT_PLAIN) + public boolean isError() { + return LoggingWitness.loggedError("error message", LOG); + } + +} diff --git a/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/promote/sub/LoggingMinLevelPromoteSub.java b/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/promote/sub/LoggingMinLevelPromoteSub.java new file mode 100644 index 0000000000000..6c29051a9c190 --- /dev/null +++ b/integration-tests/logging-min-level-set/src/main/java/io/quarkus/it/logging/minlevel/set/promote/sub/LoggingMinLevelPromoteSub.java @@ -0,0 +1,31 @@ +package io.quarkus.it.logging.minlevel.set.promote.sub; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.jboss.logging.Logger; + +import io.quarkus.it.logging.minlevel.set.LoggingWitness; + +@Path("/log/promote/sub") +public class LoggingMinLevelPromoteSub { + + static final Logger LOG = Logger.getLogger(LoggingMinLevelPromoteSub.class); + + @GET + @Path("/not-info") + @Produces(MediaType.TEXT_PLAIN) + public boolean isNotInfo() { + return !LOG.isInfoEnabled() && !LoggingWitness.loggedInfo("should not print", LOG); + } + + @GET + @Path("/error") + @Produces(MediaType.TEXT_PLAIN) + public boolean isError() { + return LoggingWitness.loggedError("error message", LOG); + } + +} diff --git a/integration-tests/logging-min-level-set/src/main/resources/application.properties b/integration-tests/logging-min-level-set/src/main/resources/application.properties new file mode 100644 index 0000000000000..b1c0f58b309b0 --- /dev/null +++ b/integration-tests/logging-min-level-set/src/main/resources/application.properties @@ -0,0 +1,4 @@ +quarkus.log.min-level=DEBUG +quarkus.log.category."io.quarkus.it.logging.minlevel.set.above".min-level=INFO +quarkus.log.category."io.quarkus.it.logging.minlevel.set.below".min-level=TRACE +quarkus.log.category."io.quarkus.it.logging.minlevel.set.promote".min-level=ERROR diff --git a/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/Asserts.java b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/Asserts.java new file mode 100644 index 0000000000000..0cf7cd42d1f50 --- /dev/null +++ b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/Asserts.java @@ -0,0 +1,46 @@ +package io.quarkus.it.logging.minlevel.set; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.function.BiConsumer; + +final class Asserts { + private static boolean assertLogged(boolean expected, String msg, BiConsumer logFunction) { + try (AssertThrowable t = new AssertThrowable(expected)) { + logFunction.accept(msg, t); + if (t.expected) + return t.executed; + else + return t.executed; + } + } + + private static final class AssertThrowable extends Throwable implements AutoCloseable { + + final boolean expected; + boolean executed; + + private AssertThrowable(boolean expected) { + this.expected = expected; + } + + @Override + public StackTraceElement[] getStackTrace() { + executed = true; + return new StackTraceElement[] {}; + } + + @Override + public void close() { + if (expected) { + assertTrue(executed, "Expected message to be printed but didn't get printed"); + } else { + assertFalse(executed, "Expected message not to be printed but got printed"); + } + } + } + + private Asserts() { + } +} diff --git a/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelAboveTest.java b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelAboveTest.java new file mode 100644 index 0000000000000..0494c00722ac0 --- /dev/null +++ b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelAboveTest.java @@ -0,0 +1,37 @@ +package io.quarkus.it.logging.minlevel.set; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +/** + * Test that verifies that min-level can go higher than default min-level, + * and this can be further tweaked at runtime to go to above to an even higher level. + */ +@QuarkusTest +@QuarkusTestResource(SetRuntimeLogLevels.class) +public class LoggingMinLevelAboveTest { + + @Test + public void testNotInfo() { + given() + .when().get("/log/above/not-info") + .then() + .statusCode(200) + .body(is("true")); + } + + @Test + public void testWarn() { + given() + .when().get("/log/above/warn") + .then() + .statusCode(200) + .body(is("true")); + } + +} diff --git a/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelBelowTest.java b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelBelowTest.java new file mode 100644 index 0000000000000..ced72852db9bf --- /dev/null +++ b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelBelowTest.java @@ -0,0 +1,27 @@ +package io.quarkus.it.logging.minlevel.set; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +/** + * Test verifies that a min-level override that goes below the default min-level is applied correctly. + */ +@QuarkusTest +@QuarkusTestResource(SetRuntimeLogLevels.class) +public class LoggingMinLevelBelowTest { + + @Test + public void testTrace() { + given() + .when().get("/log/below/trace") + .then() + .statusCode(200) + .body(is("true")); + } + +} diff --git a/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelByDefaultTest.java b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelByDefaultTest.java new file mode 100644 index 0000000000000..b4a79abf90a9d --- /dev/null +++ b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelByDefaultTest.java @@ -0,0 +1,40 @@ +package io.quarkus.it.logging.minlevel.set; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +/** + * Tests that verify that changes to the default min-level are applied correctly. + * + * If unset, the default is INFO, + * so this test verifies that when the default min-level is changed, + * say to DEBUG, the code works as expected. + */ +@QuarkusTest +@QuarkusTestResource(SetRuntimeLogLevels.class) +public class LoggingMinLevelByDefaultTest { + + @Test + public void testDebug() { + given() + .when().get("/log/bydefault/debug") + .then() + .statusCode(200) + .body(is("true")); + } + + @Test + public void testNotTrace() { + given() + .when().get("/log/bydefault/not-trace") + .then() + .statusCode(200) + .body(is("true")); + } + +} diff --git a/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelPromoteSubTest.java b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelPromoteSubTest.java new file mode 100644 index 0000000000000..9b7e978754b3e --- /dev/null +++ b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelPromoteSubTest.java @@ -0,0 +1,36 @@ +package io.quarkus.it.logging.minlevel.set; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +/** + * This test verifies that log levels are promoted to min-level when set below, even for subpackages. + */ +@QuarkusTest +@QuarkusTestResource(SetRuntimeLogLevels.class) +public class LoggingMinLevelPromoteSubTest { + + @Test + public void testNotInfo() { + given() + .when().get("/log/promote/sub/not-info") + .then() + .statusCode(200) + .body(is("true")); + } + + @Test + public void testError() { + given() + .when().get("/log/promote/sub/error") + .then() + .statusCode(200) + .body(is("true")); + } + +} diff --git a/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelPromoteTest.java b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelPromoteTest.java new file mode 100644 index 0000000000000..bd4e8208828fc --- /dev/null +++ b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/LoggingMinLevelPromoteTest.java @@ -0,0 +1,36 @@ +package io.quarkus.it.logging.minlevel.set; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +/** + * This test verifies that log levels are promoted to min-level when set below. + */ +@QuarkusTest +@QuarkusTestResource(SetRuntimeLogLevels.class) +public class LoggingMinLevelPromoteTest { + + @Test + public void testNotInfo() { + given() + .when().get("/log/promote/not-info") + .then() + .statusCode(200) + .body(is("true")); + } + + @Test + public void testError() { + given() + .when().get("/log/promote/error") + .then() + .statusCode(200) + .body(is("true")); + } + +} diff --git a/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelAboveIT.java b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelAboveIT.java new file mode 100644 index 0000000000000..cd30e3e9bd42f --- /dev/null +++ b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelAboveIT.java @@ -0,0 +1,9 @@ +package io.quarkus.it.logging.minlevel.set; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class NativeLoggingMinLevelAboveIT extends LoggingMinLevelAboveTest { + + // Execute the same tests but in native mode. +} diff --git a/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelBelowIT.java b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelBelowIT.java new file mode 100644 index 0000000000000..083f158875001 --- /dev/null +++ b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelBelowIT.java @@ -0,0 +1,9 @@ +package io.quarkus.it.logging.minlevel.set; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class NativeLoggingMinLevelBelowIT extends LoggingMinLevelBelowTest { + + // Execute the same tests but in native mode. +} diff --git a/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelByDefaultIT.java b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelByDefaultIT.java new file mode 100644 index 0000000000000..cf004df48327d --- /dev/null +++ b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelByDefaultIT.java @@ -0,0 +1,9 @@ +package io.quarkus.it.logging.minlevel.set; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class NativeLoggingMinLevelByDefaultIT extends LoggingMinLevelByDefaultTest { + + // Execute the same tests but in native mode. +} diff --git a/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelPromoteIT.java b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelPromoteIT.java new file mode 100644 index 0000000000000..d8d931a0f17c4 --- /dev/null +++ b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelPromoteIT.java @@ -0,0 +1,9 @@ +package io.quarkus.it.logging.minlevel.set; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class NativeLoggingMinLevelPromoteIT extends LoggingMinLevelPromoteTest { + + // Execute the same tests but in native mode. +} diff --git a/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelPromoteSubIT.java b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelPromoteSubIT.java new file mode 100644 index 0000000000000..900692bb54e4f --- /dev/null +++ b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/NativeLoggingMinLevelPromoteSubIT.java @@ -0,0 +1,9 @@ +package io.quarkus.it.logging.minlevel.set; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class NativeLoggingMinLevelPromoteSubIT extends LoggingMinLevelPromoteSubTest { + + // Execute the same tests but in native mode. +} diff --git a/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/SetRuntimeLogLevels.java b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/SetRuntimeLogLevels.java new file mode 100644 index 0000000000000..c6ec0817b3220 --- /dev/null +++ b/integration-tests/logging-min-level-set/src/test/java/io/quarkus/it/logging/minlevel/set/SetRuntimeLogLevels.java @@ -0,0 +1,24 @@ +package io.quarkus.it.logging.minlevel.set; + +import java.util.HashMap; +import java.util.Map; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +public final class SetRuntimeLogLevels implements QuarkusTestResourceLifecycleManager { + + @Override + public Map start() { + final Map systemProperties = new HashMap<>(); + systemProperties.put("quarkus.log.category.\"io.quarkus.it.logging.minlevel.set.bydefault\".level", "DEBUG"); + systemProperties.put("quarkus.log.category.\"io.quarkus.it.logging.minlevel.set.above\".level", "WARN"); + systemProperties.put("quarkus.log.category.\"io.quarkus.it.logging.minlevel.set.below\".level", "TRACE"); + systemProperties.put("quarkus.log.category.\"io.quarkus.it.logging.minlevel.set.promote\".level", "INFO"); + return systemProperties; + } + + @Override + public void stop() { + } + +} diff --git a/integration-tests/logging-min-level-unset/pom.xml b/integration-tests/logging-min-level-unset/pom.xml new file mode 100644 index 0000000000000..0c493fb805636 --- /dev/null +++ b/integration-tests/logging-min-level-unset/pom.xml @@ -0,0 +1,122 @@ + + + + quarkus-integration-tests-parent + io.quarkus + 999-SNAPSHOT + ../ + + 4.0.0 + + quarkus-integration-test-logging-min-level-unset + Quarkus - Integration Tests - Logging when minimum level is unset + + A integration test that verifies logging expectations when minimum logging level is unset + + + + io.quarkus + quarkus-resteasy + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + io.quarkus + quarkus-resteasy-deployment + ${project.version} + pom + test + + + * + * + + + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + + + + + native + + + native + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + ${native.surefire.skip} + + + + maven-failsafe-plugin + + + default + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + + + + + + + + + + + + + + + + + + + + + + native + + + + + diff --git a/integration-tests/logging-min-level-unset/src/main/java/io/quarkus/it/logging/minlevel/unset/LoggingWitness.java b/integration-tests/logging-min-level-unset/src/main/java/io/quarkus/it/logging/minlevel/unset/LoggingWitness.java new file mode 100644 index 0000000000000..9b5bcc9acb027 --- /dev/null +++ b/integration-tests/logging-min-level-unset/src/main/java/io/quarkus/it/logging/minlevel/unset/LoggingWitness.java @@ -0,0 +1,39 @@ +package io.quarkus.it.logging.minlevel.unset; + +import java.util.function.BiConsumer; + +import org.jboss.logging.Logger; + +public class LoggingWitness { + public static boolean loggedInfo(String msg, Logger logger) { + return logged(msg, logger::info); + } + + public static boolean notLoggedInfo(String msg, Logger logger) { + return !loggedInfo(msg, logger); + } + + public static boolean loggedWarn(String msg, Logger logger) { + return logged(msg, logger::warn); + } + + public static boolean notLoggedTrace(String msg, Logger logger) { + return !logged(msg, logger::trace); + } + + private static boolean logged(String msg, BiConsumer logFunction) { + final LoggingWitnessThrowable throwable = new LoggingWitnessThrowable(); + logFunction.accept(msg, throwable); + return throwable.logged; + } + + private static final class LoggingWitnessThrowable extends Throwable { + boolean logged; + + @Override + public StackTraceElement[] getStackTrace() { + logged = true; + return new StackTraceElement[] {}; + } + } +} diff --git a/integration-tests/logging-min-level-unset/src/main/java/io/quarkus/it/logging/minlevel/unset/above/LoggingMinLevelAbove.java b/integration-tests/logging-min-level-unset/src/main/java/io/quarkus/it/logging/minlevel/unset/above/LoggingMinLevelAbove.java new file mode 100644 index 0000000000000..f2982156233e5 --- /dev/null +++ b/integration-tests/logging-min-level-unset/src/main/java/io/quarkus/it/logging/minlevel/unset/above/LoggingMinLevelAbove.java @@ -0,0 +1,38 @@ +package io.quarkus.it.logging.minlevel.unset.above; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.jboss.logging.Logger; + +import io.quarkus.it.logging.minlevel.unset.LoggingWitness; + +@Path("/log/above") +public class LoggingMinLevelAbove { + + static final Logger LOG = Logger.getLogger(LoggingMinLevelAbove.class); + + @GET + @Path("/not-info") + @Produces(MediaType.TEXT_PLAIN) + public boolean isNotInfo() { + return !LOG.isInfoEnabled() && LoggingWitness.notLoggedInfo("should not print", LOG); + } + + @GET + @Path("/warn") + @Produces(MediaType.TEXT_PLAIN) + public boolean isWarn() { + return LoggingWitness.loggedWarn("warn message", LOG); + } + + @GET + @Path("/not-trace") + @Produces(MediaType.TEXT_PLAIN) + public boolean isNotTrace() { + return !LOG.isTraceEnabled() && LoggingWitness.notLoggedTrace("should not print", LOG); + } + +} diff --git a/integration-tests/logging-min-level-unset/src/main/java/io/quarkus/it/logging/minlevel/unset/bydefault/LoggingMinLevelByDefault.java b/integration-tests/logging-min-level-unset/src/main/java/io/quarkus/it/logging/minlevel/unset/bydefault/LoggingMinLevelByDefault.java new file mode 100644 index 0000000000000..0a6c206c6f067 --- /dev/null +++ b/integration-tests/logging-min-level-unset/src/main/java/io/quarkus/it/logging/minlevel/unset/bydefault/LoggingMinLevelByDefault.java @@ -0,0 +1,38 @@ +package io.quarkus.it.logging.minlevel.unset.bydefault; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.jboss.logging.Logger; + +import io.quarkus.it.logging.minlevel.unset.LoggingWitness; + +@Path("/log/bydefault") +public class LoggingMinLevelByDefault { + + static final Logger LOG = Logger.getLogger(LoggingMinLevelByDefault.class); + + @GET + @Path("/info") + @Produces(MediaType.TEXT_PLAIN) + public boolean isInfo() { + return LOG.isInfoEnabled() && LoggingWitness.loggedInfo("info message", LOG); + } + + @GET + @Path("/warn") + @Produces(MediaType.TEXT_PLAIN) + public boolean isWarn() { + return LoggingWitness.loggedWarn("warn message", LOG); + } + + @GET + @Path("/not-trace") + @Produces(MediaType.TEXT_PLAIN) + public boolean isNotTrace() { + return !LOG.isTraceEnabled() && LoggingWitness.notLoggedTrace("should not print", LOG); + } + +} diff --git a/integration-tests/logging-min-level-unset/src/main/java/io/quarkus/it/logging/minlevel/unset/promote/LoggingMinLevelPromote.java b/integration-tests/logging-min-level-unset/src/main/java/io/quarkus/it/logging/minlevel/unset/promote/LoggingMinLevelPromote.java new file mode 100644 index 0000000000000..4bc35d2dc7cd9 --- /dev/null +++ b/integration-tests/logging-min-level-unset/src/main/java/io/quarkus/it/logging/minlevel/unset/promote/LoggingMinLevelPromote.java @@ -0,0 +1,37 @@ +package io.quarkus.it.logging.minlevel.unset.promote; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.jboss.logging.Logger; + +import io.quarkus.it.logging.minlevel.unset.LoggingWitness; + +@Path("/log/promote") +public class LoggingMinLevelPromote { + static final Logger LOG = Logger.getLogger(LoggingMinLevelPromote.class); + + @GET + @Path("/info") + @Produces(MediaType.TEXT_PLAIN) + public boolean isInfo() { + return LOG.isInfoEnabled() && LoggingWitness.loggedInfo("info message", LOG); + } + + @GET + @Path("/warn") + @Produces(MediaType.TEXT_PLAIN) + public boolean isWarn() { + return LoggingWitness.loggedWarn("warn message", LOG); + } + + @GET + @Path("/not-trace") + @Produces(MediaType.TEXT_PLAIN) + public boolean isNotTrace() { + return !LOG.isTraceEnabled() && LoggingWitness.notLoggedTrace("should not print", LOG); + } + +} diff --git a/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/Asserts.java b/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/Asserts.java new file mode 100644 index 0000000000000..baf4464457af2 --- /dev/null +++ b/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/Asserts.java @@ -0,0 +1,46 @@ +package io.quarkus.it.logging.minlevel.unset; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.function.BiConsumer; + +final class Asserts { + private static boolean assertLogged(boolean expected, String msg, BiConsumer logFunction) { + try (AssertThrowable t = new AssertThrowable(expected)) { + logFunction.accept(msg, t); + if (t.expected) + return t.executed; + else + return t.executed; + } + } + + private static final class AssertThrowable extends Throwable implements AutoCloseable { + + final boolean expected; + boolean executed; + + private AssertThrowable(boolean expected) { + this.expected = expected; + } + + @Override + public StackTraceElement[] getStackTrace() { + executed = true; + return new StackTraceElement[] {}; + } + + @Override + public void close() { + if (expected) { + assertTrue(executed, "Expected message to be printed but didn't get printed"); + } else { + assertFalse(executed, "Expected message not to be printed but got printed"); + } + } + } + + private Asserts() { + } +} diff --git a/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/LoggingMinLevelAboveTest.java b/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/LoggingMinLevelAboveTest.java new file mode 100644 index 0000000000000..0e8ef8cb344d3 --- /dev/null +++ b/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/LoggingMinLevelAboveTest.java @@ -0,0 +1,46 @@ +package io.quarkus.it.logging.minlevel.unset; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +/** + * This test verifies that a runtime log level can go above the default min-level, + * and only messages at same runtime log level or above are shown. + */ +@QuarkusTest +@QuarkusTestResource(SetRuntimeLogLevels.class) +public class LoggingMinLevelAboveTest { + + @Test + public void testNotInfo() { + given() + .when().get("/log/above/not-info") + .then() + .statusCode(200) + .body(is("true")); + } + + @Test + public void testWarn() { + given() + .when().get("/log/above/warn") + .then() + .statusCode(200) + .body(is("true")); + } + + @Test + public void testNotTrace() { + given() + .when().get("/log/above/not-trace") + .then() + .statusCode(200) + .body(is("true")); + } + +} diff --git a/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/LoggingMinLevelByDefaultTest.java b/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/LoggingMinLevelByDefaultTest.java new file mode 100644 index 0000000000000..423be57b74ef0 --- /dev/null +++ b/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/LoggingMinLevelByDefaultTest.java @@ -0,0 +1,46 @@ +package io.quarkus.it.logging.minlevel.unset; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +/** + * Tests that logging works as expected when min-level is default, + * and there's no applicable runtime log level override. + */ +@QuarkusTest +@QuarkusTestResource(SetRuntimeLogLevels.class) +public class LoggingMinLevelByDefaultTest { + + @Test + public void testInfo() { + given() + .when().get("/log/bydefault/info") + .then() + .statusCode(200) + .body(is("true")); + } + + @Test + public void testWarn() { + given() + .when().get("/log/bydefault/warn") + .then() + .statusCode(200) + .body(is("true")); + } + + @Test + public void testNotTrace() { + given() + .when().get("/log/bydefault/not-trace") + .then() + .statusCode(200) + .body(is("true")); + } + +} diff --git a/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/LoggingMinLevelPromoteTest.java b/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/LoggingMinLevelPromoteTest.java new file mode 100644 index 0000000000000..59823e68a3f10 --- /dev/null +++ b/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/LoggingMinLevelPromoteTest.java @@ -0,0 +1,49 @@ +package io.quarkus.it.logging.minlevel.unset; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +/** + * This test verifies that log levels are promoted to min-level when set below the default min-level. + * + * So given the default min-level is INFO, + * so if log level is set to TRACE, + * it will be automatically promoted to INFO. + */ +@QuarkusTest +@QuarkusTestResource(SetRuntimeLogLevels.class) +public class LoggingMinLevelPromoteTest { + + @Test + public void testInfo() { + given() + .when().get("/log/promote/info") + .then() + .statusCode(200) + .body(is("true")); + } + + @Test + public void testWarn() { + given() + .when().get("/log/promote/warn") + .then() + .statusCode(200) + .body(is("true")); + } + + @Test + public void testNotTrace() { + given() + .when().get("/log/promote/not-trace") + .then() + .statusCode(200) + .body(is("true")); + } + +} diff --git a/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/NativeLoggingMinLevelByDefaultIT.java b/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/NativeLoggingMinLevelByDefaultIT.java new file mode 100644 index 0000000000000..40746cab36c33 --- /dev/null +++ b/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/NativeLoggingMinLevelByDefaultIT.java @@ -0,0 +1,9 @@ +package io.quarkus.it.logging.minlevel.unset; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class NativeLoggingMinLevelByDefaultIT extends LoggingMinLevelByDefaultTest { + + // Execute the same tests but in native mode. +} diff --git a/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/NativeLoggingMinLevelPromoteIT.java b/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/NativeLoggingMinLevelPromoteIT.java new file mode 100644 index 0000000000000..d046b36e4b955 --- /dev/null +++ b/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/NativeLoggingMinLevelPromoteIT.java @@ -0,0 +1,9 @@ +package io.quarkus.it.logging.minlevel.unset; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class NativeLoggingMinLevelPromoteIT extends LoggingMinLevelPromoteTest { + + // Execute the same tests but in native mode. +} diff --git a/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/NativeLoggingWarnResourceIT.java b/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/NativeLoggingWarnResourceIT.java new file mode 100644 index 0000000000000..9d0000a87df99 --- /dev/null +++ b/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/NativeLoggingWarnResourceIT.java @@ -0,0 +1,9 @@ +package io.quarkus.it.logging.minlevel.unset; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class NativeLoggingWarnResourceIT extends LoggingMinLevelAboveTest { + + // Execute the same tests but in native mode. +} diff --git a/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/SetRuntimeLogLevels.java b/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/SetRuntimeLogLevels.java new file mode 100644 index 0000000000000..777bb6e0db2fc --- /dev/null +++ b/integration-tests/logging-min-level-unset/src/test/java/io/quarkus/it/logging/minlevel/unset/SetRuntimeLogLevels.java @@ -0,0 +1,22 @@ +package io.quarkus.it.logging.minlevel.unset; + +import java.util.HashMap; +import java.util.Map; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +public final class SetRuntimeLogLevels implements QuarkusTestResourceLifecycleManager { + + @Override + public Map start() { + final Map systemProperties = new HashMap<>(); + systemProperties.put("quarkus.log.category.\"io.quarkus.it.logging.minlevel.unset.above\".level", "WARN"); + systemProperties.put("quarkus.log.category.\"io.quarkus.it.logging.minlevel.unset.promote\".level", "TRACE"); + return systemProperties; + } + + @Override + public void stop() { + } + +} diff --git a/integration-tests/micrometer-mp-metrics/src/main/resources/application.properties b/integration-tests/micrometer-mp-metrics/src/main/resources/application.properties index 3582d0e834d6d..113395279ab34 100644 --- a/integration-tests/micrometer-mp-metrics/src/main/resources/application.properties +++ b/integration-tests/micrometer-mp-metrics/src/main/resources/application.properties @@ -1,3 +1,4 @@ +#quarkus.log.category."io.quarkus.micrometer".min-level=DEBUG #quarkus.log.category."io.quarkus.micrometer".level=DEBUG quarkus.log.category."io.quarkus.micrometer.runtime.binder.vertx".level=INFO diff --git a/integration-tests/mongodb-panache-kotlin/src/main/resources/application.properties b/integration-tests/mongodb-panache-kotlin/src/main/resources/application.properties index bde8c47826706..7b06849469836 100644 --- a/integration-tests/mongodb-panache-kotlin/src/main/resources/application.properties +++ b/integration-tests/mongodb-panache-kotlin/src/main/resources/application.properties @@ -6,4 +6,5 @@ quarkus.mongodb.database=books quarkus.mongodb.cl2.connection-string=mongodb://localhost:27018 quarkus.mongodb.cl2.write-concern.journal=false +#quarkus.log.category."io.quarkus.mongodb.panache.runtime".min-level=DEBUG #quarkus.log.category."io.quarkus.mongodb.panache.runtime".level=DEBUG diff --git a/integration-tests/mongodb-panache/src/main/resources/application.properties b/integration-tests/mongodb-panache/src/main/resources/application.properties index 63810ad397a2a..630df929f4fdf 100644 --- a/integration-tests/mongodb-panache/src/main/resources/application.properties +++ b/integration-tests/mongodb-panache/src/main/resources/application.properties @@ -6,5 +6,6 @@ quarkus.mongodb.database=books quarkus.mongodb.cl2.connection-string=mongodb://localhost:27018 quarkus.mongodb.cl2.write-concern.journal=false +#quarkus.log.category."io.quarkus.mongodb.panache.runtime".min-level=DEBUG #quarkus.log.category."io.quarkus.mongodb.panache.runtime".level=DEBUG quarkus.mongodb.metrics.enabled=true diff --git a/integration-tests/oidc-code-flow/src/main/resources/application.properties b/integration-tests/oidc-code-flow/src/main/resources/application.properties index 17ad02c6d3d06..84ab0d5dadee6 100644 --- a/integration-tests/oidc-code-flow/src/main/resources/application.properties +++ b/integration-tests/oidc-code-flow/src/main/resources/application.properties @@ -146,6 +146,9 @@ quarkus.http.auth.proactive=false quarkus.http.proxy.enable-forwarded-prefix=true quarkus.http.proxy.allow-forwarded=true +quarkus.log.category."io.quarkus.oidc.runtime.CodeAuthenticationMechanism".min-level=TRACE quarkus.log.category."io.quarkus.oidc.runtime.CodeAuthenticationMechanism".level=TRACE +quarkus.log.category."io.vertx.ext.auth.oauth2.impl.OAuth2UserImpl".min-level=TRACE quarkus.log.category."io.vertx.ext.auth.oauth2.impl.OAuth2UserImpl".level=TRACE +#quarkus.log.category."org.apache.http".min-level=TRACE #quarkus.log.category."org.apache.http".level=TRACE diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index d5030cc8678a4..2c42e58202ac5 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -151,6 +151,8 @@ jaxp mailer native-config-profile + logging-min-level-unset + logging-min-level-set grpc-tls diff --git a/integration-tests/reactive-pg-client/src/test/resources/application-tl.properties b/integration-tests/reactive-pg-client/src/test/resources/application-tl.properties index 54e42ebfd6ceb..9658ab14d2754 100644 --- a/integration-tests/reactive-pg-client/src/test/resources/application-tl.properties +++ b/integration-tests/reactive-pg-client/src/test/resources/application-tl.properties @@ -3,4 +3,5 @@ quarkus.datasource.username=hibernate_orm_test quarkus.datasource.password=hibernate_orm_test quarkus.datasource.reactive.url=${reactive-postgres.url} quarkus.datasource.reactive.thread-local=true +quarkus.log.category."io.quarkus.reactive.datasource".min-level=DEBUG quarkus.log.category."io.quarkus.reactive.datasource".level=DEBUG diff --git a/integration-tests/vault-agroal/src/test/resources/application-vault-datasource.properties b/integration-tests/vault-agroal/src/test/resources/application-vault-datasource.properties index 4e4a1f9adf0a3..ee8c68367772c 100644 --- a/integration-tests/vault-agroal/src/test/resources/application-vault-datasource.properties +++ b/integration-tests/vault-agroal/src/test/resources/application-vault-datasource.properties @@ -22,7 +22,9 @@ quarkus.datasource.dynamicDS.credentials-provider=dynamic-ds quarkus.datasource.dynamicDS.credentials-provider-name=vault-credentials-provider quarkus.datasource.dynamicDS.jdbc.url=jdbc:postgresql://localhost:6543/mydb +quarkus.log.category."io.quarkus.vault".min-level=DEBUG quarkus.log.category."io.quarkus.vault".level=DEBUG +quarkus.log.category."io.quarkus.vault.runtime.vault".min-level=DEBUG quarkus.log.category."io.quarkus.vault.runtime.vault".level=DEBUG #quarkus.log.min-level=DEBUG diff --git a/integration-tests/vault-app/src/main/resources/application.properties b/integration-tests/vault-app/src/main/resources/application.properties index 1952a375ee81e..f63ecb4504ad1 100644 --- a/integration-tests/vault-app/src/main/resources/application.properties +++ b/integration-tests/vault-app/src/main/resources/application.properties @@ -14,6 +14,7 @@ quarkus.vault.secret-config-kv-path=config quarkus.vault.health.enabled=true quarkus.vault.health.stand-by-ok=true +quarkus.log.category."io.quarkus.vault".min-level=DEBUG quarkus.log.category."io.quarkus.vault".level=DEBUG quarkus.datasource.db-kind=postgresql diff --git a/integration-tests/vault/src/test/resources/application-vault-approle-wrap.properties b/integration-tests/vault/src/test/resources/application-vault-approle-wrap.properties index 64c741d6838d3..1be87cc54992f 100644 --- a/integration-tests/vault/src/test/resources/application-vault-approle-wrap.properties +++ b/integration-tests/vault/src/test/resources/application-vault-approle-wrap.properties @@ -10,6 +10,7 @@ quarkus.vault.tls.ca-cert=src/test/resources/vault-tls.crt quarkus.vault.log-confidentiality-level=low quarkus.vault.renew-grace-period=10 +quarkus.log.category."io.quarkus.vault".min-level=DEBUG quarkus.log.category."io.quarkus.vault".level=DEBUG # CI can sometimes be slow, there is no need to fail a test if Vault doesn't respond in 1 second which is the default diff --git a/integration-tests/vault/src/test/resources/application-vault-approle.properties b/integration-tests/vault/src/test/resources/application-vault-approle.properties index c9783f48037fa..a14f92d50765f 100644 --- a/integration-tests/vault/src/test/resources/application-vault-approle.properties +++ b/integration-tests/vault/src/test/resources/application-vault-approle.properties @@ -10,6 +10,7 @@ quarkus.vault.tls.ca-cert=src/test/resources/vault-tls.crt quarkus.vault.log-confidentiality-level=low quarkus.vault.renew-grace-period=10 +quarkus.log.category."io.quarkus.vault".min-level=DEBUG quarkus.log.category."io.quarkus.vault".level=DEBUG # CI can sometimes be slow, there is no need to fail a test if Vault doesn't respond in 1 second which is the default diff --git a/integration-tests/vault/src/test/resources/application-vault-client-token-wrap.properties b/integration-tests/vault/src/test/resources/application-vault-client-token-wrap.properties index c56c3cbb4bb71..5e2ac886172ad 100644 --- a/integration-tests/vault/src/test/resources/application-vault-client-token-wrap.properties +++ b/integration-tests/vault/src/test/resources/application-vault-client-token-wrap.properties @@ -9,6 +9,7 @@ quarkus.vault.tls.ca-cert=src/test/resources/vault-tls.crt quarkus.vault.log-confidentiality-level=low quarkus.vault.renew-grace-period=10 +quarkus.log.category."io.quarkus.vault".min-level=DEBUG quarkus.log.category."io.quarkus.vault".level=DEBUG # CI can sometimes be slow, there is no need to fail a test if Vault doesn't respond in 1 second which is the default diff --git a/integration-tests/vault/src/test/resources/application-vault-kubernetes.properties b/integration-tests/vault/src/test/resources/application-vault-kubernetes.properties index 7c9eee8ffd6fb..cf48cb8d3ed97 100644 --- a/integration-tests/vault/src/test/resources/application-vault-kubernetes.properties +++ b/integration-tests/vault/src/test/resources/application-vault-kubernetes.properties @@ -10,6 +10,7 @@ quarkus.vault.tls.ca-cert=src/test/resources/vault-tls.crt quarkus.vault.log-confidentiality-level=low quarkus.vault.renew-grace-period=10 +quarkus.log.category."io.quarkus.vault".min-level=DEBUG quarkus.log.category."io.quarkus.vault".level=DEBUG # CI can sometimes be slow, there is no need to fail a test if Vault doesn't respond in 1 second which is the default diff --git a/integration-tests/vault/src/test/resources/application-vault-userpass-kvv1-wrap.properties b/integration-tests/vault/src/test/resources/application-vault-userpass-kvv1-wrap.properties index 5cc1ee0b32241..21c363883bd6c 100644 --- a/integration-tests/vault/src/test/resources/application-vault-userpass-kvv1-wrap.properties +++ b/integration-tests/vault/src/test/resources/application-vault-userpass-kvv1-wrap.properties @@ -13,6 +13,7 @@ quarkus.vault.tls.ca-cert=src/test/resources/vault-tls.crt quarkus.vault.log-confidentiality-level=low quarkus.vault.renew-grace-period=10 +quarkus.log.category."io.quarkus.vault".min-level=DEBUG quarkus.log.category."io.quarkus.vault".level=DEBUG # CI can sometimes be slow, there is no need to fail a test if Vault doesn't respond in 1 second which is the default diff --git a/integration-tests/vault/src/test/resources/application-vault-userpass-kvv2-wrap.properties b/integration-tests/vault/src/test/resources/application-vault-userpass-kvv2-wrap.properties index 99d5b2d0881c4..e913eb3d9b924 100644 --- a/integration-tests/vault/src/test/resources/application-vault-userpass-kvv2-wrap.properties +++ b/integration-tests/vault/src/test/resources/application-vault-userpass-kvv2-wrap.properties @@ -10,6 +10,7 @@ quarkus.vault.tls.ca-cert=src/test/resources/vault-tls.crt quarkus.vault.log-confidentiality-level=low quarkus.vault.renew-grace-period=10 +quarkus.log.category."io.quarkus.vault".min-level=DEBUG quarkus.log.category."io.quarkus.vault".level=DEBUG # CI can sometimes be slow, there is no need to fail a test if Vault doesn't respond in 1 second which is the default diff --git a/integration-tests/vault/src/test/resources/application-vault.properties b/integration-tests/vault/src/test/resources/application-vault.properties index 9f8e053cb0be7..1fb45d7aa9707 100644 --- a/integration-tests/vault/src/test/resources/application-vault.properties +++ b/integration-tests/vault/src/test/resources/application-vault.properties @@ -17,6 +17,7 @@ quarkus.vault.tls.ca-cert=src/test/resources/vault-tls.crt quarkus.vault.log-confidentiality-level=low quarkus.vault.renew-grace-period=10 +quarkus.log.category."io.quarkus.vault".min-level=DEBUG quarkus.log.category."io.quarkus.vault".level=DEBUG # CI can sometimes be slow, there is no need to fail a test if Vault doesn't respond in 1 second which is the default