Skip to content

Conversation

@hojooo
Copy link
Contributor

@hojooo hojooo commented Sep 17, 2025

Summary

Hello! Log4j2 does not support rolling policies. So this PR adds basic rolling policy configuration property support for Log4j2. It introduces Log4j2 specific properties equivalent to the existing logging.logback.rollingpolicy.* properties, enabling consistent logging configuration through application.properties across different logging implementations. 
And advanced rolling strategy support to the Log4j2 rolling policy configuration. It introduces the SpringBootTriggeringPolicy plugin to enable various rolling strategies and strategy-specific detailed configuration through application.properties.

Changes

1. Standardized Log4j2 Rolling Policy Properties

We've added standard properties for Log4j2, similar to the existing logging.logback.rollingpolicy.* properties. Users can now easily control the rolling policy using the following attributes in application.properties:

# Basic rolling policy configuration (file size, history, etc.)
logging.log4j2.rollingpolicy.max-file-size=10MB
logging.log4j2.rollingpolicy.max-history=7
logging.log4j2.rollingpolicy.total-size-cap=1GB
logging.log4j2.rollingpolicy.clean-history-on-start=false

# Rolled file name pattern configuration
logging.log4j2.rollingpolicy.file-name-pattern=${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz

2. Advanced Rolling Strategy

A custom Log4j2 plugin, SpringBootTriggeringPolicy, has been introduced to support various advanced rolling strategies beyond simple size-based rolling.

  • size (default): Rolls files based on their size.
  • time: Rolls files based on a time interval.
  • size-and-time: Rolls when both size and time conditions are met.
  • cron: Rolls based on a cron expression schedule.

Implementation

1. Property Standardization Implementation

  • Log4j2RollingPolicySystemProperty: An enum was implemented to map Spring Environment properties to system properties that Log4j2 can recognize (e.g., max-file-size -> LOG4J2_ROLLINGPOLICY_MAX_FILE_SIZE).
  • Log4j2LoggingSystemProperties: This class handles the automatic conversion of Spring's DataSize type to bytes and ensures backward compatibility with deprecated properties like logging.file.max-size.
  • log4j2-file.xml: The configuration file was updated to reference the newly defined system properties using the ${sys:PROPERTY_NAME} syntax.

2. Advanced Rolling Strategy Implementation

  • SpringBootTriggeringPolicy Plugin: A custom TriggeringPolicy was implemented using Log4j2's @Plugin annotation. Based on the strategy property, this plugin internally selects and delegates to the appropriate policy (e.g., SizeBasedTriggeringPolicy, TimeBasedTriggeringPolicy).
  • log4j2-file.xml Update: The <Policies> block was modified to use SpringBootTriggeringPolicy, with its parameters (maxFileSize, timeInterval, cronExpression, etc.) configured via system properties.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Sep 17, 2025
@philwebb philwebb added the for: team-meeting An issue we'd like to discuss as a team to make progress label Sep 17, 2025
@hojooo hojooo force-pushed the log4j2-rolling-policy branch from 66fcaf7 to b5d3b84 Compare September 18, 2025 03:03
@hojooo hojooo force-pushed the log4j2-rolling-policy branch from 936ec72 to 05f8bb4 Compare October 7, 2025 07:18
@snicoll
Copy link
Member

snicoll commented Dec 1, 2025

@hojooo can you please rebase this PR to main and squash everything in one commit? Once you're done, running the build to make sure it passes would be appreciated.

@ppkarwasz would you have a min to review this one please? I am wondering, in particular, about SpringBootTriggeringPolicy that has to do quite a bit that looks unrelated to what a plugin should do (lifecycle management, etc).

@snicoll snicoll added the status: waiting-for-feedback We need additional information before we can continue label Dec 1, 2025
@hojooo
Copy link
Contributor Author

hojooo commented Dec 2, 2025

@snicoll Got it, thanks. I’ll update the PR shortly.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Dec 2, 2025
Signed-off-by: hojooo <[email protected]>

Log4j2: Introduce SpringBootTriggeringPolicy and wire plugin discovery
      - Add SpringBootTriggeringPolicy plugin supporting size, time, size-and-time, and cron strategies
      - Read rolling strategy parameters from LOG4J2_ROLLINGPOLICY_* system properties (fallback to attributes)
      - Register Boot plugin package in Log4J2LoggingSystem to ensure plugin discovery
      - Use SpringBootTriggeringPolicy under a top-level Policies wrapper in log4j2-file.xml

Signed-off-by: hojooo <[email protected]>

Log4j2: Add property-driven rolling strategy support and metadata
      - Introduce rolling policy properties: strategy, time-based.interval, time-based.modulate, cron.schedule
      - Extend Log4j2RollingPolicySystemProperty with STRATEGY, TIME_INTERVAL, TIME_MODULATE, CRON_SCHEDULE
      - Propagate new properties in Log4j2LoggingSystemProperties (with deprecated fallback guarded for null)
      - Document new properties in configuration metadata
      - Update Log4j2LoggingSystemPropertiesTests to assert new system properties mappingAdd Log4j2 rolling policy configuration support

Signed-off-by: hojooo <[email protected]>

Tests: Initialize with packaged file config and unwrap composite policy
      - Initialize with classpath:org/springframework/boot/logging/log4j2/log4j2-file.xml to validate file-based rolling
      - Unwrap CompositeTriggeringPolicy to locate nested SpringBootTriggeringPolicy and assert its delegate
      - Keep assertions for time, size-and-time, and cron strategies

Signed-off-by: hojooo <[email protected]>

Refactor Builder import path

Signed-off-by: hojooo <[email protected]>

add clean-history-on-start and total-size-cap property in log4j2-file.xml

Signed-off-by: hojooo <[email protected]>

refactor indent

Signed-off-by: hojooo <[email protected]>

Refactor Log4j2LoggingSystemPropertiesTests to verify System.setProperty path

Signed-off-by: hojooo <[email protected]>

Add Rolling Policy

Signed-off-by: hojooo <[email protected]>
@hojooo hojooo force-pushed the log4j2-rolling-policy branch from 2e0bb46 to ec926ad Compare December 2, 2025 04:01
@snicoll snicoll added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Dec 2, 2025
@snicoll
Copy link
Member

snicoll commented Dec 2, 2025

@hojooo can you please stop pushing one commit at a time like that? Every time you do, it sends a notification to everyone watching this repo, which adds quite a bit of noise. Please build locally and push once you're ready.

@hojooo
Copy link
Contributor Author

hojooo commented Dec 2, 2025

@snicoll Thanks for the heads-up, and sorry for the noise I caused. I didn’t realize each small commit was sending notifications to everyone. I’ll make sure to build and test locally and then push in larger batches once things are ready.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Dec 2, 2025
Copy link
Contributor

@ppkarwasz ppkarwasz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@snicoll,

From a Log4j point of view, this PR looks overall correct, although it could be simplified and there are some smaller issues.

Comment on lines 56 to 60
private final TriggeringPolicy delegate;

private SpringBootTriggeringPolicy(TriggeringPolicy delegate) {
this.delegate = delegate;
}
Copy link
Contributor

@ppkarwasz ppkarwasz Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need to implement a wrapper for a TriggeringPolicy. You can return the appropriate triggering policy directly from your factory method:

public abstract class SpringBootTriggeringPolicy implements TriggeringPolicy {

    private SpringBootTriggeringPolicy() {
      // no instantiation
    }

	public static TriggeringPolicy createPolicy(@PluginAttribute("strategy") @Nullable String strategy,
			@PluginAttribute("maxFileSize") @Nullable String maxFileSize,
			@PluginAttribute("timeInterval") @Nullable Integer timeInterval,
			@PluginAttribute("timeModulate") @Nullable Boolean timeModulate,
			@PluginAttribute("cronExpression") @Nullable String cronExpression,
			@PluginConfiguration Configuration configuration) {
    ...
    }
}

Factory methods can return whatever they want (see MongoDbProvider), although there is a small limitation in the injector that assumes they return the same type as the plugin type, which is why SpringBootTriggeringPolicy needs to implement TriggeringPolicy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the feedback! I agree this is a much cleaner approach. The wrapper pattern was adding unnecessary complexity when the factory can simply return the standard Log4j2 policies directly

Comment on lines 111 to 122
@PluginBuilderFactory
public static SpringBootTriggeringPolicyBuilder newBuilder() {
return new SpringBootTriggeringPolicyBuilder();
}

@PluginFactory
public static SpringBootTriggeringPolicy createPolicy(@PluginAttribute("strategy") @Nullable String strategy,
@PluginAttribute("maxFileSize") @Nullable String maxFileSize,
@PluginAttribute("timeInterval") @Nullable Integer timeInterval,
@PluginAttribute("timeModulate") @Nullable Boolean timeModulate,
@PluginAttribute("cronExpression") @Nullable String cronExpression,
@PluginConfiguration Configuration configuration) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need for both a factory method and a builder. Since the builder is easier to maintain than the factory method, you should leave just the builder.

Comment on lines 45 to 46
cleanHistoryOnStart="${sys:LOG4J2_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}"
totalSizeCap="${sys:LOG4J2_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0}"/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The attributes cleanHistoryOnStart and totalSizeCap do not exist, see the DefaultRolloverStrategy documentation and the automatically generated list of configuration attributes. You would need to implement them in a custom rollover strategy:

  • If you want to rollover at each JVM startup, you can use OnStartupTriggeringPolicy,
  • If you want a limit on the total size of archived files, you need to configure the appropriate Delete actions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the clarification. I've removed these non-existent attributes from DefaultRolloverStrategy.

I’ve removed those non-existent attributes from DefaultRolloverStrategy, so the configuration is now:

<DefaultRolloverStrategy max="${sys:LOG4J2_ROLLINGPOLICY_MAX_HISTORY:-7}"/>

To keep this change focused, I’m not wiring in OnStartupTriggeringPolicy or Delete actions yet.
If we need startup-based rollover or a total size cap in the future, we can follow up with a custom rollover strategy and Delete actions in a separate change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

for: team-meeting An issue we'd like to discuss as a team to make progress status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants