This is an automated email from the ASF dual-hosted git repository.
ramanathan1504 pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
The following commit(s) were added to refs/heads/2.x by this push:
new d1f9355a55 Fix startup delay and direct write file naming in
CronTriggeringPolicy (#4226)
d1f9355a55 is described below
commit d1f9355a55d7837f19e0601426369eda1b6a49c6
Author: tupelo-schneck <[email protected]>
AuthorDate: Mon Aug 31 07:17:21 2026 -0400
Fix startup delay and direct write file naming in CronTriggeringPolicy
(#4226)
`CronTriggeringPolicy` no longer delays startup, and names direct write
files after the rollover period rather than the startup time.
---------
Co-authored-by: Ramanathan <[email protected]>
---
.../appender/rolling/CronTriggeringPolicyTest.java | 38 ++++++++++++++++++++++
.../log4j/core/util/CronExpressionTest.java | 23 +++++++++++++
.../appender/rolling/CronTriggeringPolicy.java | 5 +--
.../rolling/DirectWriteRolloverStrategy.java | 2 --
.../logging/log4j/core/util/CronExpression.java | 3 ++
.../fix_cron_triggering_policy_slow_startup.xml | 12 +++++++
6 files changed, 79 insertions(+), 4 deletions(-)
diff --git
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicyTest.java
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicyTest.java
index 65f3ccb910..430a3c2cd5 100644
---
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicyTest.java
+++
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicyTest.java
@@ -16,15 +16,21 @@
*/
package org.apache.logging.log4j.core.appender.rolling;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.core.appender.RollingFileAppender;
import org.apache.logging.log4j.core.appender.RollingRandomAccessFileAppender;
import org.apache.logging.log4j.core.config.Configurator;
import org.apache.logging.log4j.core.config.NullConfiguration;
import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.util.CronExpression;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
class CronTriggeringPolicyTest {
@@ -72,6 +78,38 @@ class CronTriggeringPolicyTest {
testBuilder();
}
+ @Test
+ @Timeout(value = 2, unit = TimeUnit.SECONDS)
+ void testBuilderWithoutFileNameInitializesPromptly() {
+ // @formatter:off
+ final RollingFileAppender raf = RollingFileAppender.newBuilder()
+ .setName("test4")
+ .setFilePattern("target/testcmd4.log.%d{yyyy-MM-dd}")
+ .setPolicy(CronTriggeringPolicy.createPolicy(configuration,
Boolean.TRUE.toString(), "0 0 0 ? * SUN"))
+ .setConfiguration(configuration)
+ .build();
+ // @formatter:on
+ assertNotNull(raf);
+ }
+
+ @Test
+ void testDirectWriteFileNameUsesPeriodStart() throws Exception {
+ final String schedule = "0 0 0 ? * SUN";
+ // @formatter:off
+ final RollingFileAppender raf = RollingFileAppender.newBuilder()
+ .setName("test5")
+ .setFilePattern("target/testcmd5.log-%d{yyyyMMdd}")
+ .setPolicy(CronTriggeringPolicy.createPolicy(configuration,
Boolean.FALSE.toString(), schedule))
+ .setConfiguration(configuration)
+ .build();
+ // @formatter:on
+ assertNotNull(raf);
+
+ final Date periodStart = new
CronExpression(schedule).getPrevFireTime(new Date());
+ final String expected = "target/testcmd5.log-" + new
SimpleDateFormat("yyyyMMdd").format(periodStart);
+ assertEquals(expected, raf.getManager().getFileName());
+ }
+
/**
* Tests LOG4J2-1740 Add CronTriggeringPolicy programmatically leads to NPE
*/
diff --git
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/CronExpressionTest.java
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/CronExpressionTest.java
index e5f19c992c..38c4c419ac 100644
---
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/CronExpressionTest.java
+++
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/CronExpressionTest.java
@@ -29,9 +29,11 @@ import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
import org.assertj.core.presentation.Representation;
import org.assertj.core.presentation.StandardRepresentation;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
/**
* Class Description goes here.
@@ -181,6 +183,27 @@ class CronExpressionTest {
assertEquals(expected, fireDate, "Dates not equal.");
}
+ @Test
+ @Timeout(value = 1, unit = TimeUnit.SECONDS)
+ void testPrevFireTimeAtEpochReturnsNullPromptly() throws Exception {
+ final CronExpression parser = new CronExpression("0 0 0 ? * SUN");
+ assertThat(parser.getPrevFireTime(new Date(0))).isNull();
+ }
+
+ @Test
+ void testPrevFireTimeJustAfterEpochIsUnaffectedByBound() throws Exception {
+ final TimeZone utc = TimeZone.getTimeZone("UTC");
+ final CronExpression parser = new CronExpression("0 0 0 * * ?");
+ parser.setTimeZone(utc);
+ final Calendar target = Calendar.getInstance(utc);
+ target.clear();
+ target.set(1970, Calendar.JANUARY, 2, 6, 0, 0);
+ final Calendar expected = Calendar.getInstance(utc);
+ expected.clear();
+ expected.set(1970, Calendar.JANUARY, 2, 0, 0, 0);
+
assertThat(parser.getPrevFireTime(target.getTime())).isEqualTo(expected.getTime());
+ }
+
/**
* Test that the next valid time after a fallback at 2:00 am from Daylight
Saving Time
*/
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicy.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicy.java
index 7ec778764e..880075c76d 100644
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicy.java
+++
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicy.java
@@ -64,7 +64,8 @@ public final class CronTriggeringPolicy extends
AbstractTriggeringPolicy {
public void initialize(final RollingFileManager aManager) {
this.manager = aManager;
final Date now = new Date();
- final Date lastRollForFile = cronExpression.getPrevFireTime(new
Date(this.manager.getFileTime()));
+ final long fileTime = this.manager.getFileTime();
+ final Date lastRollForFile = fileTime > 0 ?
cronExpression.getPrevFireTime(new Date(fileTime)) : null;
final Date lastRegularRoll = cronExpression.getPrevFireTime(new
Date());
aManager.getPatternProcessor().setCurrentFileTime(lastRegularRoll.getTime());
LOGGER.debug("LastRollForFile {}, LastRegularRole {}",
lastRollForFile, lastRegularRoll);
@@ -150,7 +151,7 @@ public final class CronTriggeringPolicy extends
AbstractTriggeringPolicy {
private void rollover() {
// If possible, use the time rollover was supposed to occur, not the
actual time.
final Date rollTime = future != null ? future.getFireTime() : new
Date();
- manager.rollover(cronExpression.getPrevFireTime(rollTime),
lastRollDate);
+ manager.rollover(cronExpression.getPrevFireTime(rollTime), rollTime);
if (future != null) {
lastRollDate = future.getFireTime();
}
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DirectWriteRolloverStrategy.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DirectWriteRolloverStrategy.java
index e105fa455b..2a97f63c3f 100644
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DirectWriteRolloverStrategy.java
+++
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DirectWriteRolloverStrategy.java
@@ -390,8 +390,6 @@ public class DirectWriteRolloverStrategy extends
AbstractRolloverStrategy implem
final SortedMap<Integer, Path> eligibleFiles =
getEligibleFiles(manager);
final int fileIndex = eligibleFiles.size() > 0 ? (nextIndex > 0 ?
nextIndex : eligibleFiles.lastKey()) : 1;
final StringBuilder buf = new StringBuilder(255);
- // LOG4J2-3339 - Always use the current time for new direct write
files.
-
manager.getPatternProcessor().setCurrentFileTime(System.currentTimeMillis());
manager.getPatternProcessor().formatFileName(strSubstitutor, buf,
true, fileIndex);
final int suffixLength = suffixLength(buf.toString());
final String name = suffixLength > 0 ? buf.substring(0,
buf.length() - suffixLength) : buf.toString();
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/CronExpression.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/CronExpression.java
index 6e1433c660..6c93647a45 100644
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/CronExpression.java
+++
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/CronExpression.java
@@ -1581,6 +1581,9 @@ public final class CronExpression {
Date prevFireTime;
do {
final Date prevCheckDate = new Date(start.getTime() -
minIncrement);
+ if (prevCheckDate.getTime() < 0) {
+ return null;
+ }
prevFireTime = getTimeAfter(prevCheckDate);
if (prevFireTime == null || prevFireTime.before(MIN_DATE)) {
return null;
diff --git a/src/changelog/.2.x.x/fix_cron_triggering_policy_slow_startup.xml
b/src/changelog/.2.x.x/fix_cron_triggering_policy_slow_startup.xml
new file mode 100644
index 0000000000..1f7af2e27d
--- /dev/null
+++ b/src/changelog/.2.x.x/fix_cron_triggering_policy_slow_startup.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entry xmlns="https://logging.apache.org/xml/ns"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="
+ https://logging.apache.org/xml/ns
+ https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
+ type="changed">
+ <issue id="4226"
link="https://github.com/apache/logging-log4j2/pull/4226"/>
+ <description format="asciidoc">
+ `CronTriggeringPolicy` no longer delays startup, and names direct
write files after the rollover period rather than the startup time.
+ </description>
+</entry>