Provide simple HTTP access logs (#41389)

Closes #41352

Signed-off-by: Martin Bartoš <mabartos@redhat.com>
This commit is contained in:
Martin Bartoš 2025-07-29 10:55:18 +02:00 committed by GitHub
parent 097fe4ab46
commit 3d5a1038a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 361 additions and 1 deletions

View File

@ -20,3 +20,10 @@ You can now add context information to each log message like the realm or the cl
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].
= HTTP Access logging
{project_name} supports HTTP access logging to record details of incoming HTTP requests.
While access logs are often used for debugging and traffic analysis, they are also important for security auditing and compliance monitoring.
For more information, see the https://www.keycloak.org/server/logging[Logging guide].

View File

@ -94,7 +94,7 @@ Use the option `log-mdc-enabled` to enable it.
Specify which keys to be added by setting the configuration option `log-mdc-keys`.
==== Configuring levels as individual options
=== Configuring levels as individual options
When configuring category-specific log levels, you can also set the log levels as individual `log-level-<category>` 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.
@ -253,6 +253,48 @@ You can change the queue length as follows:
These properties are available only when asynchronous logging is enabled for these specific log handlers.
=== HTTP Access Logging
{project_name} supports HTTP access logging to record details of incoming HTTP requests.
While access logs are often used for debugging and traffic analysis, they are also important for security auditing and compliance monitoring, helping administrators track access patterns, identify suspicious activity, and maintain audit trails.
These logs are written at the `INFO` level, so make sure your logging configuration includes this level — either globally (e.g. `log-level=info`) or specifically for the access log category (e.g. `log-level=org.keycloak.http.access-log:info`).
When HTTP access logs are enabled, they are shown by default, as `INFO` level is the default log level for {project_name}.
==== How to enable
You can enable HTTP access logging by using `http-access-log-enabled` property as follows:
<@kc.start parameters="--http-access-log-enabled=true"/>
==== Change log format/pattern
You can change format/pattern of the access log records by using `http-access-log-pattern` property as follows:
<@kc.start parameters="--http-access-log-pattern=combined"/>
Predefined named patterns:
* `common` (default) - prints basic information about the request
* `combined` - prints basic information about the request + information about referer and user agent
* `long` - prints comprehensive information about the request with all its headers
You can even specify your own pattern with your required data to be logged, such as:
<@kc.start parameters="--http-access-log-pattern='%A %{METHOD} %{REQUEST_URL} %{i,User-Agent}'"/>
Consult the https://quarkus.io/guides/http-reference#configuring-http-access-logs[Quarkus documentation] for the full list of variables that can be used.
==== Exclude specific URL paths
It is possible to exclude specific URL paths from the HTTP access logging, so they will not be recorded.
You can use regular expressions to exclude them, such as:
<@kc.start parameters="--http-access-log-exclude='/realms/my-internal-realm/.*'"/>
In this case, all calls to the `/realms/my-internal-realm/` and subsequent paths will be excluded from the HTTP Access log.
== Console log handler
The console log handler is enabled by default, providing unstructured log messages for the console.
@ -511,6 +553,9 @@ As you can see, the timestamp is present twice, so you can amend it correspondin
=== Syslog
<@opts.includeOptions includedOptions="log-syslog-*"/>
=== HTTP Access log
<@opts.includeOptions includedOptions="http-access-log-*"/>
</@opts.printRelevantOptions>
</@tmpl.guide>

View File

@ -0,0 +1,23 @@
package org.keycloak.config;
public class HttpAccessLogOptions {
public static final Option<Boolean> HTTP_ACCESS_LOG_ENABLED = new OptionBuilder<>("http-access-log-enabled", Boolean.class)
.category(OptionCategory.HTTP_ACCESS_LOG)
.description("If HTTP access logging is enabled. By default this will log records in console.")
.defaultValue(Boolean.FALSE)
.build();
public static final Option<String> HTTP_ACCESS_LOG_PATTERN = new OptionBuilder<>("http-access-log-pattern", String.class)
.category(OptionCategory.HTTP_ACCESS_LOG)
.expectedValues("common", "combined", "long")
.strictExpectedValues(false)
.description("The HTTP access log pattern. You can use the available named formats, or use custom format described in Quarkus documentation.")
.defaultValue("common")
.build();
public static final Option<String> HTTP_ACCESS_LOG_EXCLUDE = new OptionBuilder<>("http-access-log-exclude", String.class)
.category(OptionCategory.HTTP_ACCESS_LOG)
.description("A regular expression that can be used to exclude some paths from logging. For instance, '/realms/my-realm/.*' will exclude all subsequent endpoints for realm 'my-realm' from the log.")
.build();
}

View File

@ -10,6 +10,7 @@ public enum OptionCategory {
HOSTNAME_V2("Hostname v2", 50, ConfigSupportLevel.SUPPORTED),
HOSTNAME_V1("Hostname v1", 51, ConfigSupportLevel.DEPRECATED),
HTTP("HTTP(S)", 60, ConfigSupportLevel.SUPPORTED),
HTTP_ACCESS_LOG("HTTP Access log", 61, ConfigSupportLevel.SUPPORTED),
HEALTH("Health", 70, ConfigSupportLevel.SUPPORTED),
MANAGEMENT("Management", 75, ConfigSupportLevel.SUPPORTED),
METRICS("Metrics", 80, ConfigSupportLevel.SUPPORTED),

View File

@ -0,0 +1,30 @@
package org.keycloak.quarkus.runtime.configuration.mappers;
import org.keycloak.config.HttpAccessLogOptions;
import org.keycloak.quarkus.runtime.configuration.Configuration;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
public class HttpAccessLogPropertyMappers {
public static final String ACCESS_LOG_ENABLED_MSG = "HTTP Access log is enabled";
public static PropertyMapper<?>[] getMappers() {
return new PropertyMapper[]{
fromOption(HttpAccessLogOptions.HTTP_ACCESS_LOG_ENABLED)
.to("quarkus.http.access-log.enabled")
.build(),
fromOption(HttpAccessLogOptions.HTTP_ACCESS_LOG_PATTERN)
.isEnabled(HttpAccessLogPropertyMappers::isHttpAccessLogEnabled, ACCESS_LOG_ENABLED_MSG)
.to("quarkus.http.access-log.pattern")
.build(),
fromOption(HttpAccessLogOptions.HTTP_ACCESS_LOG_EXCLUDE)
.isEnabled(HttpAccessLogPropertyMappers::isHttpAccessLogEnabled, ACCESS_LOG_ENABLED_MSG)
.to("quarkus.http.access-log.exclude-pattern")
.build(),
};
}
static boolean isHttpAccessLogEnabled() {
return Configuration.isTrue(HttpAccessLogOptions.HTTP_ACCESS_LOG_ENABLED);
}
}

View File

@ -56,6 +56,7 @@ public final class PropertyMappers {
MAPPERS.addAll(DatabasePropertyMappers.getDatabasePropertyMappers());
MAPPERS.addAll(HostnameV2PropertyMappers.getHostnamePropertyMappers());
MAPPERS.addAll(HttpPropertyMappers.getHttpPropertyMappers());
MAPPERS.addAll(HttpAccessLogPropertyMappers.getMappers());
MAPPERS.addAll(HealthPropertyMappers.getHealthPropertyMappers());
MAPPERS.addAll(ConfigKeystorePropertyMappers.getConfigKeystorePropertyMappers());
MAPPERS.addAll(ManagementPropertyMappers.getManagementPropertyMappers());

View File

@ -5,6 +5,9 @@ quarkus.http.root-path=/
quarkus.application.name=Keycloak
quarkus.banner.enabled=false
# Set Keycloak category for HTTP access log
quarkus.http.access-log.category=org.keycloak.http.access-log
# Enables metrics from other extensions if metrics is enabled
quarkus.datasource.metrics.enabled=${quarkus.micrometer.enabled:false}

View File

@ -877,4 +877,37 @@ public class PicocliTest extends AbstractConfigurationTest {
assertTrue(nonRunningPicocli.reaug);
assertThat(nonRunningPicocli.getOutString(), containsString("The following SPI options"));
}
@Test
public void httpAccessLog() {
// http-access-log-pattern disabled
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start-dev", "--http-access-log-pattern=long");
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
assertThat(nonRunningPicocli.getErrString(), containsString("Available only when HTTP Access log is enabled"));
onAfter();
// http-access-log-pattern disabled
nonRunningPicocli = pseudoLaunch("start-dev", "--http-access-log-exclude=something/");
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
assertThat(nonRunningPicocli.getErrString(), containsString("Available only when HTTP Access log is enabled"));
onAfter();
// accept other patterns - error will be thrown on Quarkus side
nonRunningPicocli = pseudoLaunch("start-dev", "--http-access-log-enabled=true", "--http-access-log-pattern=not-named-pattern");
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
onAfter();
// accept other patterns
nonRunningPicocli = pseudoLaunch("start-dev", "--http-access-log-enabled=true", "--http-access-log-pattern='%r n%{ALL_REQUEST_HEADERS}'");
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
onAfter();
// exclude
nonRunningPicocli = pseudoLaunch("start-dev", "--http-access-log-enabled=true", "--http-access-log-exclude=something.*");
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
onAfter();
nonRunningPicocli = pseudoLaunch("start-dev", "--http-access-log-enabled=true", "--http-access-log-exclude='/realms/my-realm/.*");
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
}
}

View File

@ -482,4 +482,39 @@ public class LoggingConfigurationTest extends AbstractConfigurationTest {
assertConfig("log-%s-async-queue-length".formatted(handlerName), queueLength.toString());
assertExternalConfig("quarkus.log.%s.async.queue-length".formatted(handlerName), queueLength.toString());
}
// HTTP Access log
@Test
public void httpAccessLogDefaults() {
initConfig();
assertConfig(Map.of(
"http-access-log-enabled", "false",
"http-access-log-pattern", "common"
));
assertConfigNull("http-access-log-exclude");
assertExternalConfig(Map.of(
"quarkus.http.access-log.enabled", "false",
"quarkus.http.access-log.pattern", "common"
));
assertExternalConfigNull("quarkus.http.access-log.exclude-pattern");
}
@Test
public void httpAccessLogChanges() {
ConfigArgsConfigSource.setCliArgs("--http-access-log-enabled=true", "--http-access-log-pattern=long", "--http-access-log-exclude=/realms/test/*");
initConfig();
assertConfig(Map.of(
"http-access-log-enabled", "true",
"http-access-log-pattern", "long",
"http-access-log-exclude", "/realms/test/*"
));
assertExternalConfig(Map.of(
"quarkus.http.access-log.enabled", "true",
"quarkus.http.access-log.pattern", "long",
"quarkus.http.access-log.exclude-pattern", "/realms/test/*"
));
}
}

View File

@ -286,4 +286,20 @@ public class LoggingDistTest {
throw new AssertionError("Cannot read default file log", e);
}
}
// HTTP Access log
@Test
@Launch({"start-dev", "--http-access-log-enabled=true", "--http-access-log-pattern='%A %{METHOD} %{REQUEST_URL} %{i,User-Agent}'", "--http-access-log-exclude='/realms/master/clients/.*'"})
void httpAccessLogNotNamedPattern(CLIResult cliResult) {
cliResult.assertStartedDevMode();
when().get("http://127.0.0.1:8080/realms/master/.well-known/openid-configuration").then()
.statusCode(200);
cliResult.assertMessage("[org.keycloak.http.access-log]");
cliResult.assertMessage("127.0.0.1 GET /realms/master/.well-known/openid-configuration");
when().get("http://127.0.0.1:8080/realms/master/clients/account/redirect").then()
.statusCode(200);
cliResult.assertNoMessage("http://127.0.0.1:8080/realms/master/clients/account/redirect");
}
}

View File

@ -139,6 +139,12 @@ Feature:
--features-disabled <feature>
Disables a set of one or more features. Possible values are: <...>.
HTTP Access log:
--http-access-log-enabled <true|false>
If HTTP access logging is enabled. By default this will log records in
console. Default: false.
Management:
--http-management-port <port>

View File

@ -141,6 +141,12 @@ Feature:
--features-disabled <feature>
Disables a set of one or more features. Possible values are: <...>.
HTTP Access log:
--http-access-log-enabled <true|false>
If HTTP access logging is enabled. By default this will log records in
console. Default: false.
Management:
--http-management-port <port>

View File

@ -134,6 +134,12 @@ Feature:
--features-disabled <feature>
Disables a set of one or more features. Possible values are: <...>.
HTTP Access log:
--http-access-log-enabled <true|false>
If HTTP access logging is enabled. By default this will log records in
console. Default: false.
Management:
--http-management-port <port>

View File

@ -134,6 +134,22 @@ Feature:
--features-disabled <feature>
Disables a set of one or more features. Possible values are: <...>.
HTTP Access log:
--http-access-log-enabled <true|false>
If HTTP access logging is enabled. By default this will log records in
console. Default: false.
--http-access-log-exclude <PARAM>
A regular expression that can be used to exclude some paths from logging. For
instance, '/realms/my-realm/.*' will exclude all subsequent endpoints for
realm 'my-realm' from the log. Available only when HTTP Access log is
enabled.
--http-access-log-pattern <PARAM>
The HTTP access log pattern. You can use the available named formats, or use
custom format described in Quarkus documentation. Possible values are:
common, combined, long, or a custom one. Default: common. Available only
when HTTP Access log is enabled.
Management:
--http-management-port <port>

View File

@ -134,6 +134,12 @@ Feature:
--features-disabled <feature>
Disables a set of one or more features. Possible values are: <...>.
HTTP Access log:
--http-access-log-enabled <true|false>
If HTTP access logging is enabled. By default this will log records in
console. Default: false.
Management:
--http-management-port <port>

View File

@ -134,6 +134,22 @@ Feature:
--features-disabled <feature>
Disables a set of one or more features. Possible values are: <...>.
HTTP Access log:
--http-access-log-enabled <true|false>
If HTTP access logging is enabled. By default this will log records in
console. Default: false.
--http-access-log-exclude <PARAM>
A regular expression that can be used to exclude some paths from logging. For
instance, '/realms/my-realm/.*' will exclude all subsequent endpoints for
realm 'my-realm' from the log. Available only when HTTP Access log is
enabled.
--http-access-log-pattern <PARAM>
The HTTP access log pattern. You can use the available named formats, or use
custom format described in Quarkus documentation. Possible values are:
common, combined, long, or a custom one. Default: common. Available only
when HTTP Access log is enabled.
Management:
--http-management-port <port>

View File

@ -258,6 +258,12 @@ HTTP(S):
detected based on the file extension. If 'fips-mode' is set to 'strict' and
no value is set, it defaults to 'BCFKS'.
HTTP Access log:
--http-access-log-enabled <true|false>
If HTTP access logging is enabled. By default this will log records in
console. Default: false.
Health:
--health-enabled <true|false>

View File

@ -334,6 +334,22 @@ HTTP(S):
detected based on the file extension. If 'fips-mode' is set to 'strict' and
no value is set, it defaults to 'BCFKS'.
HTTP Access log:
--http-access-log-enabled <true|false>
If HTTP access logging is enabled. By default this will log records in
console. Default: false.
--http-access-log-exclude <PARAM>
A regular expression that can be used to exclude some paths from logging. For
instance, '/realms/my-realm/.*' will exclude all subsequent endpoints for
realm 'my-realm' from the log. Available only when HTTP Access log is
enabled.
--http-access-log-pattern <PARAM>
The HTTP access log pattern. You can use the available named formats, or use
custom format described in Quarkus documentation. Possible values are:
common, combined, long, or a custom one. Default: common. Available only
when HTTP Access log is enabled.
Health:
--health-enabled <true|false>

View File

@ -306,6 +306,12 @@ HTTP(S):
detected based on the file extension. If 'fips-mode' is set to 'strict' and
no value is set, it defaults to 'BCFKS'.
HTTP Access log:
--http-access-log-enabled <true|false>
If HTTP access logging is enabled. By default this will log records in
console. Default: false.
Health:
--health-enabled <true|false>

View File

@ -335,6 +335,22 @@ HTTP(S):
detected based on the file extension. If 'fips-mode' is set to 'strict' and
no value is set, it defaults to 'BCFKS'.
HTTP Access log:
--http-access-log-enabled <true|false>
If HTTP access logging is enabled. By default this will log records in
console. Default: false.
--http-access-log-exclude <PARAM>
A regular expression that can be used to exclude some paths from logging. For
instance, '/realms/my-realm/.*' will exclude all subsequent endpoints for
realm 'my-realm' from the log. Available only when HTTP Access log is
enabled.
--http-access-log-pattern <PARAM>
The HTTP access log pattern. You can use the available named formats, or use
custom format described in Quarkus documentation. Possible values are:
common, combined, long, or a custom one. Default: common. Available only
when HTTP Access log is enabled.
Health:
--health-enabled <true|false>

View File

@ -272,6 +272,12 @@ HTTP(S):
detected based on the file extension. If 'fips-mode' is set to 'strict' and
no value is set, it defaults to 'BCFKS'.
HTTP Access log:
--http-access-log-enabled <true|false>
If HTTP access logging is enabled. By default this will log records in
console. Default: false.
Management:
--http-management-port <port>

View File

@ -301,6 +301,22 @@ HTTP(S):
detected based on the file extension. If 'fips-mode' is set to 'strict' and
no value is set, it defaults to 'BCFKS'.
HTTP Access log:
--http-access-log-enabled <true|false>
If HTTP access logging is enabled. By default this will log records in
console. Default: false.
--http-access-log-exclude <PARAM>
A regular expression that can be used to exclude some paths from logging. For
instance, '/realms/my-realm/.*' will exclude all subsequent endpoints for
realm 'my-realm' from the log. Available only when HTTP Access log is
enabled.
--http-access-log-pattern <PARAM>
The HTTP access log pattern. You can use the available named formats, or use
custom format described in Quarkus documentation. Possible values are:
common, combined, long, or a custom one. Default: common. Available only
when HTTP Access log is enabled.
Management:
--http-management-port <port>

View File

@ -305,6 +305,12 @@ HTTP(S):
detected based on the file extension. If 'fips-mode' is set to 'strict' and
no value is set, it defaults to 'BCFKS'.
HTTP Access log:
--http-access-log-enabled <true|false>
If HTTP access logging is enabled. By default this will log records in
console. Default: false.
Health:
--health-enabled <true|false>

View File

@ -334,6 +334,22 @@ HTTP(S):
detected based on the file extension. If 'fips-mode' is set to 'strict' and
no value is set, it defaults to 'BCFKS'.
HTTP Access log:
--http-access-log-enabled <true|false>
If HTTP access logging is enabled. By default this will log records in
console. Default: false.
--http-access-log-exclude <PARAM>
A regular expression that can be used to exclude some paths from logging. For
instance, '/realms/my-realm/.*' will exclude all subsequent endpoints for
realm 'my-realm' from the log. Available only when HTTP Access log is
enabled.
--http-access-log-pattern <PARAM>
The HTTP access log pattern. You can use the available named formats, or use
custom format described in Quarkus documentation. Possible values are:
common, combined, long, or a custom one. Default: common. Available only
when HTTP Access log is enabled.
Health:
--health-enabled <true|false>

View File

@ -303,6 +303,12 @@ HTTP(S):
detected based on the file extension. If 'fips-mode' is set to 'strict' and
no value is set, it defaults to 'BCFKS'.
HTTP Access log:
--http-access-log-enabled <true|false>
If HTTP access logging is enabled. By default this will log records in
console. Default: false.
Health:
--health-enabled <true|false>

View File

@ -332,6 +332,22 @@ HTTP(S):
detected based on the file extension. If 'fips-mode' is set to 'strict' and
no value is set, it defaults to 'BCFKS'.
HTTP Access log:
--http-access-log-enabled <true|false>
If HTTP access logging is enabled. By default this will log records in
console. Default: false.
--http-access-log-exclude <PARAM>
A regular expression that can be used to exclude some paths from logging. For
instance, '/realms/my-realm/.*' will exclude all subsequent endpoints for
realm 'my-realm' from the log. Available only when HTTP Access log is
enabled.
--http-access-log-pattern <PARAM>
The HTTP access log pattern. You can use the available named formats, or use
custom format described in Quarkus documentation. Possible values are:
common, combined, long, or a custom one. Default: common. Available only
when HTTP Access log is enabled.
Health:
--health-enabled <true|false>