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>

Reply via email to