From b98d74f0e602a983f6fabf5ef7d7dbb53151b195 Mon Sep 17 00:00:00 2001 From: hojooo Date: Thu, 18 Sep 2025 00:19:09 +0900 Subject: [PATCH 1/7] Add Log4j2 rolling policy configuration support Signed-off-by: hojooo --- .../logging/log4j2/Log4J2LoggingSystem.java | 6 +- .../log4j2/Log4j2LoggingSystemProperties.java | 119 ++++++++++++++++++ .../Log4j2RollingPolicySystemProperty.java | 81 ++++++++++++ ...itional-spring-configuration-metadata.json | 35 ++++++ .../boot/logging/log4j2/log4j2-file.xml | 12 +- .../logging/AbstractLoggingSystemTests.java | 4 + .../log4j2/Log4J2LoggingSystemTests.java | 39 ++++++ .../Log4j2LoggingSystemPropertiesTests.java | 118 +++++++++++++++++ 8 files changed, 409 insertions(+), 5 deletions(-) create mode 100644 core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4j2LoggingSystemProperties.java create mode 100644 core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4j2RollingPolicySystemProperty.java create mode 100644 core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4j2LoggingSystemPropertiesTests.java diff --git a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java index a3fdea3e978f..0ad4b8b02437 100644 --- a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java +++ b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java @@ -324,10 +324,14 @@ private void load(LoggingInitializationContext initializationContext, String loc List overrides = getOverrides(initializationContext); Environment environment = initializationContext.getEnvironment(); Assert.state(environment != null, "'environment' must not be null"); - applySystemProperties(environment, logFile); + applyLog4j2SystemProperties(environment, logFile); loadConfiguration(location, logFile, overrides); } + private void applyLog4j2SystemProperties(Environment environment, @Nullable LogFile logFile) { + new Log4j2LoggingSystemProperties(environment, getDefaultValueResolver(environment), null).apply(logFile); + } + private List getOverrides(LoggingInitializationContext initializationContext) { Environment environment = initializationContext.getEnvironment(); Assert.state(environment != null, "'environment' must not be null"); diff --git a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4j2LoggingSystemProperties.java b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4j2LoggingSystemProperties.java new file mode 100644 index 000000000000..9161c014f2ef --- /dev/null +++ b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4j2LoggingSystemProperties.java @@ -0,0 +1,119 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.logging.log4j2; + +import java.util.function.BiConsumer; +import java.util.function.Function; + +import org.jspecify.annotations.Nullable; + +import org.springframework.boot.logging.LogFile; +import org.springframework.boot.logging.LoggingSystemProperties; +import org.springframework.core.convert.ConversionFailedException; +import org.springframework.core.convert.ConverterNotFoundException; +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertyResolver; +import org.springframework.util.unit.DataSize; + +/** + * {@link LoggingSystemProperties} for Log4j2. + * + * @author hojooo + * @see Log4j2RollingPolicySystemProperty + */ +public class Log4j2LoggingSystemProperties extends LoggingSystemProperties { + + public Log4j2LoggingSystemProperties(Environment environment) { + super(environment); + } + + /** + * Create a new {@link Log4j2LoggingSystemProperties} instance. + * @param environment the source environment + * @param setter setter used to apply the property + */ + public Log4j2LoggingSystemProperties(Environment environment, + @Nullable BiConsumer setter) { + super(environment, setter); + } + + /** + * Create a new {@link Log4j2LoggingSystemProperties} instance. + * @param environment the source environment + * @param defaultValueResolver function used to resolve default values or {@code null} + * @param setter setter used to apply the property or {@code null} for system + * properties + */ + public Log4j2LoggingSystemProperties(Environment environment, + Function<@Nullable String, @Nullable String> defaultValueResolver, + @Nullable BiConsumer setter) { + super(environment, defaultValueResolver, setter); + } + + @Override + protected void apply(@Nullable LogFile logFile, PropertyResolver resolver) { + super.apply(logFile, resolver); + applyRollingPolicyProperties(resolver); + } + + private void applyRollingPolicyProperties(PropertyResolver resolver) { + applyRollingPolicy(Log4j2RollingPolicySystemProperty.FILE_NAME_PATTERN, resolver); + applyRollingPolicy(Log4j2RollingPolicySystemProperty.CLEAN_HISTORY_ON_START, resolver); + applyRollingPolicy(Log4j2RollingPolicySystemProperty.MAX_FILE_SIZE, resolver, DataSize.class); + applyRollingPolicy(Log4j2RollingPolicySystemProperty.TOTAL_SIZE_CAP, resolver, DataSize.class); + applyRollingPolicy(Log4j2RollingPolicySystemProperty.MAX_HISTORY, resolver); + } + + private void applyRollingPolicy(Log4j2RollingPolicySystemProperty property, PropertyResolver resolver) { + applyRollingPolicy(property, resolver, String.class); + } + + private void applyRollingPolicy(Log4j2RollingPolicySystemProperty property, PropertyResolver resolver, + Class type) { + T value = getProperty(resolver, property.getApplicationPropertyName(), type); + value = (value != null) ? value : getProperty(resolver, property.getDeprecatedApplicationPropertyName(), type); + if (value != null) { + String stringValue = String.valueOf((value instanceof DataSize dataSize) ? dataSize.toBytes() : value); + setSystemProperty(property.getEnvironmentVariableName(), stringValue); + } + } + + @SuppressWarnings("unchecked") + private @Nullable T getProperty(PropertyResolver resolver, String key, Class type) { + try { + return resolver.getProperty(key, type); + } + catch (ConversionFailedException | ConverterNotFoundException ex) { + if (type != DataSize.class) { + throw ex; + } + // Fallback for Log4j2 compatibility - try parsing as string if DataSize conversion fails + String value = resolver.getProperty(key); + if (value != null) { + try { + return (T) DataSize.parse(value); + } + catch (Exception parseEx) { + ex.addSuppressed(parseEx); + throw ex; + } + } + return null; + } + } + +} \ No newline at end of file diff --git a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4j2RollingPolicySystemProperty.java b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4j2RollingPolicySystemProperty.java new file mode 100644 index 000000000000..29d306eae394 --- /dev/null +++ b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4j2RollingPolicySystemProperty.java @@ -0,0 +1,81 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.logging.log4j2; + +/** + * Log4j2 rolling policy system properties that can later be used by log configuration + * files. + * + * @author hojooo + * @see Log4j2LoggingSystemProperties + */ +public enum Log4j2RollingPolicySystemProperty { + + /** + * Logging system property for the rolled-over log file name pattern. + */ + FILE_NAME_PATTERN("file-name-pattern", "logging.pattern.rolling-file-name"), + + /** + * Logging system property for the clean history on start flag. + */ + CLEAN_HISTORY_ON_START("clean-history-on-start", "logging.file.clean-history-on-start"), + + /** + * Logging system property for the file log max size. + */ + MAX_FILE_SIZE("max-file-size", "logging.file.max-size"), + + /** + * Logging system property for the file total size cap. + */ + TOTAL_SIZE_CAP("total-size-cap", "logging.file.total-size-cap"), + + /** + * Logging system property for the file log max history. + */ + MAX_HISTORY("max-history", "logging.file.max-history"); + + private final String environmentVariableName; + + private final String applicationPropertyName; + + private final String deprecatedApplicationPropertyName; + + Log4j2RollingPolicySystemProperty(String applicationPropertyName, String deprecatedApplicationPropertyName) { + this.environmentVariableName = "LOG4J2_ROLLINGPOLICY_" + name(); + this.applicationPropertyName = "logging.log4j2.rollingpolicy." + applicationPropertyName; + this.deprecatedApplicationPropertyName = deprecatedApplicationPropertyName; + } + + /** + * Return the name of environment variable that can be used to access this property. + * @return the environment variable name + */ + public String getEnvironmentVariableName() { + return this.environmentVariableName; + } + + String getApplicationPropertyName() { + return this.applicationPropertyName; + } + + String getDeprecatedApplicationPropertyName() { + return this.deprecatedApplicationPropertyName; + } + +} \ No newline at end of file diff --git a/core/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/core/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 26d63493f927..7eeda524cb95 100644 --- a/core/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/core/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -134,6 +134,41 @@ "type": "java.util.List", "description": "Overriding configuration files used to create a composite configuration. Can be prefixed with 'optional:' to only load the override if it exists." }, + { + "name": "logging.log4j2.rollingpolicy.clean-history-on-start", + "type": "java.lang.Boolean", + "description": "Whether to clean the archive log files on startup.", + "sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener", + "defaultValue": false + }, + { + "name": "logging.log4j2.rollingpolicy.file-name-pattern", + "type": "java.lang.String", + "description": "Pattern for rolled-over log file names.", + "sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener", + "defaultValue": "${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz" + }, + { + "name": "logging.log4j2.rollingpolicy.max-file-size", + "type": "org.springframework.util.unit.DataSize", + "description": "Maximum log file size.", + "sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener", + "defaultValue": "10MB" + }, + { + "name": "logging.log4j2.rollingpolicy.max-history", + "type": "java.lang.Integer", + "description": "Maximum number of archive log files to keep.", + "sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener", + "defaultValue": 7 + }, + { + "name": "logging.log4j2.rollingpolicy.total-size-cap", + "type": "org.springframework.util.unit.DataSize", + "description": "Total size of log backups to be kept.", + "sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener", + "defaultValue": "0B" + }, { "name": "logging.logback.rollingpolicy.clean-history-on-start", "type": "java.lang.Boolean", diff --git a/core/spring-boot/src/main/resources/org/springframework/boot/logging/log4j2/log4j2-file.xml b/core/spring-boot/src/main/resources/org/springframework/boot/logging/log4j2/log4j2-file.xml index c81eca81ddd0..fa9de2882f13 100644 --- a/core/spring-boot/src/main/resources/org/springframework/boot/logging/log4j2/log4j2-file.xml +++ b/core/spring-boot/src/main/resources/org/springframework/boot/logging/log4j2/log4j2-file.xml @@ -21,7 +21,8 @@ - +