From 1f2b564f4617adbff0dc39562ed1327a3e82b3c6 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 12 Sep 2022 13:13:03 +0300 Subject: [PATCH] Allow setting a custom logging filter on logging handlers Resolves: #25981 --- .../logging/LoggingResourceProcessor.java | 72 ++++++++++++- .../io/quarkus/logging/LoggingFilter.java | 23 ++++ .../runtime/logging/ConsoleConfig.java | 6 ++ .../logging/DiscoveredLogComponents.java | 21 ++++ .../quarkus/runtime/logging/FileConfig.java | 6 ++ .../runtime/logging/LoggingSetupRecorder.java | 102 ++++++++++++++---- .../quarkus/runtime/logging/SyslogConfig.java | 6 ++ docs/src/main/asciidoc/logging.adoc | 34 ++++++ .../DevServicesElasticsearchProcessor.java | 3 +- 9 files changed, 249 insertions(+), 24 deletions(-) create mode 100644 core/runtime/src/main/java/io/quarkus/logging/LoggingFilter.java create mode 100644 core/runtime/src/main/java/io/quarkus/runtime/logging/DiscoveredLogComponents.java 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 418af27f5bf1f4..20ec59091f8156 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 @@ -1,7 +1,9 @@ package io.quarkus.deployment.logging; +import java.lang.reflect.Modifier; import java.nio.file.Files; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; @@ -13,6 +15,7 @@ import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Supplier; +import java.util.logging.Filter; import java.util.logging.Formatter; import java.util.logging.Handler; import java.util.logging.Level; @@ -29,9 +32,13 @@ import org.aesh.command.completer.OptionCompleter; import org.aesh.command.invocation.CommandInvocation; import org.aesh.command.option.Option; +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationTarget; +import org.jboss.jandex.ClassInfo; import org.jboss.jandex.CompositeIndex; import org.jboss.jandex.DotName; import org.jboss.jandex.IndexView; +import org.jboss.jandex.MethodInfo; import org.jboss.logging.Logger; import org.jboss.logmanager.EmbeddedConfigurator; import org.jboss.logmanager.LogManager; @@ -49,6 +56,7 @@ import io.quarkus.deployment.annotations.Produce; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.ConsoleCommandBuildItem; import io.quarkus.deployment.builditem.ConsoleFormatterBannerBuildItem; import io.quarkus.deployment.builditem.CuratedApplicationShutdownBuildItem; @@ -79,6 +87,7 @@ import io.quarkus.deployment.pkg.builditem.BuildSystemTargetBuildItem; import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild; import io.quarkus.deployment.recording.RecorderContext; +import io.quarkus.deployment.util.JandexUtil; import io.quarkus.dev.console.CurrentAppExceptionHighlighter; import io.quarkus.dev.spi.DevModeType; import io.quarkus.gizmo.AnnotationCreator; @@ -91,11 +100,13 @@ import io.quarkus.gizmo.MethodCreator; import io.quarkus.gizmo.MethodDescriptor; import io.quarkus.gizmo.ResultHandle; +import io.quarkus.logging.LoggingFilter; import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.configuration.ConfigInstantiator; import io.quarkus.runtime.console.ConsoleRuntimeConfig; import io.quarkus.runtime.logging.CategoryBuildTimeConfig; import io.quarkus.runtime.logging.CleanupFilterConfig; +import io.quarkus.runtime.logging.DiscoveredLogComponents; import io.quarkus.runtime.logging.InheritableLevel; import io.quarkus.runtime.logging.LogBuildTimeConfig; import io.quarkus.runtime.logging.LogCleanupFilterElement; @@ -114,6 +125,12 @@ public final class LoggingResourceProcessor { "isMinLevelEnabled", boolean.class, int.class, String.class); + private static final DotName LOGGING_FILTER = DotName.createSimple(LoggingFilter.class.getName()); + private static final DotName FILTER = DotName.createSimple(Filter.class.getName()); + private static final String ILLEGAL_LOGGING_FILTER_USE_MESSAGE = "'" + LoggingFilter.class.getName() + + "' can only be used on classes that implement '" + + Filter.class.getName() + "' and that are marked as final."; + @BuildStep void setupLogFilters(BuildProducer filters) { filters.produce(new LogCleanupFilterBuildItem("org.jboss.threads", "JBoss Threads version")); @@ -204,6 +221,7 @@ void miscSetup( @Record(ExecutionTime.RUNTIME_INIT) LoggingSetupBuildItem setupLoggingRuntimeInit(RecorderContext context, LoggingSetupRecorder recorder, LogConfig log, LogBuildTimeConfig buildLog, + CombinedIndexBuildItem combinedIndexBuildItem, LogCategoryMinLevelDefaultsBuildItem categoryMinLevelDefaults, Optional logStreamHandlerBuildItem, List handlerBuildItems, @@ -246,7 +264,8 @@ LoggingSetupBuildItem setupLoggingRuntimeInit(RecorderContext context, LoggingSe context.registerSubstitution(InheritableLevel.ActualLevel.class, String.class, InheritableLevel.Substitution.class); context.registerSubstitution(InheritableLevel.Inherited.class, String.class, InheritableLevel.Substitution.class); shutdownListenerBuildItemBuildProducer.produce(new ShutdownListenerBuildItem( - recorder.initializeLogging(log, buildLog, categoryMinLevelDefaults.content, alwaysEnableLogStream, + recorder.initializeLogging(log, buildLog, discoverLogComponents(combinedIndexBuildItem.getIndex()), + categoryMinLevelDefaults.content, alwaysEnableLogStream, devUiLogHandler, handlers, namedHandlers, consoleFormatItems.stream().map(LogConsoleFormatBuildItem::getFormatterValue) .collect(Collectors.toList()), @@ -276,6 +295,57 @@ public void run() { return new LoggingSetupBuildItem(); } + private DiscoveredLogComponents discoverLogComponents(IndexView index) { + Collection loggingFilterInstances = index.getAnnotations(LOGGING_FILTER); + DiscoveredLogComponents result = new DiscoveredLogComponents(); + + Map filtersMap = new HashMap<>(); + for (AnnotationInstance instance : loggingFilterInstances) { + AnnotationTarget target = instance.target(); + if (target.kind() != AnnotationTarget.Kind.CLASS) { + throw new IllegalStateException("Unimplemented mode of use of '" + LoggingFilter.class.getName() + "'"); + } + ClassInfo classInfo = target.asClass(); + if (!Modifier.isFinal(classInfo.flags())) { + throw new RuntimeException( + ILLEGAL_LOGGING_FILTER_USE_MESSAGE + " Offending class is '" + classInfo.name() + "'"); + } + boolean isFilterImpl = false; + ClassInfo currentClassInfo = classInfo; + while ((currentClassInfo != null) && (!JandexUtil.DOTNAME_OBJECT.equals(currentClassInfo.name()))) { + boolean hasFilterInterface = false; + List ifaces = currentClassInfo.interfaceNames(); + for (DotName iface : ifaces) { + if (FILTER.equals(iface)) { + hasFilterInterface = true; + break; + } + } + if (hasFilterInterface) { + isFilterImpl = true; + break; + } + currentClassInfo = index.getClassByName(currentClassInfo.superName()); + } + if (!isFilterImpl) { + throw new RuntimeException( + ILLEGAL_LOGGING_FILTER_USE_MESSAGE + " Offending class is '" + classInfo.name() + "'"); + } + + MethodInfo ctor = classInfo.method(""); + if ((ctor == null) || (ctor.typeParameters().size() > 0)) { + throw new RuntimeException("Classes annotated with '" + LoggingFilter.class.getName() + + "' must have a no-args constructor. Offending class is '" + classInfo.name() + "'"); + } + filtersMap.put(instance.value("name").asString(), classInfo.name().toString()); + } + if (!filtersMap.isEmpty()) { + result.setNameToFilterClass(filtersMap); + } + + return result; + } + @BuildStep(onlyIfNot = IsNormal.class) @Produce(TestSetupBuildItem.class) @Produce(LogConsoleFormatBuildItem.class) diff --git a/core/runtime/src/main/java/io/quarkus/logging/LoggingFilter.java b/core/runtime/src/main/java/io/quarkus/logging/LoggingFilter.java new file mode 100644 index 00000000000000..bf2adb61f2b4f0 --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/logging/LoggingFilter.java @@ -0,0 +1,23 @@ +package io.quarkus.logging; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Makes the filter class known to Quarkus by the specified name. + * The filter can then be configured for a handler (like the logging handler using {@code quarkus.log.console.filter}). + * + * This class must ONLY be placed on implementations of {@link java.util.logging.Filter} that are marked as {@code final}. + */ + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface LoggingFilter { + + /** + * Name with which the filter is referred to in configuration + */ + String name(); +} diff --git a/core/runtime/src/main/java/io/quarkus/runtime/logging/ConsoleConfig.java b/core/runtime/src/main/java/io/quarkus/runtime/logging/ConsoleConfig.java index e888f45159bc7c..ecf2bb5b260f03 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/logging/ConsoleConfig.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/logging/ConsoleConfig.java @@ -55,6 +55,12 @@ public class ConsoleConfig { @ConfigItem(defaultValue = "0") int darken; + /** + * The names of the filter to link to the console handler. + */ + @ConfigItem + Optional filter; + /** * Console async logging config */ diff --git a/core/runtime/src/main/java/io/quarkus/runtime/logging/DiscoveredLogComponents.java b/core/runtime/src/main/java/io/quarkus/runtime/logging/DiscoveredLogComponents.java new file mode 100644 index 00000000000000..a7be611de09d85 --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/logging/DiscoveredLogComponents.java @@ -0,0 +1,21 @@ +package io.quarkus.runtime.logging; + +import java.util.Collections; +import java.util.Map; + +public class DiscoveredLogComponents { + + private Map nameToFilterClass = Collections.emptyMap(); + + public Map getNameToFilterClass() { + return nameToFilterClass; + } + + public void setNameToFilterClass(Map nameToFilterClass) { + this.nameToFilterClass = nameToFilterClass; + } + + public static DiscoveredLogComponents ofEmpty() { + return new DiscoveredLogComponents(); + } +} diff --git a/core/runtime/src/main/java/io/quarkus/runtime/logging/FileConfig.java b/core/runtime/src/main/java/io/quarkus/runtime/logging/FileConfig.java index 5e9b9239daa78e..fea3cf55d62cc3 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/logging/FileConfig.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/logging/FileConfig.java @@ -40,6 +40,12 @@ public class FileConfig { @ConfigItem(defaultValue = DEFAULT_LOG_FILE_NAME) File path; + /** + * The names of the filter to link to the file handler. + */ + @ConfigItem + Optional filter; + /** * File async logging config */ 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 e3bf40d330e4f7..a35c9105e3470c 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 @@ -18,6 +18,7 @@ import java.util.function.Function; import java.util.function.Supplier; import java.util.logging.ErrorManager; +import java.util.logging.Filter; import java.util.logging.Formatter; import java.util.logging.Handler; import java.util.logging.Level; @@ -29,6 +30,7 @@ import org.jboss.logmanager.LogContext; import org.jboss.logmanager.Logger; import org.jboss.logmanager.errormanager.OnlyOnceErrorManager; +import org.jboss.logmanager.filters.AllFilter; import org.jboss.logmanager.formatters.ColorPatternFormatter; import org.jboss.logmanager.formatters.PatternFormatter; import org.jboss.logmanager.handlers.AsyncHandler; @@ -74,6 +76,7 @@ public static void handleFailedStart(RuntimeValue>> ba ConsoleRuntimeConfig consoleRuntimeConfig = new ConsoleRuntimeConfig(); ConfigInstantiator.handleObject(consoleRuntimeConfig); new LoggingSetupRecorder(new RuntimeValue<>(consoleRuntimeConfig)).initializeLogging(config, buildConfig, + DiscoveredLogComponents.ofEmpty(), Collections.emptyMap(), false, null, Collections.emptyList(), @@ -83,6 +86,7 @@ public static void handleFailedStart(RuntimeValue>> ba } public ShutdownListener initializeLogging(LogConfig config, LogBuildTimeConfig buildConfig, + DiscoveredLogComponents discoveredLogComponents, final Map categoryDefaultMinLevels, final boolean enableWebStream, final RuntimeValue> devUiConsoleHandler, @@ -126,12 +130,14 @@ public void accept(String loggerName, CleanupFilterConfig config) { handler.setFilter(cleanupFiler); } + Map namedFilters = createNamedFilters(discoveredLogComponents); + final ArrayList handlers = new ArrayList<>( 3 + additionalHandlers.size() + (config.handlers.isPresent() ? config.handlers.get().size() : 0)); if (config.console.enable) { - final Handler consoleHandler = configureConsoleHandler(config.console, consoleRuntimeConfig.getValue(), - errorManager, cleanupFiler, + final Handler consoleHandler = configureConsoleHandler(false, config.console, consoleRuntimeConfig.getValue(), + errorManager, cleanupFiler, namedFilters, possibleConsoleFormatters, possibleBannerSupplier, launchMode); errorManager = consoleHandler.getErrorManager(); handlers.add(consoleHandler); @@ -157,11 +163,12 @@ public void close() throws SecurityException { if (config.file.enable) { handlers.add( - configureFileHandler(config.file, errorManager, cleanupFiler, possibleFileFormatters)); + configureFileHandler(false, config.file, errorManager, cleanupFiler, namedFilters, possibleFileFormatters)); } if (config.syslog.enable) { - final Handler syslogHandler = configureSyslogHandler(config.syslog, errorManager, cleanupFiler); + final Handler syslogHandler = configureSyslogHandler(false, config.syslog, errorManager, cleanupFiler, + namedFilters); if (syslogHandler != null) { handlers.add(syslogHandler); } @@ -184,8 +191,8 @@ public void close() throws SecurityException { } Map namedHandlers = shouldCreateNamedHandlers(config, additionalNamedHandlers) - ? createNamedHandlers(config, consoleRuntimeConfig.getValue(), additionalNamedHandlers, - possibleConsoleFormatters, possibleFileFormatters, errorManager, cleanupFiler, launchMode, + ? createNamedHandlers(false, config, consoleRuntimeConfig.getValue(), additionalNamedHandlers, + possibleConsoleFormatters, possibleFileFormatters, errorManager, cleanupFiler, namedFilters, launchMode, shutdownNotifier) : Collections.emptyMap(); if (!categories.isEmpty()) { @@ -238,6 +245,27 @@ public void accept(String categoryName, CategoryConfig config) { return shutdownNotifier; } + private static Map createNamedFilters(DiscoveredLogComponents discoveredLogComponents) { + if (discoveredLogComponents.getNameToFilterClass().isEmpty()) { + return Collections.emptyMap(); + } + + Map nameToFilter = new HashMap<>(); + discoveredLogComponents.getNameToFilterClass().forEach(new BiConsumer<>() { + @Override + public void accept(String name, String className) { + try { + nameToFilter.put(name, + (Filter) Class.forName(className, true, Thread.currentThread().getContextClassLoader()) + .getConstructor().newInstance()); + } catch (Exception e) { + throw new RuntimeException("Unable to create instance of Logging Filter '" + className + "'"); + } + } + }); + return nameToFilter; + } + /** * WARNING: this method is part of the recorder but is actually called statically at build time. * You may not push RuntimeValue's to it. @@ -266,16 +294,16 @@ public static void initializeBuildTimeLogging(LogConfig config, LogBuildTimeConf final ArrayList handlers = new ArrayList<>(3); if (config.console.enable) { - final Handler consoleHandler = configureConsoleHandler(config.console, consoleConfig, errorManager, + final Handler consoleHandler = configureConsoleHandler(true, config.console, consoleConfig, errorManager, logCleanupFilter, - Collections.emptyList(), new RuntimeValue<>(Optional.empty()), launchMode); + Collections.emptyMap(), Collections.emptyList(), new RuntimeValue<>(Optional.empty()), launchMode); errorManager = consoleHandler.getErrorManager(); handlers.add(consoleHandler); } - Map namedHandlers = createNamedHandlers(config, consoleConfig, Collections.emptyList(), + Map namedHandlers = createNamedHandlers(true, config, consoleConfig, Collections.emptyList(), Collections.emptyList(), - Collections.emptyList(), errorManager, logCleanupFilter, launchMode, dummy); + Collections.emptyList(), errorManager, logCleanupFilter, Collections.emptyMap(), launchMode, dummy); for (Map.Entry entry : categories.entrySet()) { final String categoryName = entry.getKey(); @@ -352,12 +380,15 @@ public static InheritableLevel getLogLevelNoInheritance(String categoryName, return inheritableLevel; } - private static Map createNamedHandlers(LogConfig config, ConsoleRuntimeConfig consoleRuntimeConfig, + private static Map createNamedHandlers(boolean isBuildTimeSetup, LogConfig config, + ConsoleRuntimeConfig consoleRuntimeConfig, List>> additionalNamedHandlers, List>> possibleConsoleFormatters, List>> possibleFileFormatters, ErrorManager errorManager, - LogCleanupFilter cleanupFilter, LaunchMode launchMode, + LogCleanupFilter cleanupFilter, + Map namedFilters, + LaunchMode launchMode, ShutdownNotifier shutdownHandler) { Map namedHandlers = new HashMap<>(); for (Entry consoleConfigEntry : config.consoleHandlers.entrySet()) { @@ -365,9 +396,10 @@ private static Map createNamedHandlers(LogConfig config, Consol if (!namedConsoleConfig.enable) { continue; } - final Handler consoleHandler = configureConsoleHandler(namedConsoleConfig, consoleRuntimeConfig, errorManager, + final Handler consoleHandler = configureConsoleHandler(isBuildTimeSetup, namedConsoleConfig, consoleRuntimeConfig, + errorManager, cleanupFilter, - possibleConsoleFormatters, null, launchMode); + namedFilters, possibleConsoleFormatters, null, launchMode); addToNamedHandlers(namedHandlers, consoleHandler, consoleConfigEntry.getKey()); } for (Entry fileConfigEntry : config.fileHandlers.entrySet()) { @@ -375,7 +407,8 @@ private static Map createNamedHandlers(LogConfig config, Consol if (!namedFileConfig.enable) { continue; } - final Handler fileHandler = configureFileHandler(namedFileConfig, errorManager, cleanupFilter, + final Handler fileHandler = configureFileHandler(isBuildTimeSetup, namedFileConfig, errorManager, cleanupFilter, + namedFilters, possibleFileFormatters); addToNamedHandlers(namedHandlers, fileHandler, fileConfigEntry.getKey()); } @@ -384,7 +417,8 @@ private static Map createNamedHandlers(LogConfig config, Consol if (!namedSyslogConfig.enable) { continue; } - final Handler syslogHandler = configureSyslogHandler(namedSyslogConfig, errorManager, cleanupFilter); + final Handler syslogHandler = configureSyslogHandler(isBuildTimeSetup, namedSyslogConfig, errorManager, + cleanupFilter, namedFilters); if (syslogHandler != null) { addToNamedHandlers(namedHandlers, syslogHandler, sysLogConfigEntry.getKey()); } @@ -471,9 +505,11 @@ public void initializeLoggingForImageBuild() { } } - private static Handler configureConsoleHandler(final ConsoleConfig config, ConsoleRuntimeConfig consoleRuntimeConfig, + private static Handler configureConsoleHandler(final boolean isBuildTimeSetup, final ConsoleConfig config, + ConsoleRuntimeConfig consoleRuntimeConfig, final ErrorManager defaultErrorManager, final LogCleanupFilter cleanupFilter, + final Map namedFilters, final List>> possibleFormatters, final RuntimeValue>> possibleBannerSupplier, LaunchMode launchMode) { Formatter formatter = null; @@ -516,7 +552,7 @@ private static Handler configureConsoleHandler(final ConsoleConfig config, Conso config.stderr ? ConsoleHandler.Target.SYSTEM_ERR : ConsoleHandler.Target.SYSTEM_OUT, formatter); consoleHandler.setLevel(config.level); consoleHandler.setErrorManager(defaultErrorManager); - consoleHandler.setFilter(cleanupFilter); + applyFilter(consoleHandler, isBuildTimeSetup, defaultErrorManager, cleanupFilter, config.filter, namedFilters); Handler handler = config.async.enable ? createAsyncHandler(config.async, config.level, consoleHandler) : consoleHandler; @@ -553,8 +589,10 @@ public void close() throws SecurityException { return handler; } - private static Handler configureFileHandler(final FileConfig config, final ErrorManager errorManager, - final LogCleanupFilter cleanupFilter, final List>> possibleFileFormatters) { + private static Handler configureFileHandler(final boolean isBuildTimeSetup, final FileConfig config, + final ErrorManager errorManager, + final LogCleanupFilter cleanupFilter, Map namedFilters, + final List>> possibleFileFormatters) { FileHandler handler; FileConfig.RotationConfig rotationConfig = config.rotation; if (rotationConfig.fileSuffix.isPresent()) { @@ -596,6 +634,7 @@ private static Handler configureFileHandler(final FileConfig config, final Error handler.setErrorManager(errorManager); handler.setLevel(config.level); handler.setFilter(cleanupFilter); + applyFilter(handler, isBuildTimeSetup, errorManager, cleanupFilter, config.filter, namedFilters); if (formatterWarning) { handler.getErrorManager().error("Multiple file formatters were activated", null, ErrorManager.GENERIC_FAILURE); @@ -607,9 +646,27 @@ private static Handler configureFileHandler(final FileConfig config, final Error return handler; } - private static Handler configureSyslogHandler(final SyslogConfig config, + private static void applyFilter(Handler handler, boolean isBuildTimeSetup, ErrorManager errorManager, + LogCleanupFilter cleanupFilter, + Optional filterName, + Map namedFilters) { + if (filterName.isEmpty() || isBuildTimeSetup) { + handler.setFilter(cleanupFilter); + } else { + String name = filterName.get(); + Filter filter = namedFilters.get(name); + if (filter == null) { + errorManager.error("Unable to find named filter '" + name + "'", null, ErrorManager.GENERIC_FAILURE); + handler.setFilter(cleanupFilter); + } else { + handler.setFilter(new AllFilter(List.of(cleanupFilter, filter))); + } + } + } + + private static Handler configureSyslogHandler(final boolean isBuildTimeSetup, final SyslogConfig config, final ErrorManager errorManager, - final LogCleanupFilter logCleanupFilter) { + final LogCleanupFilter logCleanupFilter, final Map namedFilters) { try { final SyslogHandler handler = new SyslogHandler(config.endpoint.getHostString(), config.endpoint.getPort()); handler.setAppName(config.appName.orElse(getProcessName())); @@ -625,6 +682,7 @@ private static Handler configureSyslogHandler(final SyslogConfig config, handler.setFormatter(formatter); handler.setErrorManager(errorManager); handler.setFilter(logCleanupFilter); + applyFilter(handler, isBuildTimeSetup, errorManager, logCleanupFilter, config.filter, namedFilters); if (config.async.enable) { return createAsyncHandler(config.async, config.level, handler); } diff --git a/core/runtime/src/main/java/io/quarkus/runtime/logging/SyslogConfig.java b/core/runtime/src/main/java/io/quarkus/runtime/logging/SyslogConfig.java index 38a3e49871c988..012540aac6f883 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/logging/SyslogConfig.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/logging/SyslogConfig.java @@ -89,6 +89,12 @@ public class SyslogConfig { @ConfigItem(defaultValue = "ALL") Level level; + /** + * The names of the filter to link to the file handler. + */ + @ConfigItem + Optional filter; + /** * Syslog async logging config */ diff --git a/docs/src/main/asciidoc/logging.adoc b/docs/src/main/asciidoc/logging.adoc index 60e877a48f30d5..dc3cfb288f8b7d 100644 --- a/docs/src/main/asciidoc/logging.adoc +++ b/docs/src/main/asciidoc/logging.adoc @@ -315,6 +315,40 @@ The console log handler is enabled by default. It outputs all log events to the For details of its configuration options, see link:#quarkus-log-logging-log-config_quarkus.log.console-console-logging[the Console Logging configuration reference]. +[TIP] +.Logging filters +==== +Log handlers (like the console log handler) can have a link:https://docs.oracle.com/en/java/javase/11/docs/api/java.logging/java/util/logging/Filter.html[filter] associated with them, whose +purpose is to determine whether a record should actually be logged or not. + +These filters are registered by placing the `@io.quarkus.logging.LoggingFilter` annotation on a (`final`) class that implements `java.util.logging.Filter` and setting the `name` property. + +Finally, the filter is attached using the `filter` configuration property of the appropriate handler. + +Let's say for example that we wanted to filter out logging records that contained the word `test` from the console logs. +We could write a filter like so: + +[source,java] +---- +import java.util.logging.Filter; + +@LoggingFilter(name = "my-filter") +public final class TestFilter implements Filter { + @Override + public boolean isLoggable(LogRecord record) { + return !record.getMessage().contains("test"); + } +} +---- + +And we would register this filter to the console handler like so: + +[source, properties] +---- +quarkus.log.console.filter=my-filter +---- +==== + === File log handler The file log handler is disabled by default. It outputs all log events to a file on the application's host. diff --git a/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/DevServicesElasticsearchProcessor.java b/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/DevServicesElasticsearchProcessor.java index 44803e7d84ca22..54e8d778c8b86e 100644 --- a/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/DevServicesElasticsearchProcessor.java +++ b/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/DevServicesElasticsearchProcessor.java @@ -49,6 +49,7 @@ public class DevServicesElasticsearchProcessor { private static final ContainerLocator elasticsearchContainerLocator = new ContainerLocator(DEV_SERVICE_LABEL, ELASTICSEARCH_PORT); + private static final String DEFAULT_IMAGE = "docker.elastic.co/elasticsearch/elasticsearch"; static volatile DevServicesResultBuildItem.RunningDevService devService; static volatile ElasticsearchDevServicesBuildTimeConfig cfg; @@ -197,7 +198,7 @@ private DevServicesResultBuildItem.RunningDevService startElasticsearch( // Starting the server final Supplier defaultElasticsearchSupplier = () -> { ElasticsearchContainer container = new ElasticsearchContainer( - DockerImageName.parse(config.imageName)); + DockerImageName.parse(config.imageName).asCompatibleSubstituteFor(DEFAULT_IMAGE)); ConfigureUtil.configureSharedNetwork(container, "elasticsearch"); if (config.serviceName != null) { container.withLabel(DEV_SERVICE_LABEL, config.serviceName);