diff --git a/common/src/main/java/org/keycloak/common/Profile.java b/common/src/main/java/org/keycloak/common/Profile.java index c5cced49a7a..1280e469e4e 100755 --- a/common/src/main/java/org/keycloak/common/Profile.java +++ b/common/src/main/java/org/keycloak/common/Profile.java @@ -135,6 +135,8 @@ public class Profile { ROLLING_UPDATES_V1("Rolling Updates", Type.DEFAULT, 1), ROLLING_UPDATES_V2("Rolling Updates for patch releases", Type.PREVIEW, 2), + LOG_MDC("Mapped Diagnostic Context (MDC) information in logs", Type.PREVIEW), + /** * @see Deprecate for removal the Instagram social broker. */ diff --git a/docs/documentation/release_notes/topics/26_4_0.adoc b/docs/documentation/release_notes/topics/26_4_0.adoc index da03935762c..6c326519200 100644 --- a/docs/documentation/release_notes/topics/26_4_0.adoc +++ b/docs/documentation/release_notes/topics/26_4_0.adoc @@ -5,4 +5,11 @@ Read on to learn more about each new feature, and https://www.keycloak.org/docs/ = Option to force management interface to use HTTP. -There's a new option `http-management-scheme` that may be set to `http` to force the management interface to use HTTP rather than inheriting the HTTPS settings of the main interface. +There's a new option `http-management-scheme` that may be set to `http` to force the management interface to use HTTP rather than inheriting the HTTPS settings of the main interface. + += Additional context information for log messages (preview) + +You can now add context information to each log message like the realm or the client that initiated the request. +This helps you to track down a warning or error message in the log to a specific caller or environment + +For more details on this opt-in feature, see the https://www.keycloak.org/server/logging[Logging guide]. diff --git a/docs/guides/server/logging.adoc b/docs/guides/server/logging.adoc index 57c396974d7..7762f448be6 100644 --- a/docs/guides/server/logging.adoc +++ b/docs/guides/server/logging.adoc @@ -68,6 +68,32 @@ This example sets the following log levels: * The hibernate log level in general is set to debug. * To keep SQL abstract syntax trees from creating verbose log output, the specific subcategory `org.hibernate.hql.internal.ast` is set to info. As a result, the SQL abstract syntax trees are omitted instead of appearing at the `debug` level. +=== Adding context for log messages + +:tech_feature_name: Log messages with Mapped Diagnostic Context (MDC) +:tech_feature_id: log-mdc + +[NOTE] +==== +{tech_feature_name} is +*Preview* +and is not fully supported. This feature is disabled by default. +==== + +You can enable additional context information for each log line like the current realm and client that is executing the request. + +Use the option `log-mdc-enable` to enable it. + +.Example configuration +<@kc.start parameters="--features=log-mdc --log-mdc-enable=true"/> + +.Example output +---- +2025-06-20 14:13:01,772 {kc.clientId=security-admin-console, kc.realm=master} INFO ... +---- + +Specify which keys to be added by setting the configuration option `log-mdc-keys`. + ==== Configuring levels as individual options When configuring category-specific log levels, you can also set the log levels as individual `log-level-` options instead of using the `log-level` option for that. This is useful when you want to set the log levels for selected categories without overwriting the previously set `log-level` option. 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 c8082b7f61e..e1a573d6025 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 @@ -20,10 +20,9 @@ public class LoggingOptions { public static final String DEFAULT_LOG_PATH = "data" + File.separator + "log" + File.separator + DEFAULT_LOG_FILENAME; // Log format + tracing - private static final Function DEFAULT_LOG_FORMAT_FUNC = (additionalFields) -> + public static final Function DEFAULT_LOG_FORMAT_FUNC = (additionalFields) -> "%d{yyyy-MM-dd HH:mm:ss,SSS} " + additionalFields + "%-5p [%c] (%t) %s%e%n"; public static final String DEFAULT_LOG_FORMAT = DEFAULT_LOG_FORMAT_FUNC.apply(""); - public static final String DEFAULT_LOG_TRACING_FORMAT = DEFAULT_LOG_FORMAT_FUNC.apply("traceId=%X{traceId}, parentId=%X{parentId}, spanId=%X{spanId}, sampled=%X{sampled} "); public enum Handler { console, @@ -125,6 +124,12 @@ public class LoggingOptions { .defaultValue(true) .build(); + public static final Option LOG_CONSOLE_INCLUDE_MDC = new OptionBuilder<>("log-console-include-mdc", Boolean.class) + .category(OptionCategory.LOGGING) + .description(format("Include mdc information in the console log. If the '%s' option is specified, this option has no effect.", LOG_CONSOLE_FORMAT.getKey())) + .defaultValue(true) + .build(); + public static final Option LOG_CONSOLE_COLOR = new OptionBuilder<>("log-console-color", Boolean.class) .category(OptionCategory.LOGGING) .description("Enable or disable colors when logging to console.") @@ -188,6 +193,12 @@ public class LoggingOptions { .defaultValue(true) .build(); + public static final Option LOG_FILE_INCLUDE_MDC = new OptionBuilder<>("log-file-include-mdc", Boolean.class) + .category(OptionCategory.LOGGING) + .description(format("Include MDC information in the file log. If the '%s' option is specified, this option has no effect.", LOG_FILE_FORMAT.getKey())) + .defaultValue(true) + .build(); + public static final Option LOG_FILE_OUTPUT = new OptionBuilder<>("log-file-output", Output.class) .category(OptionCategory.LOGGING) .defaultValue(DEFAULT_CONSOLE_OUTPUT) @@ -273,6 +284,12 @@ public class LoggingOptions { .defaultValue(true) .build(); + public static final Option LOG_SYSLOG_INCLUDE_MDC = new OptionBuilder<>("log-syslog-include-mdc", Boolean.class) + .category(OptionCategory.LOGGING) + .description(format("Include MDC information in the Syslog. If the '%s' option is specified, this option has no effect.", LOG_SYSLOG_FORMAT.getKey())) + .defaultValue(true) + .build(); + public static final Option LOG_SYSLOG_OUTPUT = new OptionBuilder<>("log-syslog-output", Output.class) .category(OptionCategory.LOGGING) .defaultValue(DEFAULT_SYSLOG_OUTPUT) @@ -301,4 +318,19 @@ public class LoggingOptions { .defaultValue(512) .description("The queue length to use before flushing writing when logging to Syslog.") .build(); + + public static final Option LOG_MDC_ENABLED = new OptionBuilder<>("log-mdc-enabled", Boolean.class) + .category(OptionCategory.LOGGING) + .defaultValue(false) + .buildTime(true) + .description("Indicates whether to add information about the realm and other information to the mapped diagnostic context. All elements will be prefixed with 'kc.'") + .build(); + + public static final Option> LOG_MDC_KEYS = OptionBuilder.listOptionBuilder("log-mdc-keys", String.class) + .category(OptionCategory.LOGGING) + .expectedValues(List.of("realm", "clientId", "userId", "ipAddress", "org")) + .defaultValue(List.of("realm", "org", "clientId")) + .description("Defines which information should be added to the mapped diagnostic context as a comma-separated list.") + .build(); + } diff --git a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java index 8f58ed5e6b9..f91a2072d1b 100644 --- a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java +++ b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java @@ -81,6 +81,7 @@ import org.keycloak.common.util.StreamUtil; import org.keycloak.config.DatabaseOptions; import org.keycloak.config.HealthOptions; import org.keycloak.config.HttpOptions; +import org.keycloak.config.LoggingOptions; import org.keycloak.config.ManagementOptions; import org.keycloak.config.MetricsOptions; import org.keycloak.config.SecurityOptions; @@ -111,6 +112,7 @@ import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper; import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers; import org.keycloak.quarkus.runtime.integration.resteasy.KeycloakHandlerChainCustomizer; import org.keycloak.quarkus.runtime.integration.resteasy.KeycloakTracingCustomizer; +import org.keycloak.quarkus.runtime.logging.ClearMappedDiagnosticContextFilter; import org.keycloak.quarkus.runtime.services.health.KeycloakReadyHealthCheck; import org.keycloak.quarkus.runtime.storage.database.jpa.NamedJpaConnectionProviderFactory; import org.keycloak.quarkus.runtime.themes.FlatClasspathThemeResourceProviderFactory; @@ -647,6 +649,16 @@ class KeycloakProcessor { } } + @BuildStep + void disableMdcContextFilter(BuildProducer removeBeans, CombinedIndexBuildItem index) { + if (!Configuration.isTrue(LoggingOptions.LOG_MDC_ENABLED)) { + // disables the filter + ClassInfo disabledBean = index.getIndex() + .getClassByName(DotName.createSimple(ClearMappedDiagnosticContextFilter.class.getName())); + removeBeans.produce(new BuildTimeConditionBuildItem(disabledBean.asClass(), false)); + } + } + // We can't use quarkus.datasource.health.enabled=false as that would remove the DataSourceHealthCheck from CDI and // it can't be instantiated via constructor as it now includes some field injection points. So we just make it a regular // bean without the @Readiness annotation so it won't be used as a health check on it's own. 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 d6dd5082a39..063637930e2 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 @@ -24,6 +24,7 @@ import java.util.stream.Stream; import io.quarkus.runtime.configuration.MemorySizeConverter; import org.jboss.logmanager.LogContext; +import org.keycloak.common.Profile; import org.keycloak.config.LoggingOptions; import org.keycloak.config.Option; import org.keycloak.quarkus.runtime.Messages; @@ -69,7 +70,7 @@ public final class LoggingPropertyMappers { .isEnabled(LoggingPropertyMappers::isConsoleEnabled, CONSOLE_ENABLED_MSG) .to("quarkus.log.console.format") .paramLabel("format") - .transformer((value, ctx) -> addTracingInfo(value, LoggingOptions.LOG_CONSOLE_INCLUDE_TRACE)) + .transformer((value, ctx) -> addTracingAndMdcInfo(value, LoggingOptions.LOG_CONSOLE_INCLUDE_TRACE, LoggingOptions.LOG_CONSOLE_INCLUDE_MDC)) .build(), fromOption(LoggingOptions.LOG_CONSOLE_JSON_FORMAT) .isEnabled(LoggingPropertyMappers::isConsoleJsonEnabled, "%s and output is set to 'json'".formatted(CONSOLE_ENABLED_MSG)) @@ -80,6 +81,10 @@ public final class LoggingPropertyMappers { .isEnabled(() -> LoggingPropertyMappers.isConsoleEnabled() && TracingPropertyMappers.isTracingEnabled(), "Console log handler and Tracing is activated") .build(), + fromOption(LoggingOptions.LOG_CONSOLE_INCLUDE_MDC) + .isEnabled(() -> LoggingPropertyMappers.isConsoleEnabled() && isMdcActive(), + "Console log handler and MDC logging are activated") + .build(), fromOption(LoggingOptions.LOG_CONSOLE_COLOR) .isEnabled(LoggingPropertyMappers::isConsoleEnabled, CONSOLE_ENABLED_MSG) .to("quarkus.log.console.color") @@ -120,7 +125,7 @@ public final class LoggingPropertyMappers { .isEnabled(LoggingPropertyMappers::isFileEnabled, FILE_ENABLED_MSG) .to("quarkus.log.file.format") .paramLabel("format") - .transformer((value, ctx) -> addTracingInfo(value, LoggingOptions.LOG_FILE_INCLUDE_TRACE)) + .transformer((value, ctx) -> addTracingAndMdcInfo(value, LoggingOptions.LOG_FILE_INCLUDE_TRACE, LoggingOptions.LOG_FILE_INCLUDE_MDC)) .build(), fromOption(LoggingOptions.LOG_FILE_JSON_FORMAT) .isEnabled(LoggingPropertyMappers::isFileJsonEnabled, FILE_ENABLED_MSG + " and output is set to 'json'") @@ -131,6 +136,10 @@ public final class LoggingPropertyMappers { .isEnabled(() -> LoggingPropertyMappers.isFileEnabled() && TracingPropertyMappers.isTracingEnabled(), "File log handler and Tracing is activated") .build(), + fromOption(LoggingOptions.LOG_FILE_INCLUDE_MDC) + .isEnabled(() -> LoggingPropertyMappers.isFileEnabled() && isMdcActive(), + "File log handler and MDC logging are activated") + .build(), fromOption(LoggingOptions.LOG_FILE_OUTPUT) .isEnabled(LoggingPropertyMappers::isFileEnabled, FILE_ENABLED_MSG) .to("quarkus.log.file.json.enabled") @@ -204,7 +213,7 @@ public final class LoggingPropertyMappers { .isEnabled(LoggingPropertyMappers::isSyslogEnabled, SYSLOG_ENABLED_MSG) .to("quarkus.log.syslog.format") .paramLabel("format") - .transformer((value, ctx) -> addTracingInfo(value, LoggingOptions.LOG_SYSLOG_INCLUDE_TRACE)) + .transformer((value, ctx) -> addTracingAndMdcInfo(value, LoggingOptions.LOG_SYSLOG_INCLUDE_TRACE, LoggingOptions.LOG_SYSLOG_INCLUDE_MDC)) .build(), fromOption(LoggingOptions.LOG_SYSLOG_JSON_FORMAT) .isEnabled(LoggingPropertyMappers::isSyslogJsonEnabled, SYSLOG_ENABLED_MSG + " and output is set to 'json'") @@ -215,6 +224,10 @@ public final class LoggingPropertyMappers { .isEnabled(() -> LoggingPropertyMappers.isSyslogEnabled() && TracingPropertyMappers.isTracingEnabled(), "Syslog handler and Tracing is activated") .build(), + fromOption(LoggingOptions.LOG_SYSLOG_INCLUDE_MDC) + .isEnabled(() -> LoggingPropertyMappers.isSyslogEnabled() && isMdcActive(), + "Syslog handler and MDC logging are activated") + .build(), fromOption(LoggingOptions.LOG_SYSLOG_OUTPUT) .isEnabled(LoggingPropertyMappers::isSyslogEnabled, SYSLOG_ENABLED_MSG) .to("quarkus.log.syslog.json.enabled") @@ -238,6 +251,17 @@ public final class LoggingPropertyMappers { .to("quarkus.log.syslog.async.queue-length") .paramLabel("queue-length") .build(), + // MDC + fromOption(LoggingOptions.LOG_MDC_ENABLED) + .to("kc.spi-mapped-diagnostic-context--default--enabled") + .isEnabled(LoggingPropertyMappers::isMdcAvailable, "log-mdc preview feature is enabled") + .build(), + fromOption(LoggingOptions.LOG_MDC_KEYS) + .isEnabled(LoggingPropertyMappers::isMdcActive, "MDC logging is enabled") + .to("kc.spi-mapped-diagnostic-context--default--mdc-keys") + .paramLabel("keys") + .build(), + }; return defaultMappers; @@ -275,6 +299,14 @@ public final class LoggingPropertyMappers { return isHandlerAsyncEnabled(LoggingOptions.Handler.syslog); } + private static boolean isMdcAvailable() { + return Profile.isFeatureEnabled(Profile.Feature.LOG_MDC); + } + + public static boolean isMdcActive() { + return Configuration.isTrue(LoggingOptions.LOG_MDC_ENABLED); + } + public static boolean isSyslogJsonEnabled() { return isSyslogEnabled() && isTrue("quarkus.log.syslog.json.enabled"); } @@ -380,16 +412,20 @@ public final class LoggingPropertyMappers { /** * Add tracing info to the log if the format is not explicitly set, and tracing and {@code includeTraceOption} options are enabled */ - private static String addTracingInfo(String value, Option includeTraceOption) { - var isTracingEnabled = TracingPropertyMappers.isTracingEnabled(); - var includeTrace = isTrue(includeTraceOption); - var isChangedLogFormat = !DEFAULT_LOG_FORMAT.equals(value); - - if (!isTracingEnabled || !includeTrace || isChangedLogFormat) { + private static String addTracingAndMdcInfo(String value, Option includeTraceOption, Option includeMdcOption) { + if (!DEFAULT_LOG_FORMAT.equals(value)) { return value; } + var isTracingEnabled = TracingPropertyMappers.isTracingEnabled(); + var includeTrace = isTrue(includeTraceOption); + var includeMdc = isTrue(includeMdcOption); - return LoggingOptions.DEFAULT_LOG_TRACING_FORMAT; + if (isMdcActive() && includeMdc) { + return LoggingOptions.DEFAULT_LOG_FORMAT_FUNC.apply("%X "); } + else if (isTracingEnabled && includeTrace) { + return LoggingOptions.DEFAULT_LOG_FORMAT_FUNC.apply("traceId=%X{traceId}, parentId=%X{parentId}, spanId=%X{spanId}, sampled=%X{sampled} "); + } + return LoggingOptions.DEFAULT_LOG_FORMAT; } private static String upperCase(String value, ConfigSourceInterceptorContext context) { diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/logging/ClearMappedDiagnosticContextFilter.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/logging/ClearMappedDiagnosticContextFilter.java new file mode 100644 index 00000000000..c56ee8cb263 --- /dev/null +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/logging/ClearMappedDiagnosticContextFilter.java @@ -0,0 +1,26 @@ +package org.keycloak.quarkus.runtime.logging; + +import jakarta.annotation.Priority; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.container.ContainerResponseContext; +import jakarta.ws.rs.container.ContainerResponseFilter; +import jakarta.ws.rs.ext.Provider; +import org.keycloak.logging.MappedDiagnosticContextUtil; + +import java.io.IOException; + +/** + * Response filter that clears custom properties from MDC. + * + * @author Björn Eickvonder + */ +@Provider +@Priority(0) +public class ClearMappedDiagnosticContextFilter implements ContainerResponseFilter { + + @Override + public void filter(ContainerRequestContext requestContext, + ContainerResponseContext responseContext) throws IOException { + MappedDiagnosticContextUtil.clearMdc(); + } +} diff --git a/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/TracingConfigurationTest.java b/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/TracingConfigurationTest.java index daf00699ba0..5fa8a600464 100644 --- a/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/TracingConfigurationTest.java +++ b/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/TracingConfigurationTest.java @@ -147,42 +147,61 @@ public class TracingConfigurationTest extends AbstractConfigurationTest { @Test public void consoleLogTraceOn() { - assertLogFormat(LoggingOptions.Handler.console, true, LoggingOptions.DEFAULT_LOG_TRACING_FORMAT); + assertLogFormat(LoggingOptions.Handler.console, true, false, LoggingOptions.DEFAULT_LOG_FORMAT_FUNC.apply("traceId=%X{traceId}, parentId=%X{parentId}, spanId=%X{spanId}, sampled=%X{sampled} ")); + } + + @Test + public void consoleLogMdcOn() { + assertLogFormat(LoggingOptions.Handler.console, true, true, LoggingOptions.DEFAULT_LOG_FORMAT_FUNC.apply("%X ")); } @Test public void consoleLogTraceOff() { - assertLogFormat(LoggingOptions.Handler.console, false, LoggingOptions.DEFAULT_LOG_FORMAT); + assertLogFormat(LoggingOptions.Handler.console, false, false, LoggingOptions.DEFAULT_LOG_FORMAT); } @Test public void fileLogTraceOn() { - assertLogFormat(LoggingOptions.Handler.file, true, LoggingOptions.DEFAULT_LOG_TRACING_FORMAT); + assertLogFormat(LoggingOptions.Handler.file, true, false, LoggingOptions.DEFAULT_LOG_FORMAT_FUNC.apply("traceId=%X{traceId}, parentId=%X{parentId}, spanId=%X{spanId}, sampled=%X{sampled} ")); + } + + @Test + public void fileLogMdcOn() { + assertLogFormat(LoggingOptions.Handler.file, true, true, LoggingOptions.DEFAULT_LOG_FORMAT_FUNC.apply("%X ")); } @Test public void fileLogTraceOff() { - assertLogFormat(LoggingOptions.Handler.file, false, LoggingOptions.DEFAULT_LOG_FORMAT); + assertLogFormat(LoggingOptions.Handler.file, false, false, LoggingOptions.DEFAULT_LOG_FORMAT); } @Test public void syslogLogTraceOn() { - assertLogFormat(LoggingOptions.Handler.syslog, true, LoggingOptions.DEFAULT_LOG_TRACING_FORMAT); + assertLogFormat(LoggingOptions.Handler.syslog, true, false, LoggingOptions.DEFAULT_LOG_FORMAT_FUNC.apply("traceId=%X{traceId}, parentId=%X{parentId}, spanId=%X{spanId}, sampled=%X{sampled} ")); + } + + @Test + public void syslogLogMdcOn() { + assertLogFormat(LoggingOptions.Handler.syslog, true, true, LoggingOptions.DEFAULT_LOG_FORMAT_FUNC.apply("%X ")); } @Test public void syslogLogTraceOff() { - assertLogFormat(LoggingOptions.Handler.syslog, false, LoggingOptions.DEFAULT_LOG_FORMAT); + assertLogFormat(LoggingOptions.Handler.syslog, false, false, LoggingOptions.DEFAULT_LOG_FORMAT); } /** * Assert log format for individual log handlers with different `includeTrace` option. * It also checks the log format is unchanged despite the `includeTrace` option, when explicitly specified. */ - protected void assertLogFormat(LoggingOptions.Handler loggerType, boolean includeTrace, String expectedFormat) { + protected void assertLogFormat(LoggingOptions.Handler loggerType, boolean includeTrace, boolean includeMdc, String expectedFormat) { var envVars = new HashMap(); envVars.put("KC_TRACING_ENABLED", "true"); + if (includeMdc) { + envVars.put("KC_LOG_MDC_ENABLED", "true"); + } envVars.put("KC_LOG_" + loggerType.name().toUpperCase() + "_INCLUDE_TRACE", Boolean.toString(includeTrace)); + envVars.put("KC_LOG_" + loggerType.name().toUpperCase() + "_INCLUDE_MDC", Boolean.toString(includeMdc)); putEnvVars(envVars); diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LoggingDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LoggingDistTest.java index bfdfcf33511..1f94cac2239 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LoggingDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LoggingDistTest.java @@ -17,6 +17,7 @@ package org.keycloak.it.cli.dist; +import static io.restassured.RestAssured.when; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; @@ -31,7 +32,6 @@ import java.nio.charset.Charset; import java.nio.file.Path; import java.nio.file.Paths; -import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.keycloak.config.LoggingOptions; @@ -42,13 +42,12 @@ import org.keycloak.it.junit5.extension.RawDistOnly; import org.keycloak.it.utils.KeycloakDistribution; import org.keycloak.it.utils.RawDistRootPath; import org.keycloak.it.utils.RawKeycloakDistribution; -import org.keycloak.quarkus.runtime.configuration.Configuration; import org.testcontainers.shaded.org.apache.commons.io.FileUtils; import io.quarkus.deployment.util.FileUtil; import io.quarkus.test.junit.main.Launch; -@DistributionTest +@DistributionTest(keepAlive = true) @RawDistOnly(reason = "Too verbose for docker and enough to check raw dist") @Tag(DistributionTest.SLOW) public class LoggingDistTest { @@ -266,6 +265,16 @@ public class LoggingDistTest { cliResult.assertStartedDevMode(); } + @Test + @Launch({ "start-dev", "--features=log-mdc","--log-mdc-enabled=true", "--log-level=org.keycloak:debug" }) + void testLogMdcShowingInTheLogs(CLIResult cliResult) { + + when().get("http://127.0.0.1:8080/realms/master/.well-known/openid-configuration").then() + .statusCode(200); + assertTrue(cliResult.getOutput().contains("{kc.realm=master} DEBUG [org.keycloak.")); + cliResult.assertStartedDevMode(); + } + protected static String readDefaultFileLog(RawDistRootPath path) { Path logFilePath = Paths.get(path.getDistRootPath() + File.separator + LoggingOptions.DEFAULT_LOG_PATH); File logFile = new File(logFilePath.toString()); diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testExportHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testExportHelpAll.approved.txt index efb5c13812f..5a0487a3ac1 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testExportHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testExportHelpAll.approved.txt @@ -214,6 +214,10 @@ Logging: The format of unstructured console log entries. If the format has spaces in it, escape the value using "". Default: %d{yyyy-MM-dd HH:mm:ss,SSS} % -5p [%c] (%t) %s%e%n. Available only when Console log handler is activated. +--log-console-include-mdc + Include mdc information in the console log. If the 'log-console-format' option + is specified, this option has no effect. Default: true. Available only when + Console log handler and MDC logging are activated. --log-console-include-trace Include tracing information in the console log. If the 'log-console-format' option is specified, this option has no effect. Default: true. Available @@ -247,6 +251,10 @@ Logging: Set a format specific to file log entries. Default: %d{yyyy-MM-dd HH:mm:ss, SSS} %-5p [%c] (%t) %s%e%n. Available only when File log handler is activated. +--log-file-include-mdc + Include MDC information in the file log. If the 'log-file-format' option is + specified, this option has no effect. Default: true. Available only when + File log handler and MDC logging are activated. --log-file-include-trace Include tracing information in the file log. If the 'log-file-format' option is specified, this option has no effect. Default: true. Available only when @@ -274,6 +282,15 @@ Logging: The log level of a category. Takes precedence over the 'log-level' option. Possible values are (case insensitive): off, fatal, error, warn, info, debug, trace, all. +--log-mdc-enabled + Indicates whether to add information about the realm and other information to + the mapped diagnostic context. All elements will be prefixed with 'kc.' + Default: false. Available only when log-mdc preview feature is enabled. +--log-mdc-keys + Defines which information should be added to the mapped diagnostic context as + a comma-separated list. Possible values are: realm, clientId, userId, + ipAddress, org. Default: realm,org,clientId. Available only when MDC logging + is enabled. --log-syslog-app-name Set the app name used when formatting the message in RFC5424 format. Default: keycloak. Available only when Syslog is activated. @@ -297,6 +314,10 @@ Logging: --log-syslog-format Set a format specific to Syslog entries. Default: %d{yyyy-MM-dd HH:mm:ss,SSS} % -5p [%c] (%t) %s%e%n. Available only when Syslog is activated. +--log-syslog-include-mdc + Include MDC information in the Syslog. If the 'log-syslog-format' option is + specified, this option has no effect. Default: true. Available only when + Syslog handler and MDC logging are activated. --log-syslog-include-trace Include tracing information in the Syslog. If the 'log-syslog-format' option is specified, this option has no effect. Default: true. Available only when @@ -420,4 +441,4 @@ Bootstrap Admin: --bootstrap-admin-username Temporary bootstrap admin username. Used only when the master realm is created. Available only when bootstrap admin password is set. Default: - temp-admin. \ No newline at end of file + temp-admin. diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testImportHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testImportHelpAll.approved.txt index 031e9e1f6b3..40c20ca6aca 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testImportHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testImportHelpAll.approved.txt @@ -214,6 +214,10 @@ Logging: The format of unstructured console log entries. If the format has spaces in it, escape the value using "". Default: %d{yyyy-MM-dd HH:mm:ss,SSS} % -5p [%c] (%t) %s%e%n. Available only when Console log handler is activated. +--log-console-include-mdc + Include mdc information in the console log. If the 'log-console-format' option + is specified, this option has no effect. Default: true. Available only when + Console log handler and MDC logging are activated. --log-console-include-trace Include tracing information in the console log. If the 'log-console-format' option is specified, this option has no effect. Default: true. Available @@ -247,6 +251,10 @@ Logging: Set a format specific to file log entries. Default: %d{yyyy-MM-dd HH:mm:ss, SSS} %-5p [%c] (%t) %s%e%n. Available only when File log handler is activated. +--log-file-include-mdc + Include MDC information in the file log. If the 'log-file-format' option is + specified, this option has no effect. Default: true. Available only when + File log handler and MDC logging are activated. --log-file-include-trace Include tracing information in the file log. If the 'log-file-format' option is specified, this option has no effect. Default: true. Available only when @@ -274,6 +282,15 @@ Logging: The log level of a category. Takes precedence over the 'log-level' option. Possible values are (case insensitive): off, fatal, error, warn, info, debug, trace, all. +--log-mdc-enabled + Indicates whether to add information about the realm and other information to + the mapped diagnostic context. All elements will be prefixed with 'kc.' + Default: false. Available only when log-mdc preview feature is enabled. +--log-mdc-keys + Defines which information should be added to the mapped diagnostic context as + a comma-separated list. Possible values are: realm, clientId, userId, + ipAddress, org. Default: realm,org,clientId. Available only when MDC logging + is enabled. --log-syslog-app-name Set the app name used when formatting the message in RFC5424 format. Default: keycloak. Available only when Syslog is activated. @@ -297,6 +314,10 @@ Logging: --log-syslog-format Set a format specific to Syslog entries. Default: %d{yyyy-MM-dd HH:mm:ss,SSS} % -5p [%c] (%t) %s%e%n. Available only when Syslog is activated. +--log-syslog-include-mdc + Include MDC information in the Syslog. If the 'log-syslog-format' option is + specified, this option has no effect. Default: true. Available only when + Syslog handler and MDC logging are activated. --log-syslog-include-trace Include tracing information in the Syslog. If the 'log-syslog-format' option is specified, this option has no effect. Default: true. Available only when @@ -414,4 +435,4 @@ Bootstrap Admin: --bootstrap-admin-username Temporary bootstrap admin username. Used only when the master realm is created. Available only when bootstrap admin password is set. Default: - temp-admin. \ No newline at end of file + temp-admin. diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelpAll.approved.txt index 936d9178e9b..014bdfdd19b 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelpAll.approved.txt @@ -445,6 +445,10 @@ Logging: The format of unstructured console log entries. If the format has spaces in it, escape the value using "". Default: %d{yyyy-MM-dd HH:mm:ss,SSS} % -5p [%c] (%t) %s%e%n. Available only when Console log handler is activated. +--log-console-include-mdc + Include mdc information in the console log. If the 'log-console-format' option + is specified, this option has no effect. Default: true. Available only when + Console log handler and MDC logging are activated. --log-console-include-trace Include tracing information in the console log. If the 'log-console-format' option is specified, this option has no effect. Default: true. Available @@ -478,6 +482,10 @@ Logging: Set a format specific to file log entries. Default: %d{yyyy-MM-dd HH:mm:ss, SSS} %-5p [%c] (%t) %s%e%n. Available only when File log handler is activated. +--log-file-include-mdc + Include MDC information in the file log. If the 'log-file-format' option is + specified, this option has no effect. Default: true. Available only when + File log handler and MDC logging are activated. --log-file-include-trace Include tracing information in the file log. If the 'log-file-format' option is specified, this option has no effect. Default: true. Available only when @@ -505,6 +513,15 @@ Logging: The log level of a category. Takes precedence over the 'log-level' option. Possible values are (case insensitive): off, fatal, error, warn, info, debug, trace, all. +--log-mdc-enabled + Indicates whether to add information about the realm and other information to + the mapped diagnostic context. All elements will be prefixed with 'kc.' + Default: false. Available only when log-mdc preview feature is enabled. +--log-mdc-keys + Defines which information should be added to the mapped diagnostic context as + a comma-separated list. Possible values are: realm, clientId, userId, + ipAddress, org. Default: realm,org,clientId. Available only when MDC logging + is enabled. --log-syslog-app-name Set the app name used when formatting the message in RFC5424 format. Default: keycloak. Available only when Syslog is activated. @@ -528,6 +545,10 @@ Logging: --log-syslog-format Set a format specific to Syslog entries. Default: %d{yyyy-MM-dd HH:mm:ss,SSS} % -5p [%c] (%t) %s%e%n. Available only when Syslog is activated. +--log-syslog-include-mdc + Include MDC information in the Syslog. If the 'log-syslog-format' option is + specified, this option has no effect. Default: true. Available only when + Syslog handler and MDC logging are activated. --log-syslog-include-trace Include tracing information in the Syslog. If the 'log-syslog-format' option is specified, this option has no effect. Default: true. Available only when @@ -651,4 +672,4 @@ Bootstrap Admin: Do NOT start the server using this command when deploying to production. Use 'kc.sh start-dev --help-all' to list all available options, including build -options. \ No newline at end of file +options. diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelpAll.approved.txt index a6a46914aed..7c9d592ae5e 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelpAll.approved.txt @@ -446,6 +446,10 @@ Logging: The format of unstructured console log entries. If the format has spaces in it, escape the value using "". Default: %d{yyyy-MM-dd HH:mm:ss,SSS} % -5p [%c] (%t) %s%e%n. Available only when Console log handler is activated. +--log-console-include-mdc + Include mdc information in the console log. If the 'log-console-format' option + is specified, this option has no effect. Default: true. Available only when + Console log handler and MDC logging are activated. --log-console-include-trace Include tracing information in the console log. If the 'log-console-format' option is specified, this option has no effect. Default: true. Available @@ -479,6 +483,10 @@ Logging: Set a format specific to file log entries. Default: %d{yyyy-MM-dd HH:mm:ss, SSS} %-5p [%c] (%t) %s%e%n. Available only when File log handler is activated. +--log-file-include-mdc + Include MDC information in the file log. If the 'log-file-format' option is + specified, this option has no effect. Default: true. Available only when + File log handler and MDC logging are activated. --log-file-include-trace Include tracing information in the file log. If the 'log-file-format' option is specified, this option has no effect. Default: true. Available only when @@ -506,6 +514,15 @@ Logging: The log level of a category. Takes precedence over the 'log-level' option. Possible values are (case insensitive): off, fatal, error, warn, info, debug, trace, all. +--log-mdc-enabled + Indicates whether to add information about the realm and other information to + the mapped diagnostic context. All elements will be prefixed with 'kc.' + Default: false. Available only when log-mdc preview feature is enabled. +--log-mdc-keys + Defines which information should be added to the mapped diagnostic context as + a comma-separated list. Possible values are: realm, clientId, userId, + ipAddress, org. Default: realm,org,clientId. Available only when MDC logging + is enabled. --log-syslog-app-name Set the app name used when formatting the message in RFC5424 format. Default: keycloak. Available only when Syslog is activated. @@ -529,6 +546,10 @@ Logging: --log-syslog-format Set a format specific to Syslog entries. Default: %d{yyyy-MM-dd HH:mm:ss,SSS} % -5p [%c] (%t) %s%e%n. Available only when Syslog is activated. +--log-syslog-include-mdc + Include MDC information in the Syslog. If the 'log-syslog-format' option is + specified, this option has no effect. Default: true. Available only when + Syslog handler and MDC logging are activated. --log-syslog-include-trace Include tracing information in the Syslog. If the 'log-syslog-format' option is specified, this option has no effect. Default: true. Available only when @@ -656,4 +677,4 @@ By default, this command tries to update the server configuration by running a $ kc.sh start '--optimized' By doing that, the server should start faster based on any previous -configuration you have set when manually running the 'build' command. \ No newline at end of file +configuration you have set when manually running the 'build' command. diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelpAll.approved.txt index ef6b274656f..3d3847bd907 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelpAll.approved.txt @@ -384,6 +384,10 @@ Logging: The format of unstructured console log entries. If the format has spaces in it, escape the value using "". Default: %d{yyyy-MM-dd HH:mm:ss,SSS} % -5p [%c] (%t) %s%e%n. Available only when Console log handler is activated. +--log-console-include-mdc + Include mdc information in the console log. If the 'log-console-format' option + is specified, this option has no effect. Default: true. Available only when + Console log handler and MDC logging are activated. --log-console-include-trace Include tracing information in the console log. If the 'log-console-format' option is specified, this option has no effect. Default: true. Available @@ -417,6 +421,10 @@ Logging: Set a format specific to file log entries. Default: %d{yyyy-MM-dd HH:mm:ss, SSS} %-5p [%c] (%t) %s%e%n. Available only when File log handler is activated. +--log-file-include-mdc + Include MDC information in the file log. If the 'log-file-format' option is + specified, this option has no effect. Default: true. Available only when + File log handler and MDC logging are activated. --log-file-include-trace Include tracing information in the file log. If the 'log-file-format' option is specified, this option has no effect. Default: true. Available only when @@ -444,6 +452,11 @@ Logging: The log level of a category. Takes precedence over the 'log-level' option. Possible values are (case insensitive): off, fatal, error, warn, info, debug, trace, all. +--log-mdc-keys + Defines which information should be added to the mapped diagnostic context as + a comma-separated list. Possible values are: realm, clientId, userId, + ipAddress, org. Default: realm,org,clientId. Available only when MDC logging + is enabled. --log-syslog-app-name Set the app name used when formatting the message in RFC5424 format. Default: keycloak. Available only when Syslog is activated. @@ -467,6 +480,10 @@ Logging: --log-syslog-format Set a format specific to Syslog entries. Default: %d{yyyy-MM-dd HH:mm:ss,SSS} % -5p [%c] (%t) %s%e%n. Available only when Syslog is activated. +--log-syslog-include-mdc + Include MDC information in the Syslog. If the 'log-syslog-format' option is + specified, this option has no effect. Default: true. Available only when + Syslog handler and MDC logging are activated. --log-syslog-include-trace Include tracing information in the Syslog. If the 'log-syslog-format' option is specified, this option has no effect. Default: true. Available only when @@ -571,4 +588,4 @@ By default, this command tries to update the server configuration by running a $ kc.sh start '--optimized' By doing that, the server should start faster based on any previous -configuration you have set when manually running the 'build' command. \ No newline at end of file +configuration you have set when manually running the 'build' command. diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityCheckHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityCheckHelpAll.approved.txt index 973525ae354..3e5075b981d 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityCheckHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityCheckHelpAll.approved.txt @@ -445,6 +445,10 @@ Logging: The format of unstructured console log entries. If the format has spaces in it, escape the value using "". Default: %d{yyyy-MM-dd HH:mm:ss,SSS} % -5p [%c] (%t) %s%e%n. Available only when Console log handler is activated. +--log-console-include-mdc + Include mdc information in the console log. If the 'log-console-format' option + is specified, this option has no effect. Default: true. Available only when + Console log handler and MDC logging are activated. --log-console-include-trace Include tracing information in the console log. If the 'log-console-format' option is specified, this option has no effect. Default: true. Available @@ -478,6 +482,10 @@ Logging: Set a format specific to file log entries. Default: %d{yyyy-MM-dd HH:mm:ss, SSS} %-5p [%c] (%t) %s%e%n. Available only when File log handler is activated. +--log-file-include-mdc + Include MDC information in the file log. If the 'log-file-format' option is + specified, this option has no effect. Default: true. Available only when + File log handler and MDC logging are activated. --log-file-include-trace Include tracing information in the file log. If the 'log-file-format' option is specified, this option has no effect. Default: true. Available only when @@ -505,6 +513,15 @@ Logging: The log level of a category. Takes precedence over the 'log-level' option. Possible values are (case insensitive): off, fatal, error, warn, info, debug, trace, all. +--log-mdc-enabled + Indicates whether to add information about the realm and other information to + the mapped diagnostic context. All elements will be prefixed with 'kc.' + Default: false. Available only when log-mdc preview feature is enabled. +--log-mdc-keys + Defines which information should be added to the mapped diagnostic context as + a comma-separated list. Possible values are: realm, clientId, userId, + ipAddress, org. Default: realm,org,clientId. Available only when MDC logging + is enabled. --log-syslog-app-name Set the app name used when formatting the message in RFC5424 format. Default: keycloak. Available only when Syslog is activated. @@ -528,6 +545,10 @@ Logging: --log-syslog-format Set a format specific to Syslog entries. Default: %d{yyyy-MM-dd HH:mm:ss,SSS} % -5p [%c] (%t) %s%e%n. Available only when Syslog is activated. +--log-syslog-include-mdc + Include MDC information in the Syslog. If the 'log-syslog-format' option is + specified, this option has no effect. Default: true. Available only when + Syslog handler and MDC logging are activated. --log-syslog-include-trace Include tracing information in the Syslog. If the 'log-syslog-format' option is specified, this option has no effect. Default: true. Available only when @@ -646,4 +667,4 @@ Bootstrap Admin: --bootstrap-admin-username Temporary bootstrap admin username. Used only when the master realm is created. Available only when bootstrap admin password is set. Default: - temp-admin. \ No newline at end of file + temp-admin. diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityMetadataHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityMetadataHelpAll.approved.txt index 5d6bb8fa0f8..1573bca4fc9 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityMetadataHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityMetadataHelpAll.approved.txt @@ -443,6 +443,10 @@ Logging: The format of unstructured console log entries. If the format has spaces in it, escape the value using "". Default: %d{yyyy-MM-dd HH:mm:ss,SSS} % -5p [%c] (%t) %s%e%n. Available only when Console log handler is activated. +--log-console-include-mdc + Include mdc information in the console log. If the 'log-console-format' option + is specified, this option has no effect. Default: true. Available only when + Console log handler and MDC logging are activated. --log-console-include-trace Include tracing information in the console log. If the 'log-console-format' option is specified, this option has no effect. Default: true. Available @@ -476,6 +480,10 @@ Logging: Set a format specific to file log entries. Default: %d{yyyy-MM-dd HH:mm:ss, SSS} %-5p [%c] (%t) %s%e%n. Available only when File log handler is activated. +--log-file-include-mdc + Include MDC information in the file log. If the 'log-file-format' option is + specified, this option has no effect. Default: true. Available only when + File log handler and MDC logging are activated. --log-file-include-trace Include tracing information in the file log. If the 'log-file-format' option is specified, this option has no effect. Default: true. Available only when @@ -503,6 +511,15 @@ Logging: The log level of a category. Takes precedence over the 'log-level' option. Possible values are (case insensitive): off, fatal, error, warn, info, debug, trace, all. +--log-mdc-enabled + Indicates whether to add information about the realm and other information to + the mapped diagnostic context. All elements will be prefixed with 'kc.' + Default: false. Available only when log-mdc preview feature is enabled. +--log-mdc-keys + Defines which information should be added to the mapped diagnostic context as + a comma-separated list. Possible values are: realm, clientId, userId, + ipAddress, org. Default: realm,org,clientId. Available only when MDC logging + is enabled. --log-syslog-app-name Set the app name used when formatting the message in RFC5424 format. Default: keycloak. Available only when Syslog is activated. @@ -526,6 +543,10 @@ Logging: --log-syslog-format Set a format specific to Syslog entries. Default: %d{yyyy-MM-dd HH:mm:ss,SSS} % -5p [%c] (%t) %s%e%n. Available only when Syslog is activated. +--log-syslog-include-mdc + Include MDC information in the Syslog. If the 'log-syslog-format' option is + specified, this option has no effect. Default: true. Available only when + Syslog handler and MDC logging are activated. --log-syslog-include-trace Include tracing information in the Syslog. If the 'log-syslog-format' option is specified, this option has no effect. Default: true. Available only when @@ -644,4 +665,4 @@ Bootstrap Admin: --bootstrap-admin-username Temporary bootstrap admin username. Used only when the master realm is created. Available only when bootstrap admin password is set. Default: - temp-admin. \ No newline at end of file + temp-admin. diff --git a/server-spi-private/src/main/java/org/keycloak/logging/MappedDiagnosticContextProvider.java b/server-spi-private/src/main/java/org/keycloak/logging/MappedDiagnosticContextProvider.java new file mode 100644 index 00000000000..8c34f78c7d8 --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/logging/MappedDiagnosticContextProvider.java @@ -0,0 +1,71 @@ +package org.keycloak.logging; + +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakContext; +import org.keycloak.models.OrganizationModel; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserSessionModel; +import org.keycloak.provider.Provider; +import org.keycloak.sessions.AuthenticationSessionModel; + +/** + * Provider interface for updating the Mapped Diagnostic Context (MDC) with key/value pairs based on the current keycloak context. + * All keys in the MDC will be prefixed with "kc." to avoid conflicts. + * + * @author Björn Eickvonder + */ +public interface MappedDiagnosticContextProvider extends Provider { + + String MDC_PREFIX = "kc."; + + /** + * Updates the Mapped Diagnostic Context (MDC) with key/value pairs based on the current Keycloak context. + * This method is called when a Keycloak Session is set and when the authentication session property of the + * Keycloak context is updated. + * + * @param keycloakContext the current Keycloak context, never null + * @param session the authentication session + */ + void update(KeycloakContext keycloakContext, AuthenticationSessionModel session); + + /** + * Updates the Mapped Diagnostic Context (MDC) with key/value pairs based on the current Keycloak context. + * This method is called when a Keycloak Session is set and when the realm property of the Keycloak context + * is updated. + * + * @param keycloakContext the current Keycloak context, never null + * @param realm the realm + */ + void update(KeycloakContext keycloakContext, RealmModel realm); + + /** + * Updates the Mapped Diagnostic Context (MDC) with key/value pairs based on the current Keycloak context. + * This method is called when a Keycloak Session is set and when the client property of the Keycloak context + * is updated. + * + * @param keycloakContext the current Keycloak context, never null + * @param client the client + */ + void update(KeycloakContext keycloakContext, ClientModel client); + + /** + * Updates the Mapped Diagnostic Context (MDC) with key/value pairs based on the current Keycloak context. + * This method is called when a Keycloak Session is set and when the organization property of the Keycloak context + * is updated. + * + * @param keycloakContext the current Keycloak context, never null + * @param organization the organization + */ + void update(KeycloakContext keycloakContext, OrganizationModel organization); + + /** + * Updates the Mapped Diagnostic Context (MDC) with key/value pairs based on the current Keycloak context. + * This method is called when a Keycloak Session is set and when the user session property of the Keycloak context + * is updated. + * + * @param keycloakContext the current Keycloak context, never null + * @param userSession the user session + */ + void update(KeycloakContext keycloakContext, UserSessionModel userSession); + +} diff --git a/server-spi-private/src/main/java/org/keycloak/logging/MappedDiagnosticContextProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/logging/MappedDiagnosticContextProviderFactory.java new file mode 100644 index 00000000000..27fe437ca2e --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/logging/MappedDiagnosticContextProviderFactory.java @@ -0,0 +1,9 @@ +package org.keycloak.logging; + +import org.keycloak.provider.ProviderFactory; + +/** + * @author Björn Eickvonder + */ +public interface MappedDiagnosticContextProviderFactory extends ProviderFactory { +} diff --git a/server-spi-private/src/main/java/org/keycloak/logging/MappedDiagnosticContextSpi.java b/server-spi-private/src/main/java/org/keycloak/logging/MappedDiagnosticContextSpi.java new file mode 100644 index 00000000000..7fa0bd85907 --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/logging/MappedDiagnosticContextSpi.java @@ -0,0 +1,31 @@ +package org.keycloak.logging; + +import org.keycloak.provider.Spi; + +/** + * This SPI is used to define the MDC keys and values that should be set for each request. + * + * @author Björn Eickvonder + */ +public class MappedDiagnosticContextSpi implements Spi { + + @Override + public boolean isInternal() { + return false; + } + + @Override + public String getName() { + return "mappedDiagnosticContext"; + } + + @Override + public Class getProviderClass() { + return MappedDiagnosticContextProvider.class; + } + + @Override + public Class getProviderFactoryClass() { + return MappedDiagnosticContextProviderFactory.class; + } +} diff --git a/server-spi-private/src/main/java/org/keycloak/logging/MappedDiagnosticContextUtil.java b/server-spi-private/src/main/java/org/keycloak/logging/MappedDiagnosticContextUtil.java new file mode 100644 index 00000000000..cd335a2ec95 --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/logging/MappedDiagnosticContextUtil.java @@ -0,0 +1,45 @@ +package org.keycloak.logging; + +import org.jboss.logging.Logger; +import org.jboss.logging.MDC; +import org.keycloak.common.Profile; +import org.keycloak.models.KeycloakSession; + +import java.util.Collection; +import java.util.Collections; + +public final class MappedDiagnosticContextUtil { + + private static final Logger log = Logger.getLogger(MappedDiagnosticContextUtil.class); + private static final MappedDiagnosticContextProvider NOOP_PROVIDER = new NoopMappedDiagnosticContextProvider(); + private static volatile Collection keysToClear = Collections.emptySet(); + + public static MappedDiagnosticContextProvider getMappedDiagnosticContextProvider(KeycloakSession session) { + if (!Profile.isFeatureEnabled(Profile.Feature.LOG_MDC)) { + return NOOP_PROVIDER; + } + if (session == null) { + log.warn("Cannot obtain session from thread to init MappedDiagnosticContextProvider. Return Noop provider."); + return NOOP_PROVIDER; + } + MappedDiagnosticContextProvider provider = session.getProvider(MappedDiagnosticContextProvider.class); + if (provider == null) { + return NOOP_PROVIDER; + } + return provider; + } + + public static void setKeysToClear(Collection keys) { + // As the MDC.getMap() clones the context and is possibly expensive, we instead iterate over the list of all known keys + keysToClear = keys; + } + + /** + * Clears the Mapped Diagnostic Context (MDC), but only clears the key/value pairs that were set by this provider. + */ + public static void clearMdc() { + for (String key : keysToClear) { + MDC.remove(key); + } + } +} diff --git a/server-spi-private/src/main/java/org/keycloak/logging/NoopMappedDiagnosticContextProvider.java b/server-spi-private/src/main/java/org/keycloak/logging/NoopMappedDiagnosticContextProvider.java new file mode 100644 index 00000000000..318d3618fc7 --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/logging/NoopMappedDiagnosticContextProvider.java @@ -0,0 +1,41 @@ +package org.keycloak.logging; + +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakContext; +import org.keycloak.models.OrganizationModel; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserSessionModel; +import org.keycloak.sessions.AuthenticationSessionModel; + +public class NoopMappedDiagnosticContextProvider implements MappedDiagnosticContextProvider { + + @Override + public void update(KeycloakContext keycloakContext, AuthenticationSessionModel session) { + // no-op + } + + @Override + public void update(KeycloakContext keycloakContext, RealmModel realm) { + // no-op + } + + @Override + public void update(KeycloakContext keycloakContext, ClientModel client) { + // no-op + } + + @Override + public void update(KeycloakContext keycloakContext, OrganizationModel organization) { + // no-op + } + + @Override + public void update(KeycloakContext keycloakContext, UserSessionModel userSession) { + // no-op + } + + @Override + public void close() { + // no-op + } +} diff --git a/server-spi-private/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java b/server-spi-private/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java index 2737f68fb4f..852ff68d92b 100644 --- a/server-spi-private/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java +++ b/server-spi-private/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java @@ -18,6 +18,7 @@ package org.keycloak.services.scheduled; import org.jboss.logging.Logger; +import org.keycloak.logging.MappedDiagnosticContextUtil; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.utils.KeycloakModelUtils; @@ -76,6 +77,7 @@ public class ScheduledTaskRunner implements TaskRunner { logger.errorf(t, "Failed to run scheduled task %s", task.getTaskName()); } finally { tracing.close(); + MappedDiagnosticContextUtil.clearMdc(); } } diff --git a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi index f7acfb1b045..bc4d14b4f43 100755 --- a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi +++ b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -104,3 +104,4 @@ org.keycloak.health.LoadBalancerCheckSpi org.keycloak.cookie.CookieSpi org.keycloak.organization.OrganizationSpi org.keycloak.securityprofile.SecurityProfileSpi +org.keycloak.logging.MappedDiagnosticContextSpi diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java index 41072d45528..562ab155185 100755 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java @@ -24,6 +24,8 @@ import org.keycloak.common.ClientConnection; import org.keycloak.http.HttpRequest; import org.keycloak.http.HttpResponse; import org.keycloak.locale.LocaleSelectorProvider; +import org.keycloak.logging.MappedDiagnosticContextProvider; +import org.keycloak.logging.MappedDiagnosticContextUtil; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakContext; import org.keycloak.models.KeycloakSession; @@ -118,7 +120,8 @@ public abstract class DefaultKeycloakContext implements KeycloakContext { public void setRealm(RealmModel realm) { this.realm = realm; this.uriInfo = null; - trace(this.realm); + trace(realm); + mdc().update(this, realm); } @Override @@ -134,7 +137,8 @@ public abstract class DefaultKeycloakContext implements KeycloakContext { @Override public void setClient(ClientModel client) { this.client = client; - trace(this.client); + trace(client); + mdc().update(this, client); } @Override @@ -145,6 +149,7 @@ public abstract class DefaultKeycloakContext implements KeycloakContext { @Override public void setOrganization(OrganizationModel organization) { this.organization = organization; + mdc().update(this, organization); } @Override @@ -174,7 +179,8 @@ public abstract class DefaultKeycloakContext implements KeycloakContext { @Override public void setAuthenticationSession(AuthenticationSessionModel authenticationSession) { this.authenticationSession = authenticationSession; - trace(this.authenticationSession); + trace(authenticationSession); + mdc().update(this, authenticationSession); } @Override @@ -230,7 +236,8 @@ public abstract class DefaultKeycloakContext implements KeycloakContext { @Override public void setUserSession(UserSessionModel userSession) { this.userSession = userSession; - trace(this.userSession); + trace(userSession); + mdc().update(this, userSession); } // Tracing @@ -309,4 +316,8 @@ public abstract class DefaultKeycloakContext implements KeycloakContext { return user; } + + private MappedDiagnosticContextProvider mdc() { + return MappedDiagnosticContextUtil.getMappedDiagnosticContextProvider(session); + } } diff --git a/services/src/main/java/org/keycloak/services/logging/DefaultMappedDiagnosticContextProviderFactory.java b/services/src/main/java/org/keycloak/services/logging/DefaultMappedDiagnosticContextProviderFactory.java new file mode 100644 index 00000000000..7f5127bc530 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/logging/DefaultMappedDiagnosticContextProviderFactory.java @@ -0,0 +1,135 @@ +package org.keycloak.services.logging; + +import org.jboss.logging.MDC; +import org.keycloak.Config; +import org.keycloak.common.Profile; +import org.keycloak.logging.MappedDiagnosticContextProvider; +import org.keycloak.logging.MappedDiagnosticContextProviderFactory; +import org.keycloak.logging.MappedDiagnosticContextUtil; +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakContext; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.OrganizationModel; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserSessionModel; +import org.keycloak.provider.EnvironmentDependentProviderFactory; +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.provider.ProviderConfigurationBuilder; +import org.keycloak.sessions.AuthenticationSessionModel; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * The default provider factory can be configured via --spi-mapped-diagnostic-context-default-mdc-keys to define mdc + * keys to add as a comma-separated list. By default, "realm", "clientId", "userId", "ipAddress" and "org" are supported by the default provider implementation. + * If you need further keys, you need to extend the provider. + * + * @author Björn Eickvonder + */ +public class DefaultMappedDiagnosticContextProviderFactory implements MappedDiagnosticContextProviderFactory, MappedDiagnosticContextProvider, EnvironmentDependentProviderFactory { + + public static final String MDC_KEY_REALM = MDC_PREFIX + "realm"; + public static final String MDC_KEY_CLIENT_ID = MDC_PREFIX + "clientId"; + public static final String MDC_KEY_USER_ID = MDC_PREFIX + "userId"; + public static final String MDC_KEY_IP_ADDRESS = MDC_PREFIX + "ipAddress"; + public static final String MDC_KEY_ORGANIZATION = MDC_PREFIX + "org"; + + public static final String MDC_KEYS = "mdcKeys"; + private Set mdcKeys; + + @Override + public MappedDiagnosticContextProvider create(KeycloakSession session) { + // not using session, thus implementing MappedDiagnosticContextProvider here and handling it as singleton is fine + return this; + } + + @Override + public void init(Config.Scope config) { + this.mdcKeys = Arrays.stream(Objects.requireNonNullElse(config.getArray(MDC_KEYS), new String[] {})).map(s -> MDC_PREFIX + s).collect(Collectors.toSet()); + MappedDiagnosticContextUtil.setKeysToClear(mdcKeys); + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + // no-op + } + + @Override + public void close() { + // no-op + } + + @Override + public String getId() { + return "default"; + } + + @Override + public List getConfigMetadata() { + ProviderConfigurationBuilder builder = ProviderConfigurationBuilder.create(); + + builder.property() + .name(MDC_KEYS) + .type("string") + .helpText("Comma-separated list of MDC keys to add to the Mapped Diagnostic Context.") + .options(Stream.of(MDC_KEY_REALM, MDC_KEY_CLIENT_ID, MDC_KEY_USER_ID, MDC_KEY_IP_ADDRESS, MDC_KEY_ORGANIZATION).map(s -> s.substring(MDC_PREFIX.length())).collect(Collectors.toList())) + .add(); + + return builder.build(); + } + + @Override + public boolean isSupported(Config.Scope config) { + return Profile.isFeatureEnabled(Profile.Feature.LOG_MDC); + } + + @Override + public void update(KeycloakContext keycloakContext, AuthenticationSessionModel session) { + // nothing of interest here + } + + @Override + public void update(KeycloakContext keycloakContext, RealmModel realm) { + if (mdcKeys.contains(MDC_KEY_REALM)) { + putMdc(MDC_KEY_REALM, realm != null ? realm.getName() : null); + } + } + + @Override + public void update(KeycloakContext keycloakContext, ClientModel client) { + if (mdcKeys.contains(MDC_KEY_CLIENT_ID)) { + putMdc(MDC_KEY_CLIENT_ID, client != null ? client.getClientId() : null); + } + } + + @Override + public void update(KeycloakContext keycloakContext, OrganizationModel organization) { + if (mdcKeys.contains(MDC_KEY_ORGANIZATION)) { + putMdc(MDC_KEY_ORGANIZATION, organization != null ? organization.getAlias() : null); + } + } + + @Override + public void update(KeycloakContext keycloakContext, UserSessionModel userSession) { + if (mdcKeys.contains(MDC_KEY_USER_ID)) { + putMdc(MDC_KEY_USER_ID, userSession != null && userSession.getUser() != null ? userSession.getUser().getId() : null); + } + if (mdcKeys.contains(MDC_KEY_IP_ADDRESS)) { + putMdc(MDC_KEY_IP_ADDRESS, userSession != null ? userSession.getIpAddress() : null); + } + } + + protected void putMdc(String key, String value) { + if (value != null) { + MDC.put(key, value); + } else { + MDC.remove(key); + } + } +} diff --git a/services/src/main/resources/META-INF/services/org.keycloak.logging.MappedDiagnosticContextProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.logging.MappedDiagnosticContextProviderFactory new file mode 100644 index 00000000000..886f548d207 --- /dev/null +++ b/services/src/main/resources/META-INF/services/org.keycloak.logging.MappedDiagnosticContextProviderFactory @@ -0,0 +1 @@ +org.keycloak.services.logging.DefaultMappedDiagnosticContextProviderFactory