diff --git a/docs/guides/server/logging.adoc b/docs/guides/server/logging.adoc index 3c554b9d63..9cdadb53ba 100644 --- a/docs/guides/server/logging.adoc +++ b/docs/guides/server/logging.adoc @@ -1,6 +1,7 @@ <#import "/templates/guide.adoc" as tmpl> <#import "/templates/kc.adoc" as kc> <#import "/templates/links.adoc" as links> +<#import "/templates/profile.adoc" as profile> <@tmpl.guide title="Configuring logging" @@ -12,7 +13,9 @@ Keycloak uses the JBoss Logging framework. The following is a high-level overvie * root ** console (_default_) ** file +<@profile.ifCommunity> ** GELF + == Logging configuration Logging is done on a per-category basis in Keycloak. You can configure logging for the root log level or for more specific categories such as `org.hibernate` or `org.keycloak`. This {section} describes how to configure logging. @@ -69,7 +72,14 @@ To enable log handlers, enter the following command: <@kc.start parameters="--log=\",\""/> -The available handlers are `console`, `file` and `gelf`. The more specific handler configuration mentioned below will only take effect when the handler is added to this comma-separated list. +The available handlers are +<@profile.ifCommunity> +`console`, `file` and `gelf`. + +<@profile.ifProduct> +`console` and `file`. + +The more specific handler configuration mentioned below will only take effect when the handler is added to this comma-separated list. == Console log handler The console log handler is enabled by default, providing unstructured log messages for the console. @@ -178,6 +188,8 @@ To configure a different logging format for the file log handler, enter the foll See <> for more information and a table of the available pattern configuration. +<@profile.ifCommunity> + == Centralized logging using GELF Keycloak can send logs to a centralized log management system such as the following: @@ -497,4 +509,6 @@ Currently, the Keycloak configuration does not support partly dynamic configurat To add user-defined fields, you can provide these fields through a quarkus.properties file. See <@links.server id="configuration"/> and the _Using raw Quarkus properties_ section. + + diff --git a/quarkus/config-api/src/main/java/org/keycloak/config/LoggingOptions.java b/quarkus/config-api/src/main/java/org/keycloak/config/LoggingOptions.java index 7e7b7d79d0..be32b0ad9d 100644 --- a/quarkus/config-api/src/main/java/org/keycloak/config/LoggingOptions.java +++ b/quarkus/config-api/src/main/java/org/keycloak/config/LoggingOptions.java @@ -1,8 +1,10 @@ package org.keycloak.config; import java.io.File; +import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.function.Predicate; public class LoggingOptions { @@ -11,6 +13,7 @@ public class LoggingOptions { public static final Output DEFAULT_CONSOLE_OUTPUT = Output.DEFAULT; public static final String DEFAULT_LOG_FILENAME = "keycloak.log"; public static final String DEFAULT_LOG_PATH = "data" + File.separator + "log" + File.separator + DEFAULT_LOG_FILENAME; + public static final Boolean GELF_ACTIVATED = isGelfActivated(); public enum Handler { console, @@ -18,9 +21,19 @@ public class LoggingOptions { gelf } + public static List getAvailableHandlerNames() { + final Predicate checkGelf = (handler) -> GELF_ACTIVATED || !handler.equals(Handler.gelf); + + return Arrays.stream(Handler.values()) + .filter(checkGelf) + .map(Handler::name) + .toList(); + } + public static final Option LOG = new OptionBuilder("log", List.class, Handler.class) .category(OptionCategory.LOGGING) .description("Enable one or more log handlers in a comma-separated list.") + .expectedValues(() -> getAvailableHandlerNames()) .defaultValue(DEFAULT_LOG_HANDLER) .build(); @@ -168,4 +181,13 @@ public class LoggingOptions { .description("Include source code location.") .defaultValue(Boolean.TRUE) .build(); + + private static boolean isGelfActivated() { + try { + Thread.currentThread().getContextClassLoader().loadClass("io.quarkus.logging.gelf.GelfConfig"); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } } diff --git a/quarkus/deployment/pom.xml b/quarkus/deployment/pom.xml index 743dc72ba6..4ba1b0a01c 100644 --- a/quarkus/deployment/pom.xml +++ b/quarkus/deployment/pom.xml @@ -149,10 +149,6 @@ io.quarkus quarkus-logging-json-deployment - - io.quarkus - quarkus-logging-gelf-deployment - io.quarkus @@ -197,4 +193,21 @@ + + + includeGelf + + + !product + + + + + io.quarkus + quarkus-logging-gelf-deployment + + + + + diff --git a/quarkus/runtime/pom.xml b/quarkus/runtime/pom.xml index 7f8a37b3f8..67e37d2b76 100644 --- a/quarkus/runtime/pom.xml +++ b/quarkus/runtime/pom.xml @@ -109,10 +109,6 @@ io.quarkus quarkus-logging-json - - io.quarkus - quarkus-logging-gelf - @@ -736,4 +732,21 @@ + + + + includeGelf + + + !product + + + + + io.quarkus + quarkus-logging-gelf + + + + diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java index 3d5b170b2c..1484fe9820 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java @@ -1,18 +1,18 @@ package org.keycloak.quarkus.runtime.configuration.mappers; import static java.util.Optional.of; +import static org.keycloak.config.LoggingOptions.GELF_ACTIVATED; import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; import static org.keycloak.quarkus.runtime.integration.QuarkusPlatform.addInitializationException; import java.io.File; -import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Optional; import java.util.function.BiFunction; import java.util.logging.Level; -import java.util.stream.Collectors; +import org.apache.commons.lang3.ArrayUtils; import org.jboss.logmanager.LogContext; import org.keycloak.config.LoggingOptions; import org.keycloak.quarkus.runtime.Messages; @@ -24,7 +24,7 @@ public final class LoggingPropertyMappers { private LoggingPropertyMappers(){} public static PropertyMapper[] getMappers() { - return new PropertyMapper[] { + PropertyMapper[] defaultMappers = new PropertyMapper[]{ fromOption(LoggingOptions.LOG) .paramLabel("") .build(), @@ -68,7 +68,14 @@ public final class LoggingPropertyMappers { .to("quarkus.log.level") .transformer(LoggingPropertyMappers::resolveLogLevel) .paramLabel("category:level") - .build(), + .build() + }; + + return GELF_ACTIVATED ? ArrayUtils.addAll(defaultMappers, getGelfMappers()) : defaultMappers; + } + + public static PropertyMapper[] getGelfMappers() { + return new PropertyMapper[]{ fromOption(LoggingOptions.LOG_GELF_ENABLED) .mapFrom("log") .to("quarkus.log.handler.gelf.enabled") @@ -126,7 +133,7 @@ public final class LoggingPropertyMappers { } String[] logHandlerValues = handlers.split(","); - List availableLogHandlers = Arrays.stream(LoggingOptions.Handler.values()).map(Enum::name).collect(Collectors.toList()); + final List availableLogHandlers = LoggingOptions.getAvailableHandlerNames(); if (!availableLogHandlers.containsAll(List.of(logHandlerValues))) { addInitializationException(Messages.notRecognizedValueInList("log", handlers, String.join(",", availableLogHandlers))); diff --git a/quarkus/tests/integration/pom.xml b/quarkus/tests/integration/pom.xml index 6bf7ab6c59..3ee0ab2f25 100644 --- a/quarkus/tests/integration/pom.xml +++ b/quarkus/tests/integration/pom.xml @@ -163,6 +163,26 @@ + + includeGelf + + + !product + + + + + + maven-surefire-plugin + + + true + + + + + + diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/GelfRemovedTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/GelfRemovedTest.java new file mode 100644 index 0000000000..ec6d8c9bae --- /dev/null +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/GelfRemovedTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.it.cli.dist; + +import io.quarkus.test.junit.main.Launch; +import io.quarkus.test.junit.main.LaunchResult; +import org.junit.jupiter.api.Test; +import org.keycloak.it.junit5.extension.CLIResult; +import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.RawDistOnly; +import org.keycloak.quarkus.runtime.cli.command.StartDev; + +@DistributionTest +@RawDistOnly(reason = "Verifying the help message output doesn't need long spin-up of docker dist tests.") +public class GelfRemovedTest { + + public static final String INCLUDE_GELF_PROPERTY = "includeGelf"; + + @Test + @Launch({StartDev.NAME, "--help-all"}) + void checkGelfRemoved(LaunchResult result) { + CLIResult cliResult = (CLIResult) result; + if (Boolean.getBoolean(INCLUDE_GELF_PROPERTY)) { + cliResult.assertMessage("gelf"); + } else { + cliResult.assertNoMessage("gelf"); + } + } +} diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HelpCommandDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HelpCommandDistTest.java index 7a59337209..5400af170e 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HelpCommandDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HelpCommandDistTest.java @@ -18,12 +18,15 @@ package org.keycloak.it.cli.dist; import static org.junit.Assert.assertEquals; +import static org.keycloak.it.cli.dist.GelfRemovedTest.INCLUDE_GELF_PROPERTY; import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG; import java.util.List; import org.approvaltests.Approvals; import org.approvaltests.namer.NamedEnvironment; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.OS; import org.keycloak.it.approvaltests.KcNamerFactory; @@ -44,6 +47,11 @@ import io.quarkus.test.junit.main.LaunchResult; @RawDistOnly(reason = "Verifying the help message output doesn't need long spin-up of docker dist tests.") public class HelpCommandDistTest { + @BeforeAll + public static void assumeGelfEnabled() { + Assumptions.assumeTrue(Boolean.getBoolean(INCLUDE_GELF_PROPERTY), "Assume GELF support is given in order to simplify these test cases"); + } + @Test @Launch({}) void testDefaultToHelp(LaunchResult result) { @@ -52,7 +60,7 @@ public class HelpCommandDistTest { } @Test - @Launch({ "--help" }) + @Launch({"--help"}) void testHelp(LaunchResult result) { CLIResult cliResult = (CLIResult) result; assertHelp(cliResult);