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><handler-name></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><handler-name></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
|