ASF Bugzilla – Attachment 20317 Details for
Bug 42598
Allow rolling appenders to put rolled files in another directory
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch to allow rolling appenders to put rolled files in another directory
backupDirectory.patch (text/plain), 22.05 KB, created by
Chris Heisterkamp
on 2007-06-05 20:47:36 UTC
(
hide
)
Description:
Patch to allow rolling appenders to put rolled files in another directory
Filename:
MIME Type:
Creator:
Chris Heisterkamp
Created:
2007-06-05 20:47:36 UTC
Size:
22.05 KB
patch
obsolete
>Index: src/main/java/org/apache/log4j/DailyRollingFileAppender.java >=================================================================== >--- src/main/java/org/apache/log4j/DailyRollingFileAppender.java (revision 544346) >+++ src/main/java/org/apache/log4j/DailyRollingFileAppender.java (working copy) >@@ -28,6 +28,8 @@ > import java.util.TimeZone; > import java.util.Locale; > >+import org.apache.log4j.helpers.BackupDirectory; >+import org.apache.log4j.helpers.FileMove; > import org.apache.log4j.helpers.LogLog; > import org.apache.log4j.spi.LoggingEvent; > >@@ -179,6 +181,7 @@ > // The gmtTimeZone is used only in computeCheckPeriod() method. > static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT"); > >+ protected String backupDirectory = null; > > /** > The default constructor does nothing. */ >@@ -212,6 +215,19 @@ > return datePattern; > } > >+ public void setBackupDirectory(String directory) { >+ String val = directory.trim(); >+ backupDirectory = val; >+ } >+ >+ public String getBackupDirectory() { >+ return backupDirectory; >+ } >+ >+ protected String getScheduledFilename() { >+ return scheduledFilename; >+ } >+ > public void activateOptions() { > super.activateOptions(); > if(datePattern != null && fileName != null) { >@@ -312,17 +328,20 @@ > // close current file, and rename it to datedFilename > this.closeFile(); > >- File target = new File(scheduledFilename); >+ String backupFileName = BackupDirectory.createBackupFileName(backupDirectory, scheduledFilename); >+ LogLog.debug("backupFileName=" + backupFileName); >+ >+ File target = new File(backupFileName); > if (target.exists()) { > target.delete(); > } > > File file = new File(fileName); >- boolean result = file.renameTo(target); >+ boolean result = FileMove.move(file, target); > if(result) { >- LogLog.debug(fileName +" -> "+ scheduledFilename); >+ LogLog.debug(fileName +" -> "+ target); > } else { >- LogLog.error("Failed to rename ["+fileName+"] to ["+scheduledFilename+"]."); >+ LogLog.error("Failed to rename ["+fileName+"] to ["+target+"]."); > } > > try { >Index: src/main/java/org/apache/log4j/helpers/BackupDirectory.java >=================================================================== >--- src/main/java/org/apache/log4j/helpers/BackupDirectory.java (revision 0) >+++ src/main/java/org/apache/log4j/helpers/BackupDirectory.java (revision 0) >@@ -0,0 +1,58 @@ >+/* >+ * Licensed to the Apache Software Foundation (ASF) under one or more >+ * contributor license agreements. See the NOTICE file distributed with >+ * this work for additional information regarding copyright ownership. >+ * The ASF licenses this file to You 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 >+ * >+ * http://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.apache.log4j.helpers; >+ >+import java.io.File; >+ >+/** >+ Utility class for generating a backup directory name >+ */ >+public class BackupDirectory { >+ >+ /** Create a backup file name >+ * Uses the log file name and the backup directory to generate the path for the backup file >+ * If the backup directory does not exist it will attempt to create the directory >+ * >+ * @param backupDirectory The backup directory path >+ * @param fileName The log file directory path >+ * @return The backup log file path >+ * */ >+ public final static String createBackupFileName(String backupDirectory, String fileName) { >+ String fileNameOnly = fileName; >+ File backupDir = null; >+ >+ if(backupDirectory != null) { >+ fileNameOnly = new File(fileName).getName(); >+ backupDir = new File(backupDirectory); >+ if(!backupDir.exists()) { >+ LogLog.debug("Backup directory does not exist attempting to create backupDirectory=" + backupDir); >+ if(!backupDir.mkdirs()) { >+ LogLog.warn("Could not create backupDirectory=" + backupDir + " using default directory instead"); >+ fileNameOnly = fileName; >+ backupDir = null; >+ } >+ } else if(!backupDir.isDirectory()) { >+ LogLog.warn("Backup directory is not a directory. backupDirectory=" + backupDir + " using default directory instead"); >+ fileNameOnly = fileName; >+ backupDir = null; >+ } >+ } >+ >+ return new File(backupDir, fileNameOnly).getPath(); >+ } >+} >Index: src/main/java/org/apache/log4j/helpers/FileMove.java >=================================================================== >--- src/main/java/org/apache/log4j/helpers/FileMove.java (revision 0) >+++ src/main/java/org/apache/log4j/helpers/FileMove.java (revision 0) >@@ -0,0 +1,101 @@ >+/* >+ * Licensed to the Apache Software Foundation (ASF) under one or more >+ * contributor license agreements. See the NOTICE file distributed with >+ * this work for additional information regarding copyright ownership. >+ * The ASF licenses this file to You 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 >+ * >+ * http://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.apache.log4j.helpers; >+ >+import java.io.BufferedInputStream; >+import java.io.BufferedOutputStream; >+import java.io.File; >+import java.io.FileInputStream; >+import java.io.FileOutputStream; >+import java.io.FileNotFoundException; >+import java.io.IOException; >+ >+/** >+ Utility class for moving Files >+ */ >+public class FileMove { >+ >+ /** Copy a File >+ * The renameTo method does not allow action across NFS mounted filesystems >+ * this method is the workaround >+ * see sun bug id:4073756 for more info >+ * >+ * @param fromFile The existing File >+ * @param toFile The new File >+ * @return <code>true</code> if and only if the renaming succeeded; >+ * <code>false</code> otherwise >+ * */ >+ public final static boolean copy(File fromFile, File toFile) { >+ >+ try { >+ FileInputStream in = new FileInputStream(fromFile); >+ FileOutputStream out = new FileOutputStream(toFile); >+ BufferedInputStream inBuffer = new BufferedInputStream(in); >+ BufferedOutputStream outBuffer = new >+ BufferedOutputStream(out); >+ >+ int theByte = 0; >+ >+ while ((theByte = inBuffer.read()) > -1) { >+ outBuffer.write(theByte); >+ } >+ >+ outBuffer.close(); >+ inBuffer.close(); >+ out.close(); >+ in.close(); >+ >+ // cleanup if files are not the same length >+ if (fromFile.length() != toFile.length()) { >+ toFile.delete(); >+ >+ return false; >+ } >+ >+ return true; >+ >+ } catch (IOException e) { >+ >+ return false; >+ } >+ } >+ >+ /** Move a File >+ * The renameTo method does not allow action across NFS mounted >+ * filesystems. this method is the workaround >+ * see sun bug id:4073756 for more info >+ * >+ * @param fromFile The existing File >+ * @param toFile The new File >+ * @return <code>true</code> if and only if the renaming succeeded; >+ * <code>false</code> otherwise >+ * */ >+ public final static boolean move(File fromFile, File toFile) { >+ >+ if(fromFile.renameTo(toFile)) { >+ return true; >+ } >+ >+ // delete if copy was successful, otherwise move will fail >+ if (copy(fromFile, toFile)) { >+ return fromFile.delete(); >+ } >+ >+ return false; >+ } >+} >Index: src/main/java/org/apache/log4j/RollingFileAppender.java >=================================================================== >--- src/main/java/org/apache/log4j/RollingFileAppender.java (revision 544346) >+++ src/main/java/org/apache/log4j/RollingFileAppender.java (working copy) >@@ -22,6 +22,8 @@ > import java.io.IOException; > import java.io.Writer; > import java.io.File; >+import org.apache.log4j.helpers.BackupDirectory; >+import org.apache.log4j.helpers.FileMove; > import org.apache.log4j.helpers.OptionConverter; > import org.apache.log4j.helpers.LogLog; > import org.apache.log4j.helpers.CountingQuietWriter; >@@ -48,6 +50,8 @@ > protected int maxBackupIndex = 1; > > private long nextRollover = 0; >+ >+ protected String backupDirectory = null; > > /** > The default constructor simply calls its {@link >@@ -102,6 +106,18 @@ > return maxFileSize; > } > >+ public >+ void setBackupDirectory(String directory) { >+ String val = directory.trim(); >+ backupDirectory = val; >+ } >+ >+ public >+ String getBackupDirectory() { >+ return backupDirectory; >+ } >+ >+ > /** > Implements the usual roll over behaviour. > >@@ -130,33 +146,38 @@ > } > LogLog.debug("maxBackupIndex="+maxBackupIndex); > >+ String backupFileName = BackupDirectory.createBackupFileName(backupDirectory, fileName); >+ LogLog.debug("backupFileName=" + backupFileName); >+ > boolean renameSucceeded = true; > // If maxBackups <= 0, then there is no file renaming to be done. > if(maxBackupIndex > 0) { > // Delete the oldest file, to keep Windows happy. >- file = new File(fileName + '.' + maxBackupIndex); >+ file = new File(backupFileName + '.' + maxBackupIndex); > if (file.exists()) > renameSucceeded = file.delete(); > > // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2} > for (int i = maxBackupIndex - 1; i >= 1 && renameSucceeded; i--) { >- file = new File(fileName + "." + i); >- if (file.exists()) { >- target = new File(fileName + '.' + (i + 1)); >- LogLog.debug("Renaming file " + file + " to " + target); >- renameSucceeded = file.renameTo(target); >- } >+ file = new File(backupFileName + "." + i); >+ if (file.exists()) { >+ target = new File(backupFileName + '.' + (i + 1)); >+ >+ LogLog.debug("Renaming file " + file + " to " + target); >+ renameSucceeded = FileMove.move(file, target); >+ } > } > > if(renameSucceeded) { > // Rename fileName to fileName.1 >- target = new File(fileName + "." + 1); >+ target = new File(backupFileName + "." + 1); > > this.closeFile(); // keep windows happy. > > file = new File(fileName); > LogLog.debug("Renaming file " + file + " to " + target); >- renameSucceeded = file.renameTo(target); >+ renameSucceeded = FileMove.move(file, target); >+ > // > // if file rename failed, reopen file with append = true > // >@@ -186,7 +207,7 @@ > } > } > } >- >+ > public > synchronized > void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize) >Index: tests/build.xml >=================================================================== >--- tests/build.xml (revision 544346) >+++ tests/build.xml (working copy) >@@ -161,7 +161,7 @@ > HierarchyThreshold, DefaultInit, SocketServer, > XMLLayout, AsyncAppender, > OptionConverter, BoundedFIFO, >- CyclicBuffer, OR, >+ CyclicBuffer, BackupDirectory, OR, > LevelMatchFilter, PatternParser, > PatternLayout, RFA, ERFA, DRFA, > NTEventLogAppender, Syslog, Socket"/> >@@ -387,6 +387,15 @@ > </junit> > </target> > >+ <target name="BackupDirectory" depends="build"> >+ <junit printsummary="yes" fork="yes" >+ haltonfailure="${haltonfailure}" dir="${basedir}"> >+ <classpath refid="tests.classpath"/> >+ <formatter type="plain" usefile="false"/> >+ <test name="org.apache.log4j.helpers.BackupDirectoryTestCase" /> >+ </junit> >+ </target> >+ > <target name="PatternParser" depends="build"> > <junit printsummary="yes" fork="yes" > haltonfailure="${haltonfailure}" dir="${basedir}"> >Index: tests/input/RFA2.properties >=================================================================== >--- tests/input/RFA2.properties (revision 0) >+++ tests/input/RFA2.properties (revision 0) >@@ -0,0 +1,13 @@ >+log4j.rootLogger=DEBUG, testAppender >+log4j.appender.testAppender=org.apache.log4j.RollingFileAppender >+log4j.appender.testAppender.file=output/RFA-testBackup.log >+log4j.appender.testAppender.BackupDirectory=output/archive >+log4j.appender.testAppender.Append=false >+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout >+log4j.appender.testAppender.layout.ConversionPattern=%m\n >+log4j.appender.testAppender.maxFileSize=100 >+ >+# Prevent internal log4j DEBUG messages from polluting the output. >+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO >+log4j.logger.org.apache.log4j.config.PropertySetter=INFO >+log4j.logger.org.apache.log4j.FileAppender=INFO >Index: tests/run-tests.bat >=================================================================== >--- tests/run-tests.bat (revision 544346) >+++ tests/run-tests.bat (working copy) >@@ -75,6 +75,7 @@ > java junit.textui.TestRunner org.apache.log4j.helpers.OptionConverterTestCase > java junit.textui.TestRunner org.apache.log4j.helpers.BoundedFIFOTestCase > java junit.textui.TestRunner org.apache.log4j.helpers.CyclicBufferTestCase >+java junit.textui.TestRunner org.apache.log4j.helpers.BackupDirectoryTestCase > java junit.textui.TestRunner org.apache.log4j.or.ORTestCase > java junit.textui.TestRunner org.apache.log4j.varia.LevelMatchFilterTestCase > java junit.textui.TestRunner org.apache.log4j.helpers.PatternParserTestCase >Index: tests/src/java/org/apache/log4j/DRFATestCase.java >=================================================================== >--- tests/src/java/org/apache/log4j/DRFATestCase.java (revision 544346) >+++ tests/src/java/org/apache/log4j/DRFATestCase.java (working copy) >@@ -421,5 +421,48 @@ > assertTrue(firstFile.length() > 0); > > } >+ >+ /** >+ * Tests rollOver with a minute periodicity. >+ * >+ * @throws IOException >+ * @throws InterruptedException >+ */ >+ public void testMinuteRolloverWithBackupDirectory() throws IOException, InterruptedException { >+ Layout layout = new SimpleLayout(); >+ String filename = "output/drfa_minuteRolloverBackup.log"; >+ String pattern = "'.'yyyy-MM-dd-HH-mm"; > >+ DailyRollingFileAppender appender = >+ new DailyRollingFileAppender(layout, >+ filename, >+ pattern); >+ appender.setBackupDirectory("output/drfa_archive"); >+ Logger root = Logger.getRootLogger(); >+ root.addAppender(appender); >+ >+ root.info("Hello, World"); >+ String expectedFilename = appender.getScheduledFilename(); >+ String fileNameOnly = new File(expectedFilename).getName(); >+ File backupFile = new File("output/drfa_archive/" + fileNameOnly); >+ >+ // In theory this assertion could fail if this test is run twice in the same minute but it seems >+ // unlikely since we wait until the end of the current minute in the same test >+ assertFalse(backupFile.exists()); >+ >+ Calendar cal = Calendar.getInstance(); >+ long now = cal.getTime().getTime(); >+ cal.set(Calendar.SECOND, 3); >+ cal.set(Calendar.MILLISECOND, 0); >+ cal.add(Calendar.MINUTE, 1); >+ >+ long until = cal.getTime().getTime(); >+ Thread.sleep(until - now); >+ >+ root.info("Hello, World"); >+ assertTrue("backup file " + backupFile + " does not exist", backupFile.exists()); >+ assertTrue(backupFile.length() > 0); >+ >+ } >+ > } >Index: tests/src/java/org/apache/log4j/helpers/BackupDirectoryTestCase.java >=================================================================== >--- tests/src/java/org/apache/log4j/helpers/BackupDirectoryTestCase.java (revision 0) >+++ tests/src/java/org/apache/log4j/helpers/BackupDirectoryTestCase.java (revision 0) >@@ -0,0 +1,80 @@ >+/* >+ * Licensed to the Apache Software Foundation (ASF) under one or more >+ * contributor license agreements. See the NOTICE file distributed with >+ * this work for additional information regarding copyright ownership. >+ * The ASF licenses this file to You 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 >+ * >+ * http://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.apache.log4j.helpers; >+ >+import junit.framework.TestCase; >+ >+import java.io.File; >+ >+ >+/** >+ * Tests for BackupDirectory. >+ * >+ **/ >+public class BackupDirectoryTestCase extends TestCase { >+ /** >+ * Create new instance of BackupDirectoryTest. >+ * @param testName test name >+ */ >+ public BackupDirectoryTestCase(final String testName) { >+ super(testName); >+ } >+ >+ public void testBackupDirectoryIsSubDirectoryOfFileDirectory() throws Exception { >+ String backupDirectory = "output" + File.separator + "archive"; >+ String fileName = "output" + File.separator + "testBackupDirectory.log"; >+ String backupFileName = BackupDirectory.createBackupFileName(backupDirectory, fileName); >+ assertTrue(new File(backupDirectory).exists()); >+ assertFalse(fileName.equals(backupFileName)); >+ assertFalse(new File(backupFileName).exists()); >+ assertFalse(new File(fileName).exists()); >+ } >+ >+ public void testBackupDirectoryIsSameAsFileDirectory() throws Exception { >+ String backupDirectory = "output"; >+ String fileName = backupDirectory + File.separator + "testBackupDirectory.log"; >+ String backupFileName = BackupDirectory.createBackupFileName(backupDirectory, fileName); >+ assertTrue(new File(backupDirectory).exists()); >+ assertEquals(fileName, backupFileName); >+ assertFalse(new File(backupFileName).exists()); >+ assertFalse(new File(fileName).exists()); >+ } >+ >+ public void testInvalidBackupDirectory() throws Exception { >+ String invalidDirectory = "+<=>*"; >+ String fileName = "output" + File.separator + "testBackupDirectory.log"; >+ String backupFileName = BackupDirectory.createBackupFileName(invalidDirectory, fileName); >+ assertFalse(new File(invalidDirectory).exists()); >+ assertEquals(fileName, backupFileName); >+ >+ assertFalse(new File(backupFileName).exists()); >+ assertFalse(new File(fileName).exists()); >+ } >+ >+ public void testBackupDirectoryIsAFileThatExists() throws Exception { >+ String backupDirectory = "output" + File.separator + "testBackupDirectory.log"; >+ String fileName = backupDirectory; >+ assertTrue(new File(fileName).createNewFile()); >+ >+ String backupFileName = BackupDirectory.createBackupFileName(backupDirectory, fileName); >+ assertTrue(new File(backupDirectory).exists()); >+ assertEquals(fileName, backupFileName); >+ >+ assertTrue(new File(backupFileName).exists()); >+ } >+} >Index: tests/src/java/org/apache/log4j/RFATestCase.java >=================================================================== >--- tests/src/java/org/apache/log4j/RFATestCase.java (revision 544346) >+++ tests/src/java/org/apache/log4j/RFATestCase.java (working copy) >@@ -233,5 +233,63 @@ > } > } > >+ /** >+ * Test basic rolling functionality with backup directory using property file configuration. >+ */ >+ public void testBackupDirectoryFromConfigFile() throws Exception { >+ Logger logger = Logger.getLogger(RFATestCase.class); >+ PropertyConfigurator.configure("input/RFA2.properties"); > >+ // Write exactly 10 bytes with each log >+ for (int i = 0; i < 25; i++) { >+ if (i < 10) { >+ logger.debug("Hello---" + i); >+ } else if (i < 100) { >+ logger.debug("Hello--" + i); >+ } >+ } >+ >+ assertTrue(new File("output/RFA-testBackup.log").exists()); >+ assertFalse(new File("output/RFA-testBackup.log.1").exists()); >+ assertTrue(new File("output/archive/RFA-testBackup.log.1").exists()); >+ } >+ >+ /** >+ * Test basic rolling functionality with a backup directory using API configuration. >+ */ >+ public void testBackupDirectoryThroughAPI() throws Exception { >+ Logger logger = Logger.getLogger(RFATestCase.class); >+ Logger root = Logger.getRootLogger(); >+ PatternLayout layout = new PatternLayout("%m\n"); >+ org.apache.log4j.RollingFileAppender rfa = >+ new org.apache.log4j.RollingFileAppender(); >+ rfa.setName("ROLLING"); >+ rfa.setLayout(layout); >+ rfa.setAppend(false); >+ rfa.setMaxBackupIndex(3); >+ rfa.setMaximumFileSize(100); >+ rfa.setFile("output/RFA-testBackup2.log"); >+ rfa.setBackupDirectory("output/archive2"); >+ rfa.activateOptions(); >+ root.addAppender(rfa); >+ >+ // Write exactly 10 bytes with each log >+ for (int i = 0; i < 55; i++) { >+ if (i < 10) { >+ logger.debug("Hello---" + i); >+ } else if (i < 100) { >+ logger.debug("Hello--" + i); >+ } >+ } >+ >+ assertTrue(new File("output/RFA-testBackup2.log").exists()); >+ assertFalse(new File("output/RFA-testBackup2.log.1").exists()); >+ assertTrue(new File("output/archive2/RFA-testBackup2.log.1").exists()); >+ assertFalse(new File("output/RFA-testBackup2.log.2").exists()); >+ assertTrue(new File("output/archive2/RFA-testBackup2.log.2").exists()); >+ assertFalse(new File("output/RFA-testBackup2.log.3").exists()); >+ assertTrue(new File("output/archive2/RFA-testBackup2.log.3").exists()); >+ assertFalse(new File("output/RFA-testBackup2.log.4").exists()); >+ assertFalse(new File("output/archive2/RFA-testBackup2.log.4").exists()); >+ } > }
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 42598
: 20317