Missing Quarkus flag for Syslog counting framing (#40621)

* Missing Quarkus flag for Syslog counting framing

Closes #39893

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

* Remove handling of mapFrom

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

* Add notable change

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

---------

Signed-off-by: Martin Bartoš <mabartos@redhat.com>
This commit is contained in:
Martin Bartoš 2025-06-25 17:36:41 +02:00 committed by GitHub
parent a50d15be05
commit d475c5aecc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 167 additions and 4 deletions

View File

@ -123,6 +123,12 @@ Previously the default *browser* flow had a *Browser - Conditional OTP* conditio
Upgraded realms will not be changed. The updated flow will only be available for new realms. Take this change into consideration if you have automated the realm creation.
=== Syslog counting framing now enabled based on protocol
Syslog messages sent over `tcp` (or `ssl-tcp`) protocol now use counting framing by default, prefixing messages with their size as required by some Syslog servers.
To change this behavior, use the `--log-syslog-counting-framing` option with one of the following values: `protocol-dependent` (default), `true`, or `false`.
== Deprecated features
The following sections provide details on deprecated features.

View File

@ -389,6 +389,23 @@ To use UDP instead of TCP, add the `--log-syslog-protocol` option as follows:
The available protocols are: `tpc`, `udp`, and `ssl-tcp`.
=== Configuring the Syslog counting framing
By default, Syslog messages sent over TCP or SSL-TCP are prefixed with the message size, as required by certain Syslog receivers.
This behavior is controlled by the `--log-syslog-counting-framing` option.
To explicitly enable or disable this feature, use the following command:
<@kc.start parameters="--log-syslog-counting-framing=true"/>
You can set the value to one of the following:
* `protocol-dependent` (default) Enable counting framing only when the `log-syslog-protocol` is `tcp` or `ssl-tcp`.
* `true` Always enable counting framing by prefixing messages with their size.
* `false` Never use counting framing.
Note that using `protocol-dependent` ensures compatibility with most Syslog servers by enabling the prefix only when required by the protocol.
=== Configuring the Syslog log format
To set the logging format for a logged line, perform these steps:

View File

@ -279,6 +279,16 @@ public class LoggingOptions {
.description("Set the Syslog output to JSON or default (plain) unstructured logging.")
.build();
// we can use SyslogConfig.CountingFraming type once https://github.com/quarkusio/quarkus/pull/48479 is present
public static final String SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT = "protocol-dependent";
public static final Option<String> LOG_SYSLOG_COUNTING_FRAMING = new OptionBuilder<>("log-syslog-counting-framing", String.class)
.category(OptionCategory.LOGGING)
.expectedValues(Boolean.TRUE.toString(), Boolean.FALSE.toString(), SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT)
.defaultValue(SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT)
.description("If 'true', the message being sent is prefixed with the size of the message. If '%s', the default value is 'true' when '%s' is 'tcp' or 'ssl-tcp', otherwise 'false'."
.formatted(SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT, LOG_SYSLOG_PROTOCOL.getKey()))
.build();
// Syslog async
public static final Option<Boolean> LOG_SYSLOG_ASYNC = new OptionBuilder<>("log-syslog-async", Boolean.class)
.category(OptionCategory.LOGGING)

View File

@ -4,6 +4,7 @@ import static org.keycloak.config.LoggingOptions.DEFAULT_LOG_FORMAT;
import static org.keycloak.config.LoggingOptions.LOG_CONSOLE_ENABLED;
import static org.keycloak.config.LoggingOptions.LOG_FILE_ENABLED;
import static org.keycloak.config.LoggingOptions.LOG_SYSLOG_ENABLED;
import static org.keycloak.config.LoggingOptions.SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT;
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;
@ -215,6 +216,12 @@ public final class LoggingPropertyMappers {
.paramLabel("output")
.transformer(LoggingPropertyMappers::resolveLogOutput)
.build(),
fromOption(LoggingOptions.LOG_SYSLOG_COUNTING_FRAMING)
.isEnabled(LoggingPropertyMappers::isSyslogEnabled, SYSLOG_ENABLED_MSG)
.transformer(LoggingPropertyMappers::resolveSyslogCountingFraming)
.to("quarkus.log.syslog.use-counting-framing")
.paramLabel("strategy")
.build(),
// Syslog async
fromOption(LoggingOptions.LOG_SYSLOG_ASYNC)
.mapFrom(LoggingOptions.LOG_ASYNC)
@ -411,4 +418,20 @@ public final class LoggingPropertyMappers {
throw new PropertyException(String.format("Invalid value for option '--log-syslog-max-length': %s", e.getMessage()));
}
}
// Workaround BEGIN - for https://github.com/keycloak/keycloak/issues/39893
// Remove once the https://github.com/quarkusio/quarkus/issues/48036 is included in Keycloak as Quarkus might handle it on its own
private static String resolveSyslogCountingFraming(String value, ConfigSourceInterceptorContext context) {
if (SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT.equals(value)) {
return Configuration.getOptionalKcValue(LoggingOptions.LOG_SYSLOG_PROTOCOL)
.map(protocol -> switch (protocol) {
case "tcp", "ssl-tcp" -> Boolean.TRUE.toString();
case "udp" -> Boolean.FALSE.toString();
default -> throw new PropertyException("Invalid Syslog protocol: " + protocol);
})
.orElse(Boolean.FALSE.toString());
}
return value;
}
// Workaround END
}

View File

@ -19,6 +19,7 @@ package org.keycloak.quarkus.runtime.cli;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
@ -488,6 +489,31 @@ public class PicocliTest extends AbstractConfigurationTest {
"Invalid value for option '--log-syslog-max-length': value wrong not in correct format (regular expression): [0-9]+[BbKkMmGgTtPpEeZzYy]?"));
}
@Test
public void syslogCountingFraming() {
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start-dev", "--log=syslog", "--log-syslog-counting-framing=TRUE");
assertThat(nonRunningPicocli.exitCode, is(CommandLine.ExitCode.USAGE));
assertThat(nonRunningPicocli.getErrString(), containsString(
"Invalid value for option '--log-syslog-counting-framing': TRUE. Expected values are: true, false, protocol-dependent"));
nonRunningPicocli = pseudoLaunch("start-dev", "--log=syslog", "--log-syslog-counting-framing=true");
assertThat(nonRunningPicocli.exitCode, is(CommandLine.ExitCode.OK));
assertThat(nonRunningPicocli.config.getConfigValue("quarkus.log.syslog.use-counting-framing").getValue(), is("true"));
nonRunningPicocli = pseudoLaunch("start-dev", "--log=syslog", "--log-syslog-counting-framing=false");
assertThat(nonRunningPicocli.exitCode, is(CommandLine.ExitCode.OK));
assertThat(nonRunningPicocli.config.getConfigValue("quarkus.log.syslog.use-counting-framing").getValue(), is("false"));
nonRunningPicocli = pseudoLaunch("start-dev", "--log=syslog", "--log-syslog-protocol=ssl-tcp", "--log-syslog-counting-framing=protocol-dependent");
assertThat(nonRunningPicocli.exitCode, is(CommandLine.ExitCode.OK));
assertThat(nonRunningPicocli.config.getConfigValue("quarkus.log.syslog.use-counting-framing").getValue(), is("true"));
nonRunningPicocli = pseudoLaunch("start-dev", "--log=syslog", "--log-syslog-counting-framing=wrong");
assertThat(nonRunningPicocli.exitCode, is(CommandLine.ExitCode.USAGE));
assertThat(nonRunningPicocli.getErrString(), containsString(
"Invalid value for option '--log-syslog-counting-framing': wrong. Expected values are: true, false, protocol-dependent"));
}
@Test
public void providerChanged() {
build("build", "--db=dev-file");

View File

@ -17,13 +17,16 @@
package org.keycloak.quarkus.runtime.configuration;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.keycloak.config.LoggingOptions.DEFAULT_LOG_FORMAT;
import static org.keycloak.config.LoggingOptions.DEFAULT_SYSLOG_OUTPUT;
import static org.keycloak.config.LoggingOptions.SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT;
import java.util.Map;
import java.util.Set;
@ -35,9 +38,6 @@ import org.junit.Test;
import org.keycloak.config.LoggingOptions;
import org.keycloak.quarkus.runtime.Environment;
import org.keycloak.quarkus.runtime.cli.PropertyException;
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
import org.keycloak.quarkus.runtime.configuration.Configuration;
import io.smallrye.config.SmallRyeConfig;
public class LoggingConfigurationTest extends AbstractConfigurationTest {
@ -86,7 +86,8 @@ public class LoggingConfigurationTest extends AbstractConfigurationTest {
"log-syslog-app-name", "keycloak",
"log-syslog-protocol", "tcp",
"log-syslog-format", DEFAULT_LOG_FORMAT,
"log-syslog-output", DEFAULT_SYSLOG_OUTPUT.toString()
"log-syslog-output", DEFAULT_SYSLOG_OUTPUT.toString(),
"log-syslog-counting-framing", SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT
));
assertThat(Configuration.getOptionalKcValue(LoggingOptions.LOG_SYSLOG_MAX_LENGTH).orElse(null), CoreMatchers.nullValue());
@ -96,6 +97,7 @@ public class LoggingConfigurationTest extends AbstractConfigurationTest {
"quarkus.log.syslog.syslog-type", "rfc5424",
"quarkus.log.syslog.app-name", "keycloak",
"quarkus.log.syslog.protocol", "tcp",
"quarkus.log.syslog.use-counting-framing", "true",
"quarkus.log.syslog.format", DEFAULT_LOG_FORMAT,
"quarkus.log.syslog.json.enabled", "false"
));
@ -113,6 +115,7 @@ public class LoggingConfigurationTest extends AbstractConfigurationTest {
"KC_LOG_SYSLOG_MAX_LENGTH", "4096",
"KC_LOG_SYSLOG_APP_NAME", "keycloak2",
"KC_LOG_SYSLOG_PROTOCOL", "udp",
"KC_LOG_SYSLOG_COUNTING_FRAMING", "false",
"KC_LOG_SYSLOG_FORMAT", "some format",
"KC_LOG_SYSLOG_OUTPUT", "json"
));
@ -126,6 +129,7 @@ public class LoggingConfigurationTest extends AbstractConfigurationTest {
"log-syslog-max-length", "4096",
"log-syslog-app-name", "keycloak2",
"log-syslog-protocol", "udp",
"log-syslog-counting-framing", "false",
"log-syslog-format", "some format",
"log-syslog-output", "json"
));
@ -137,6 +141,7 @@ public class LoggingConfigurationTest extends AbstractConfigurationTest {
"quarkus.log.syslog.max-length", "4096",
"quarkus.log.syslog.app-name", "keycloak2",
"quarkus.log.syslog.protocol", "udp",
"quarkus.log.syslog.use-counting-framing", "false",
"quarkus.log.syslog.format", "some format",
"quarkus.log.syslog.json.enabled", "true"
));
@ -168,6 +173,40 @@ public class LoggingConfigurationTest extends AbstractConfigurationTest {
assertExternalConfig("quarkus.log.syslog.max-length", "512");
}
@Test
public void syslogCountingFraming() {
assertSyslogCountingFramingProtocolDependent("tcp", true);
assertSyslogCountingFramingProtocolDependent("udp", false);
assertSyslogCountingFramingProtocolDependent("ssl-tcp", true);
try {
assertSyslogCountingFramingProtocolDependent("error", false);
fail("Wrong protocol name should throw an error");
} catch (PropertyException expected) {
assertThat(expected.getMessage(), containsString("Invalid Syslog protocol: error"));
}
}
protected void assertSyslogCountingFramingProtocolDependent(String protocol, boolean expectedCountingFraming) {
putEnvVars(Map.of(
"KC_LOG", "syslog",
"KC_LOG_SYSLOG_PROTOCOL", protocol
));
initConfig();
assertConfig(Map.of(
"log-syslog-enabled", "true",
"log-syslog-protocol", protocol,
"log-syslog-counting-framing", SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT
));
assertExternalConfig(Map.of(
"quarkus.log.syslog.enable", "true",
"quarkus.log.syslog.protocol", protocol,
"quarkus.log.syslog.use-counting-framing", Boolean.toString(expectedCountingFraming)
));
onAfter();
}
@Test
public void logLevelsHandlers() {
putEnvVars(Map.of(

View File

@ -273,6 +273,12 @@ Logging:
The queue length to use before flushing writing when logging to Syslog.
Default: 512. Available only when Syslog is activated and asynchronous
logging is enabled.
--log-syslog-counting-framing <strategy>
If 'true', the message being sent is prefixed with the size of the message. If
'protocol-dependent', the default value is 'true' when 'log-syslog-protocol'
is 'tcp' or 'ssl-tcp', otherwise 'false'. Possible values are: true, false,
protocol-dependent. Default: protocol-dependent. Available only when Syslog
is activated.
--log-syslog-endpoint <host:port>
Set the IP address and port of the Syslog server. Default: localhost:514.
Available only when Syslog is activated.

View File

@ -273,6 +273,12 @@ Logging:
The queue length to use before flushing writing when logging to Syslog.
Default: 512. Available only when Syslog is activated and asynchronous
logging is enabled.
--log-syslog-counting-framing <strategy>
If 'true', the message being sent is prefixed with the size of the message. If
'protocol-dependent', the default value is 'true' when 'log-syslog-protocol'
is 'tcp' or 'ssl-tcp', otherwise 'false'. Possible values are: true, false,
protocol-dependent. Default: protocol-dependent. Available only when Syslog
is activated.
--log-syslog-endpoint <host:port>
Set the IP address and port of the Syslog server. Default: localhost:514.
Available only when Syslog is activated.

View File

@ -488,6 +488,12 @@ Logging:
The queue length to use before flushing writing when logging to Syslog.
Default: 512. Available only when Syslog is activated and asynchronous
logging is enabled.
--log-syslog-counting-framing <strategy>
If 'true', the message being sent is prefixed with the size of the message. If
'protocol-dependent', the default value is 'true' when 'log-syslog-protocol'
is 'tcp' or 'ssl-tcp', otherwise 'false'. Possible values are: true, false,
protocol-dependent. Default: protocol-dependent. Available only when Syslog
is activated.
--log-syslog-endpoint <host:port>
Set the IP address and port of the Syslog server. Default: localhost:514.
Available only when Syslog is activated.

View File

@ -489,6 +489,12 @@ Logging:
The queue length to use before flushing writing when logging to Syslog.
Default: 512. Available only when Syslog is activated and asynchronous
logging is enabled.
--log-syslog-counting-framing <strategy>
If 'true', the message being sent is prefixed with the size of the message. If
'protocol-dependent', the default value is 'true' when 'log-syslog-protocol'
is 'tcp' or 'ssl-tcp', otherwise 'false'. Possible values are: true, false,
protocol-dependent. Default: protocol-dependent. Available only when Syslog
is activated.
--log-syslog-endpoint <host:port>
Set the IP address and port of the Syslog server. Default: localhost:514.
Available only when Syslog is activated.

View File

@ -427,6 +427,12 @@ Logging:
The queue length to use before flushing writing when logging to Syslog.
Default: 512. Available only when Syslog is activated and asynchronous
logging is enabled.
--log-syslog-counting-framing <strategy>
If 'true', the message being sent is prefixed with the size of the message. If
'protocol-dependent', the default value is 'true' when 'log-syslog-protocol'
is 'tcp' or 'ssl-tcp', otherwise 'false'. Possible values are: true, false,
protocol-dependent. Default: protocol-dependent. Available only when Syslog
is activated.
--log-syslog-endpoint <host:port>
Set the IP address and port of the Syslog server. Default: localhost:514.
Available only when Syslog is activated.

View File

@ -488,6 +488,12 @@ Logging:
The queue length to use before flushing writing when logging to Syslog.
Default: 512. Available only when Syslog is activated and asynchronous
logging is enabled.
--log-syslog-counting-framing <strategy>
If 'true', the message being sent is prefixed with the size of the message. If
'protocol-dependent', the default value is 'true' when 'log-syslog-protocol'
is 'tcp' or 'ssl-tcp', otherwise 'false'. Possible values are: true, false,
protocol-dependent. Default: protocol-dependent. Available only when Syslog
is activated.
--log-syslog-endpoint <host:port>
Set the IP address and port of the Syslog server. Default: localhost:514.
Available only when Syslog is activated.

View File

@ -486,6 +486,12 @@ Logging:
The queue length to use before flushing writing when logging to Syslog.
Default: 512. Available only when Syslog is activated and asynchronous
logging is enabled.
--log-syslog-counting-framing <strategy>
If 'true', the message being sent is prefixed with the size of the message. If
'protocol-dependent', the default value is 'true' when 'log-syslog-protocol'
is 'tcp' or 'ssl-tcp', otherwise 'false'. Possible values are: true, false,
protocol-dependent. Default: protocol-dependent. Available only when Syslog
is activated.
--log-syslog-endpoint <host:port>
Set the IP address and port of the Syslog server. Default: localhost:514.
Available only when Syslog is activated.