Add native KC options for JSON log service.name and service.environment fields (#47147)

* Add native KC options for JSON log service.name and service.environment fields

Closes: #47146

Signed-off-by: Daniele Mammarella <dmammare@redhat.com>

* Consolidate per-handler JSON service options into 2 global options

Replace 4 per-handler options (log-console-json-service-name, etc.)
with 2 global options (log-service-name, log-service-environment) that
apply to all log handlers (console, file, syslog).

Signed-off-by: Daniele Mammarella <dmammare@redhat.com>

* Provide logic to obtain value from parent option

Signed-off-by: Martin Bartoš <mabartos@redhat.com>

* Use generateId instead of shortId

Signed-off-by: Martin Bartoš <mabartos@redhat.com>

* Add parent option mappers, default value, and LoggingDistTest for service fields

Register log-service-name and log-service-environment as parent option
mappers so they are recognized as CLI options. Set default value
"keycloak" for log-service-name, consistent with TELEMETRY_SERVICE_NAME.
Add integration test ecsFormatServiceFields to verify ECS JSON logs
contain custom service fields on both console and file handlers.

Signed-off-by: Daniele Mammarella <dmammare@redhat.com>

* Address paramLabels

Co-authored-by: Martin Bartoš <mabartos@redhat.com>
Co-authored-by: Daniele Mammarella <dmammare@redhat.com>
Signed-off-by: Martin Bartoš <mabartos@redhat.com>

* Fix OptionsDistTest to include log-service-name and log-service-environment

Update expected "Possible solutions" strings to include the new
log-service-name and log-service-environment options.

Signed-off-by: Daniele Mammarella <dmammare@redhat.com>

---------

Signed-off-by: Daniele Mammarella <dmammare@redhat.com>
Signed-off-by: Martin Bartoš <mabartos@redhat.com>
Co-authored-by: Martin Bartoš <mabartos@redhat.com>
This commit is contained in:
Daniele Mams 2026-03-15 15:58:38 +01:00 committed by GitHub
parent 5facde53fe
commit f98b94f4fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 195 additions and 3 deletions

View file

@ -72,6 +72,17 @@ public class LoggingOptions {
.description("Indicates whether to log asynchronously to all handlers.")
.build();
public static final Option<String> LOG_SERVICE_NAME = new OptionBuilder<>("log-service-name", String.class)
.category(OptionCategory.LOGGING)
.description("Set the 'service.name' field in JSON log entries for all log handlers.")
.defaultValue("keycloak")
.build();
public static final Option<String> LOG_SERVICE_ENVIRONMENT = new OptionBuilder<>("log-service-environment", String.class)
.category(OptionCategory.LOGGING)
.description("Set the 'service.environment' field in JSON log entries for all log handlers. In ECS format, defaults to the Quarkus profile if not set.")
.build();
public enum Output {
DEFAULT,
JSON;

View file

@ -34,6 +34,7 @@ import static org.keycloak.config.LoggingOptions.LOG_SYSLOG_ENABLED;
import static org.keycloak.quarkus.runtime.configuration.Configuration.isSet;
import static org.keycloak.quarkus.runtime.configuration.Configuration.isTrue;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromParentOption;
public final class LoggingPropertyMappers implements PropertyMapperGrouping {
@ -54,6 +55,12 @@ public final class LoggingPropertyMappers implements PropertyMapperGrouping {
.build(),
fromOption(LoggingOptions.LOG_ASYNC)
.build(),
fromOption(LoggingOptions.LOG_SERVICE_NAME)
.paramLabel("name")
.build(),
fromOption(LoggingOptions.LOG_SERVICE_ENVIRONMENT)
.paramLabel("environment")
.build(),
// Console
fromOption(LoggingOptions.LOG_CONSOLE_OUTPUT)
.isEnabled(LoggingPropertyMappers::isConsoleEnabled, CONSOLE_ENABLED_MSG)
@ -78,6 +85,14 @@ public final class LoggingPropertyMappers implements PropertyMapperGrouping {
.to("quarkus.log.console.json.log-format")
.paramLabel("format")
.build(),
fromParentOption(LoggingOptions.LOG_SERVICE_NAME)
.isEnabled(LoggingPropertyMappers::isConsoleJsonEnabled, "%s and output is set to 'json'".formatted(CONSOLE_ENABLED_MSG))
.to("quarkus.log.console.json.additional-field.\"service.name\".value")
.build(),
fromParentOption(LoggingOptions.LOG_SERVICE_ENVIRONMENT)
.isEnabled(LoggingPropertyMappers::isConsoleJsonEnabled, "%s and output is set to 'json'".formatted(CONSOLE_ENABLED_MSG))
.to("quarkus.log.console.json.additional-field.\"service.environment\".value")
.build(),
fromOption(LoggingOptions.LOG_CONSOLE_INCLUDE_TRACE)
.isEnabled(() -> LoggingPropertyMappers.isConsoleEnabled() && TracingPropertyMappers.isTracingEnabled(),
"Console log handler and Tracing is activated")
@ -134,6 +149,14 @@ public final class LoggingPropertyMappers implements PropertyMapperGrouping {
.to("quarkus.log.file.json.log-format")
.paramLabel("format")
.build(),
fromParentOption(LoggingOptions.LOG_SERVICE_NAME)
.isEnabled(LoggingPropertyMappers::isFileJsonEnabled, FILE_ENABLED_MSG + " and output is set to 'json'")
.to("quarkus.log.file.json.additional-field.\"service.name\".value")
.build(),
fromParentOption(LoggingOptions.LOG_SERVICE_ENVIRONMENT)
.isEnabled(LoggingPropertyMappers::isFileJsonEnabled, FILE_ENABLED_MSG + " and output is set to 'json'")
.to("quarkus.log.file.json.additional-field.\"service.environment\".value")
.build(),
fromOption(LoggingOptions.LOG_FILE_INCLUDE_TRACE)
.isEnabled(() -> LoggingPropertyMappers.isFileEnabled() && TracingPropertyMappers.isTracingEnabled(),
"File log handler and Tracing is activated")
@ -246,6 +269,14 @@ public final class LoggingPropertyMappers implements PropertyMapperGrouping {
.to("quarkus.log.syslog.json.log-format")
.paramLabel("format")
.build(),
fromParentOption(LoggingOptions.LOG_SERVICE_NAME)
.isEnabled(LoggingPropertyMappers::isSyslogJsonEnabled, SYSLOG_ENABLED_MSG + " and output is set to 'json'")
.to("quarkus.log.syslog.json.additional-field.\"service.name\".value")
.build(),
fromParentOption(LoggingOptions.LOG_SERVICE_ENVIRONMENT)
.isEnabled(LoggingPropertyMappers::isSyslogJsonEnabled, SYSLOG_ENABLED_MSG + " and output is set to 'json'")
.to("quarkus.log.syslog.json.additional-field.\"service.environment\".value")
.build(),
fromOption(LoggingOptions.LOG_SYSLOG_INCLUDE_TRACE)
.isEnabled(() -> LoggingPropertyMappers.isSyslogEnabled() && TracingPropertyMappers.isTracingEnabled(),
"Syslog handler and Tracing is activated")

View file

@ -36,6 +36,7 @@ import org.keycloak.config.Option;
import org.keycloak.config.OptionBuilder;
import org.keycloak.config.OptionCategory;
import org.keycloak.config.WildcardOptionsUtil;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.quarkus.runtime.cli.PropertyException;
import org.keycloak.quarkus.runtime.cli.ShortErrorMessageHandler;
import org.keycloak.quarkus.runtime.cli.command.AbstractCommand;
@ -573,6 +574,18 @@ public class PropertyMapper<T> {
return new PropertyMapper.Builder<>(opt);
}
/**
* Create a property mapper that get value from the parent option
*/
public static <T> PropertyMapper.Builder<T> fromParentOption(Option<T> parentOption) {
final var option = new OptionBuilder<>(parentOption.getKey() + KeycloakModelUtils.generateId(), parentOption.getType())
.buildTime(parentOption.isBuildTime())
.hidden()
.build();
return new Builder<>(option)
.mapFrom(parentOption);
}
/**
* Create a property mapper from a feature.
* The mapper maps to external properties the state of the feature.

View file

@ -305,6 +305,34 @@ public class LoggingConfigurationTest extends AbstractConfigurationTest {
));
}
@Test
public void jsonServiceFields() {
putEnvVars(Map.of(
"KC_LOG", "console,file,syslog",
"KC_LOG_CONSOLE_OUTPUT", "json",
"KC_LOG_FILE_OUTPUT", "json",
"KC_LOG_SYSLOG_OUTPUT", "json",
"KC_LOG_SERVICE_NAME", "my-service",
"KC_LOG_SERVICE_ENVIRONMENT", "production"
));
initConfig();
assertConfig(Map.of(
"log-service-name", "my-service",
"log-service-environment", "production"
));
assertExternalConfig(Map.of(
"quarkus.log.console.json.additional-field.\"service.name\".value", "my-service",
"quarkus.log.console.json.additional-field.\"service.environment\".value", "production",
"quarkus.log.file.json.additional-field.\"service.name\".value", "my-service",
"quarkus.log.file.json.additional-field.\"service.environment\".value", "production",
"quarkus.log.syslog.json.additional-field.\"service.name\".value", "my-service",
"quarkus.log.syslog.json.additional-field.\"service.environment\".value", "production"
));
}
@Test
public void testWildcardCliOptionCanBeMappedToQuarkusOption() {
ConfigArgsConfigSource.setCliArgs("--log-level-org.keycloak=trace");

View file

@ -274,6 +274,19 @@ public class LoggingDistTest {
assertThat(data, containsString("@timestamp"));
}
@Test
@Launch({"start-dev", "--log=console,file", "--log-console-output=json", "--log-console-json-format=ecs", "--log-file-output=json", "--log-file-json-format=ecs", "--log-service-name=my-custom-service", "--log-service-environment=my-custom-env"})
void ecsFormatServiceFields(CLIResult cliResult, RawDistRootPath path) {
var output = cliResult.getOutput();
assertThat(output, containsString("\"service.name\":\"my-custom-service\""));
assertThat(output, containsString("\"service.environment\":\"my-custom-env\""));
String data = readDefaultFileLog(path);
assertThat(data, containsString("\"service.name\":\"my-custom-service\""));
assertThat(data, containsString("\"service.environment\":\"my-custom-env\""));
}
@Test
@Launch({"start-dev", "--log-async=true"})
void asyncLogging(CLIResult cliResult) {

View file

@ -60,7 +60,7 @@ public class OptionsDistTest {
@Launch({"start", "--db=dev-file", "--log=console", "--log-file-output=json", "--http-enabled=true", "--hostname-strict=false"})
public void testServerDoesNotStartIfDisabledFileLogOption(CLIResult result) {
result.assertError("Disabled option: '--log-file-output'. Available only when File log handler is activated");
result.assertError("--log, --log-async, --log-console-output, --log-console-level, --log-console-format, --log-console-color, --log-console-async, --log-level, --log-level-<category>");
result.assertError("--log, --log-async, --log-service-name, --log-service-environment, --log-console-output, --log-console-level, --log-console-format, --log-console-color, --log-console-async, --log-level, --log-level-<category>");
}
@DryRun
@ -114,7 +114,7 @@ public class OptionsDistTest {
@Launch({"start-dev", "--log=console", "--log-file-output=json"})
public void testServerDoesNotStartDevIfDisabledFileLogOption(CLIResult result) {
result.assertError("Disabled option: '--log-file-output'. Available only when File log handler is activated");
result.assertError("Possible solutions: --log, --log-async, --log-console-output, --log-console-level, --log-console-format, --log-console-color, --log-console-async, --log-level, --log-level-<category>");
result.assertError("Possible solutions: --log, --log-async, --log-service-name, --log-service-environment, --log-console-output, --log-console-level, --log-console-format, --log-console-color, --log-console-async, --log-level, --log-level-<category>");
}
@DryRun
@ -124,7 +124,7 @@ public class OptionsDistTest {
public void testServerStartDevIfEnabledFileLogOption(CLIResult result) {
result.assertNoError("Disabled option: '--log-file-output'. Available only when File log handler is activated");
result.assertError("Disabled option: '--log-console-color'. Available only when Console log handler is activated");
result.assertError("Possible solutions: --log, --log-async, --log-file, --log-file-level, --log-file-format, --log-file-json-format, --log-file-output, --log-file-async, --log-file-rotation-enabled, --log-file-rotation-max-file-size, --log-file-rotation-max-backup-index, --log-file-rotation-file-suffix, --log-file-rotation-rotate-on-boot, --log-level, --log-level-<category>, --log-mdc-enabled");
result.assertError("Possible solutions: --log, --log-async, --log-service-name, --log-service-environment, --log-file, --log-file-level, --log-file-format, --log-file-json-format, --log-file-output, --log-file-async, --log-file-rotation-enabled, --log-file-rotation-max-file-size, --log-file-rotation-max-backup-index, --log-file-rotation-file-suffix, --log-file-rotation-rotate-on-boot, --log-level, --log-level-<category>, --log-mdc-enabled");
}
@DryRun

View file

@ -238,6 +238,12 @@ Logging:
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-service-environment <environment>
Set the 'service.environment' field in JSON log entries for all log handlers.
In ECS format, defaults to the Quarkus profile if not set.
--log-service-name <name>
Set the 'service.name' field in JSON log entries for all log handlers.
Default: keycloak.
Tracing:

View file

@ -240,6 +240,12 @@ Logging:
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-service-environment <environment>
Set the 'service.environment' field in JSON log entries for all log handlers.
In ECS format, defaults to the Quarkus profile if not set.
--log-service-name <name>
Set the 'service.name' field in JSON log entries for all log handlers.
Default: keycloak.
Tracing:

View file

@ -233,6 +233,12 @@ Logging:
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-service-environment <environment>
Set the 'service.environment' field in JSON log entries for all log handlers.
In ECS format, defaults to the Quarkus profile if not set.
--log-service-name <name>
Set the 'service.name' field in JSON log entries for all log handlers.
Default: keycloak.
Tracing:

View file

@ -312,6 +312,12 @@ Logging:
ipAddress, org, sessionId, authenticationSessionId, authenticationTabId.
Default: realmName,clientId,org,sessionId,authenticationSessionId,
authenticationTabId. Available only when MDC logging is enabled.
--log-service-environment <environment>
Set the 'service.environment' field in JSON log entries for all log handlers.
In ECS format, defaults to the Quarkus profile if not set.
--log-service-name <name>
Set the 'service.name' field in JSON log entries for all log handlers.
Default: keycloak.
--log-syslog-app-name <name>
Set the app name used when formatting the message in RFC5424 format. Default:
keycloak. Available only when Syslog is activated.

View file

@ -233,6 +233,12 @@ Logging:
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-service-environment <environment>
Set the 'service.environment' field in JSON log entries for all log handlers.
In ECS format, defaults to the Quarkus profile if not set.
--log-service-name <name>
Set the 'service.name' field in JSON log entries for all log handlers.
Default: keycloak.
Tracing:

View file

@ -312,6 +312,12 @@ Logging:
ipAddress, org, sessionId, authenticationSessionId, authenticationTabId.
Default: realmName,clientId,org,sessionId,authenticationSessionId,
authenticationTabId. Available only when MDC logging is enabled.
--log-service-environment <environment>
Set the 'service.environment' field in JSON log entries for all log handlers.
In ECS format, defaults to the Quarkus profile if not set.
--log-service-name <name>
Set the 'service.name' field in JSON log entries for all log handlers.
Default: keycloak.
--log-syslog-app-name <name>
Set the app name used when formatting the message in RFC5424 format. Default:
keycloak. Available only when Syslog is activated.

View file

@ -478,6 +478,12 @@ Logging:
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-service-environment <environment>
Set the 'service.environment' field in JSON log entries for all log handlers.
In ECS format, defaults to the Quarkus profile if not set.
--log-service-name <name>
Set the 'service.name' field in JSON log entries for all log handlers.
Default: keycloak.
Tracing:

View file

@ -677,6 +677,12 @@ Logging:
ipAddress, org, sessionId, authenticationSessionId, authenticationTabId.
Default: realmName,clientId,org,sessionId,authenticationSessionId,
authenticationTabId. Available only when MDC logging is enabled.
--log-service-environment <environment>
Set the 'service.environment' field in JSON log entries for all log handlers.
In ECS format, defaults to the Quarkus profile if not set.
--log-service-name <name>
Set the 'service.name' field in JSON log entries for all log handlers.
Default: keycloak.
--log-syslog-app-name <name>
Set the app name used when formatting the message in RFC5424 format. Default:
keycloak. Available only when Syslog is activated.

View file

@ -526,6 +526,12 @@ Logging:
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-service-environment <environment>
Set the 'service.environment' field in JSON log entries for all log handlers.
In ECS format, defaults to the Quarkus profile if not set.
--log-service-name <name>
Set the 'service.name' field in JSON log entries for all log handlers.
Default: keycloak.
Tracing:

View file

@ -678,6 +678,12 @@ Logging:
ipAddress, org, sessionId, authenticationSessionId, authenticationTabId.
Default: realmName,clientId,org,sessionId,authenticationSessionId,
authenticationTabId. Available only when MDC logging is enabled.
--log-service-environment <environment>
Set the 'service.environment' field in JSON log entries for all log handlers.
In ECS format, defaults to the Quarkus profile if not set.
--log-service-name <name>
Set the 'service.name' field in JSON log entries for all log handlers.
Default: keycloak.
--log-syslog-app-name <name>
Set the app name used when formatting the message in RFC5424 format. Default:
keycloak. Available only when Syslog is activated.

View file

@ -459,6 +459,12 @@ 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-service-environment <environment>
Set the 'service.environment' field in JSON log entries for all log handlers.
In ECS format, defaults to the Quarkus profile if not set.
--log-service-name <name>
Set the 'service.name' field in JSON log entries for all log handlers.
Default: keycloak.
Truststore:

View file

@ -607,6 +607,12 @@ Logging:
ipAddress, org, sessionId, authenticationSessionId, authenticationTabId.
Default: realmName,clientId,org,sessionId,authenticationSessionId,
authenticationTabId. Available only when MDC logging is enabled.
--log-service-environment <environment>
Set the 'service.environment' field in JSON log entries for all log handlers.
In ECS format, defaults to the Quarkus profile if not set.
--log-service-name <name>
Set the 'service.name' field in JSON log entries for all log handlers.
Default: keycloak.
--log-syslog-app-name <name>
Set the app name used when formatting the message in RFC5424 format. Default:
keycloak. Available only when Syslog is activated.

View file

@ -525,6 +525,12 @@ Logging:
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-service-environment <environment>
Set the 'service.environment' field in JSON log entries for all log handlers.
In ECS format, defaults to the Quarkus profile if not set.
--log-service-name <name>
Set the 'service.name' field in JSON log entries for all log handlers.
Default: keycloak.
Tracing:

View file

@ -677,6 +677,12 @@ Logging:
ipAddress, org, sessionId, authenticationSessionId, authenticationTabId.
Default: realmName,clientId,org,sessionId,authenticationSessionId,
authenticationTabId. Available only when MDC logging is enabled.
--log-service-environment <environment>
Set the 'service.environment' field in JSON log entries for all log handlers.
In ECS format, defaults to the Quarkus profile if not set.
--log-service-name <name>
Set the 'service.name' field in JSON log entries for all log handlers.
Default: keycloak.
--log-syslog-app-name <name>
Set the app name used when formatting the message in RFC5424 format. Default:
keycloak. Available only when Syslog is activated.

View file

@ -523,6 +523,12 @@ Logging:
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-service-environment <environment>
Set the 'service.environment' field in JSON log entries for all log handlers.
In ECS format, defaults to the Quarkus profile if not set.
--log-service-name <name>
Set the 'service.name' field in JSON log entries for all log handlers.
Default: keycloak.
Tracing:

View file

@ -675,6 +675,12 @@ Logging:
ipAddress, org, sessionId, authenticationSessionId, authenticationTabId.
Default: realmName,clientId,org,sessionId,authenticationSessionId,
authenticationTabId. Available only when MDC logging is enabled.
--log-service-environment <environment>
Set the 'service.environment' field in JSON log entries for all log handlers.
In ECS format, defaults to the Quarkus profile if not set.
--log-service-name <name>
Set the 'service.name' field in JSON log entries for all log handlers.
Default: keycloak.
--log-syslog-app-name <name>
Set the app name used when formatting the message in RFC5424 format. Default:
keycloak. Available only when Syslog is activated.