src/share/classes/java/util/logging/FileHandler.java

Print this page




   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.util.logging;
  27 

  28 import static java.nio.file.StandardOpenOption.CREATE_NEW;
  29 import static java.nio.file.StandardOpenOption.WRITE;
  30 
  31 import java.io.BufferedOutputStream;
  32 import java.io.File;
  33 import java.io.FileOutputStream;
  34 import java.io.IOException;
  35 import java.io.OutputStream;
  36 import java.nio.channels.FileChannel;

  37 import java.nio.file.FileAlreadyExistsException;


  38 import java.nio.file.Paths;
  39 import java.security.AccessController;
  40 import java.security.PrivilegedAction;


  41 
  42 /**
  43  * Simple file logging <tt>Handler</tt>.
  44  * <p>
  45  * The <tt>FileHandler</tt> can either write to a specified file,
  46  * or it can write to a rotating set of files.
  47  * <p>
  48  * For a rotating set of files, as each file reaches a given size
  49  * limit, it is closed, rotated out, and a new file opened.
  50  * Successively older files are named by adding "0", "1", "2",
  51  * etc. into the base filename.
  52  * <p>
  53  * By default buffering is enabled in the IO libraries but each log
  54  * record is flushed out when it is complete.
  55  * <p>
  56  * By default the <tt>XMLFormatter</tt> class is used for formatting.
  57  * <p>
  58  * <b>Configuration:</b>
  59  * By default each <tt>FileHandler</tt> is initialized using the following
  60  * <tt>LogManager</tt> configuration properties where <tt>&lt;handler-name&gt;</tt>


 132  * Thus if three processes were all trying to log to fred%u.%g.txt then
 133  * they  might end up using fred0.0.txt, fred1.0.txt, fred2.0.txt as
 134  * the first file in their rotating sequences.
 135  * <p>
 136  * Note that the use of unique ids to avoid conflicts is only guaranteed
 137  * to work reliably when using a local disk file system.
 138  *
 139  * @since 1.4
 140  */
 141 
 142 public class FileHandler extends StreamHandler {
 143     private MeteredStream meter;
 144     private boolean append;
 145     private int limit;       // zero => no limit.
 146     private int count;
 147     private String pattern;
 148     private String lockFileName;
 149     private FileChannel lockFileChannel;
 150     private File files[];
 151     private static final int MAX_LOCKS = 100;
 152     private static final java.util.HashMap<String, String> locks = new java.util.HashMap<>();
 153 
 154     /**
 155      * A metered stream is a subclass of OutputStream that
 156      * (a) forwards all its output to a target stream
 157      * (b) keeps track of how many bytes have been written
 158      */
 159     private class MeteredStream extends OutputStream {
 160         final OutputStream out;
 161         int written;
 162 
 163         MeteredStream(OutputStream out, int written) {
 164             this.out = out;
 165             this.written = written;
 166         }
 167 
 168         @Override
 169         public void write(int b) throws IOException {
 170             out.write(b);
 171             written++;
 172         }


 411         // We register our own ErrorManager during initialization
 412         // so we can record exceptions.
 413         InitializationErrorManager em = new InitializationErrorManager();
 414         setErrorManager(em);
 415 
 416         // Create a lock file.  This grants us exclusive access
 417         // to our set of output files, as long as we are alive.
 418         int unique = -1;
 419         for (;;) {
 420             unique++;
 421             if (unique > MAX_LOCKS) {
 422                 throw new IOException("Couldn't get lock for " + pattern);
 423             }
 424             // Generate a lock file name from the "unique" int.
 425             lockFileName = generate(pattern, 0, unique).toString() + ".lck";
 426             // Now try to lock that filename.
 427             // Because some systems (e.g., Solaris) can only do file locks
 428             // between processes (and not within a process), we first check
 429             // if we ourself already have the file locked.
 430             synchronized(locks) {
 431                 if (locks.get(lockFileName) != null) {
 432                     // We already own this lock, for a different FileHandler
 433                     // object.  Try again.
 434                     continue;
 435                 }
 436 


 437                 try {
 438                     lockFileChannel = FileChannel.open(Paths.get(lockFileName),
 439                             CREATE_NEW, WRITE);

 440                 } catch (FileAlreadyExistsException ix) {










 441                     // try the next lock file name in the sequence
 442                     continue;
 443                 }

 444 
 445                 boolean available;
 446                 try {
 447                     available = lockFileChannel.tryLock() != null;
 448                     // We got the lock OK.






 449                 } catch (IOException ix) {
 450                     // We got an IOException while trying to get the lock.
 451                     // This normally indicates that locking is not supported
 452                     // on the target directory.  We have to proceed without
 453                     // getting a lock.   Drop through.
 454                     available = true;





 455                 }
 456                 if (available) {
 457                     // We got the lock.  Remember it.
 458                     locks.put(lockFileName, lockFileName);
 459                     break;
 460                 }
 461 
 462                 // We failed to get the lock.  Try next file.
 463                 lockFileChannel.close();
 464             }
 465         }
 466 
 467         files = new File[count];
 468         for (int i = 0; i < count; i++) {
 469             files[i] = generate(pattern, i, unique);
 470         }
 471 
 472         // Create the initial log file.
 473         if (append) {
 474             open(files[0], true);
 475         } else {
 476             rotate();
 477         }
 478 




   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.util.logging;
  27 
  28 import static java.nio.file.StandardOpenOption.APPEND;
  29 import static java.nio.file.StandardOpenOption.CREATE_NEW;
  30 import static java.nio.file.StandardOpenOption.WRITE;
  31 
  32 import java.io.BufferedOutputStream;
  33 import java.io.File;
  34 import java.io.FileOutputStream;
  35 import java.io.IOException;
  36 import java.io.OutputStream;
  37 import java.nio.channels.FileChannel;
  38 import java.nio.channels.OverlappingFileLockException;
  39 import java.nio.file.FileAlreadyExistsException;
  40 import java.nio.file.Files;
  41 import java.nio.file.Path;
  42 import java.nio.file.Paths;
  43 import java.security.AccessController;
  44 import java.security.PrivilegedAction;
  45 import java.util.HashSet;
  46 import java.util.Set;
  47 
  48 /**
  49  * Simple file logging <tt>Handler</tt>.
  50  * <p>
  51  * The <tt>FileHandler</tt> can either write to a specified file,
  52  * or it can write to a rotating set of files.
  53  * <p>
  54  * For a rotating set of files, as each file reaches a given size
  55  * limit, it is closed, rotated out, and a new file opened.
  56  * Successively older files are named by adding "0", "1", "2",
  57  * etc. into the base filename.
  58  * <p>
  59  * By default buffering is enabled in the IO libraries but each log
  60  * record is flushed out when it is complete.
  61  * <p>
  62  * By default the <tt>XMLFormatter</tt> class is used for formatting.
  63  * <p>
  64  * <b>Configuration:</b>
  65  * By default each <tt>FileHandler</tt> is initialized using the following
  66  * <tt>LogManager</tt> configuration properties where <tt>&lt;handler-name&gt;</tt>


 138  * Thus if three processes were all trying to log to fred%u.%g.txt then
 139  * they  might end up using fred0.0.txt, fred1.0.txt, fred2.0.txt as
 140  * the first file in their rotating sequences.
 141  * <p>
 142  * Note that the use of unique ids to avoid conflicts is only guaranteed
 143  * to work reliably when using a local disk file system.
 144  *
 145  * @since 1.4
 146  */
 147 
 148 public class FileHandler extends StreamHandler {
 149     private MeteredStream meter;
 150     private boolean append;
 151     private int limit;       // zero => no limit.
 152     private int count;
 153     private String pattern;
 154     private String lockFileName;
 155     private FileChannel lockFileChannel;
 156     private File files[];
 157     private static final int MAX_LOCKS = 100;
 158     private static final Set<String> locks = new HashSet<>();
 159 
 160     /**
 161      * A metered stream is a subclass of OutputStream that
 162      * (a) forwards all its output to a target stream
 163      * (b) keeps track of how many bytes have been written
 164      */
 165     private class MeteredStream extends OutputStream {
 166         final OutputStream out;
 167         int written;
 168 
 169         MeteredStream(OutputStream out, int written) {
 170             this.out = out;
 171             this.written = written;
 172         }
 173 
 174         @Override
 175         public void write(int b) throws IOException {
 176             out.write(b);
 177             written++;
 178         }


 417         // We register our own ErrorManager during initialization
 418         // so we can record exceptions.
 419         InitializationErrorManager em = new InitializationErrorManager();
 420         setErrorManager(em);
 421 
 422         // Create a lock file.  This grants us exclusive access
 423         // to our set of output files, as long as we are alive.
 424         int unique = -1;
 425         for (;;) {
 426             unique++;
 427             if (unique > MAX_LOCKS) {
 428                 throw new IOException("Couldn't get lock for " + pattern);
 429             }
 430             // Generate a lock file name from the "unique" int.
 431             lockFileName = generate(pattern, 0, unique).toString() + ".lck";
 432             // Now try to lock that filename.
 433             // Because some systems (e.g., Solaris) can only do file locks
 434             // between processes (and not within a process), we first check
 435             // if we ourself already have the file locked.
 436             synchronized(locks) {
 437                 if (locks.contains(lockFileName)) {
 438                     // We already own this lock, for a different FileHandler
 439                     // object.  Try again.
 440                     continue;
 441                 }
 442 
 443                 final Path lockFilePath = Paths.get(lockFileName);
 444                 boolean fileCreated;
 445                 try {
 446                     lockFileChannel = FileChannel.open(lockFilePath,
 447                             CREATE_NEW, WRITE);
 448                     fileCreated = true;
 449                 } catch (FileAlreadyExistsException ix) {
 450                     // This may be a zombie file left over by a previous
 451                     // execution. Reuse it - but only if we can actually
 452                     // write to its directory.
 453                     if (Files.isRegularFile(lockFilePath) 
 454                             && Files.isWritable(lockFilePath)
 455                             && Files.isWritable(lockFilePath.getParent())) {
 456                         lockFileChannel = FileChannel.open(lockFilePath, 
 457                                 WRITE, APPEND);
 458                         fileCreated = false;
 459                     } else {
 460                         // try the next lock file name in the sequence
 461                         continue;
 462                     }
 463                 }
 464 
 465                 boolean available;
 466                 try {
 467                     available = lockFileChannel.tryLock() != null;
 468                     // We got the lock OK.
 469                     // At this point we could call File.deleteOnExit().
 470                     // However, this could have undesirable side effects
 471                     // as indicated by JDK-4872014. So we will instead
 472                     // rely on the fact that close() will remove the lock
 473                     // file and that whoever is creating FileHandlers should
 474                     // be responsible for closing them.
 475                 } catch (IOException ix) {
 476                     // We got an IOException while trying to get the lock.
 477                     // This normally indicates that locking is not supported
 478                     // on the target directory.  We have to proceed without
 479                     // getting a lock.   Drop through, but only if we did
 480                     // create the file...
 481                     available = fileCreated;
 482                 } catch (OverlappingFileLockException x) {
 483                     // someone already locked this file in this VM.
 484                     // continue searching for an available lock.
 485                     available = false;
 486                 }
 487                 if (available) {
 488                     // We got the lock.  Remember it.
 489                     locks.add(lockFileName);
 490                     break;
 491                 }
 492 
 493                 // We failed to get the lock.  Try next file.
 494                 lockFileChannel.close();
 495             }
 496         }
 497 
 498         files = new File[count];
 499         for (int i = 0; i < count; i++) {
 500             files[i] = generate(pattern, i, unique);
 501         }
 502 
 503         // Create the initial log file.
 504         if (append) {
 505             open(files[0], true);
 506         } else {
 507             rotate();
 508         }
 509