This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 11.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 353333141ae5672f733f70aef45f68c38be631f3 Author: opencode <[email protected]> AuthorDate: Tue Oct 20 09:03:54 2026 +0200 Open the current log file when no date is set FileHandler builds the name of the log file from the prefix, the as-of date of the currently open file and the suffix. The date is normally set by publish() just before it opens the writer, but a direct call to open(), the public API used to (re-)open a handler, ran with no date set, so the concatenation rendered the null date as the literal text null and created files such as catalina.null.log. Such a file never matches the pattern used for rotation and cleanup, so it lingered in the log directory. The reopen path of AsyncFileHandler, which opens a previously closed handler, was equally affected. The writer now opens the file of the current date when no date is set, computing the date in the same way as publish(), whose date logic was extracted to a shared helper. The computed date only becomes the handler's as-of date once the writer has been created successfully, so a failed open can still be retried by a subsequent publish. Added a regression test that opens a handler before any record was published, verifies the file of the current date is used for the record published afterwards, and that reopening a closed handler does not create files with an unset date. --- java/org/apache/juli/FileHandler.java | 32 +++++++++++++++++++---- test/org/apache/juli/TestFileHandler.java | 43 +++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/java/org/apache/juli/FileHandler.java b/java/org/apache/juli/FileHandler.java index 3ae058391d..13249c8e55 100644 --- a/java/org/apache/juli/FileHandler.java +++ b/java/org/apache/juli/FileHandler.java @@ -232,9 +232,7 @@ public class FileHandler extends Handler { final String tsDate; if (rotatable.booleanValue()) { - // Construct the timestamp we will use - Timestamp ts = new Timestamp(System.currentTimeMillis()); - tsDate = ts.toString().substring(0, 10); + tsDate = obtainCurrentDate(); } else { tsDate = ""; } @@ -449,7 +447,8 @@ public class FileHandler extends Handler { /** - * Open the new log file for the date specified by <code>date</code>. + * Open the new log file. If the handler is rotatable and no date has been set yet, the log file for the current + * date is opened. */ public void open() { openWriter(); @@ -473,7 +472,16 @@ public class FileHandler extends Handler { FileOutputStream fos = null; OutputStream os = null; try { - File pathname = new File(dir.getAbsoluteFile(), prefix + (rotatable.booleanValue() ? date : "") + suffix); + // If no date has been set, typically because the handler has been opened before any record was + // published, use the current date, as publish() does when it rotates the log file, so the file + // name always matches the pattern used for rotation and cleanup + String currentDate = date; + boolean dateComputed = rotatable.booleanValue() && currentDate == null; + if (dateComputed) { + currentDate = obtainCurrentDate(); + } + File pathname = new File(dir.getAbsoluteFile(), + prefix + (rotatable.booleanValue() ? currentDate : "") + suffix); File parent = pathname.getParentFile(); if (!parent.mkdirs() && !parent.isDirectory()) { reportError("Unable to create [" + parent + "]", null, ErrorManager.OPEN_FAILURE); @@ -486,6 +494,9 @@ public class FileHandler extends Handler { writer = new PrintWriter( (encoding != null) ? new OutputStreamWriter(os, encoding) : new OutputStreamWriter(os), false); writer.write(getFormatter().getHead(this)); + if (dateComputed) { + date = currentDate; + } } catch (Exception e) { reportError(null, e, ErrorManager.OPEN_FAILURE); writer = null; @@ -508,6 +519,17 @@ public class FileHandler extends Handler { } } + /** + * Obtain the date string used as the date part of the names of the rotatable log files. + * + * @return The current date, formatted as <code>YYYY-MM-DD</code> + */ + private static String obtainCurrentDate() { + // Construct the timestamp we will use + Timestamp ts = new Timestamp(System.currentTimeMillis()); + return ts.toString().substring(0, 10); + } + private void clean() { if (maxDays.intValue() <= 0 || Files.notExists(getDirectoryAsPath())) { return; diff --git a/test/org/apache/juli/TestFileHandler.java b/test/org/apache/juli/TestFileHandler.java index b6f4f2cdea..7b7d9725c5 100644 --- a/test/org/apache/juli/TestFileHandler.java +++ b/test/org/apache/juli/TestFileHandler.java @@ -23,6 +23,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.logging.Level; +import java.util.logging.LogRecord; import org.junit.After; import org.junit.Assert; @@ -123,6 +125,47 @@ public class TestFileHandler { fh1.close(); } + /* + * Opening a rotatable handler before any record has been published must open the log file for the current date, + * rather than a file whose name contains the string representation of the unset date. The same applies when a + * closed handler is opened again. + */ + @Test + public void testOpenUsesCurrentDate() throws Exception { + String prefix = "opendate."; + String suffix = ".log"; + + FileHandler fh = new FileHandler(logsDir.getAbsolutePath(), prefix, suffix); + fh.open(); + + String today = LocalDate.now().toString(); + File expected = new File(logsDir, prefix + today + suffix); + Assert.assertTrue("Expected file [" + expected + "] was not created", expected.isFile()); + + // Publishing after the open must use the file that was opened, not rotate to a new one + fh.publish(new LogRecord(Level.INFO, "hello")); + Assert.assertTrue("The published record was not written to [" + expected + "]", + Files.readString(expected.toPath()).contains("hello")); + Assert.assertEquals(1, countFiles(prefix)); + + // Reopening a closed handler must not create a file with an unset date either + fh.close(); + fh.open(); + Assert.assertEquals(1, countFiles(prefix)); + + fh.close(); + } + + private int countFiles(String prefix) { + int count = 0; + for (File file : logsDir.listFiles()) { + if (file.getName().startsWith(prefix)) { + count++; + } + } + return count; + } + private void generateLogFiles(File dir, String prefix, String suffix, int amount) throws IOException { for (int i = 0; i < amount; i++) { --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
