1 /* 2 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 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.LinkOption; 42 import java.nio.file.NoSuchFileException; 43 import java.nio.file.Path; 44 import java.nio.file.Paths; 45 import java.security.AccessController; 46 import java.security.PrivilegedAction; 47 import java.util.HashSet; 48 import java.util.Set; 49 50 /** 51 * Simple file logging {@code Handler}. 52 * <p> 53 * The {@code FileHandler} can either write to a specified file, 54 * or it can write to a rotating set of files. 55 * <p> 56 * For a rotating set of files, as each file reaches a given size 57 * limit, it is closed, rotated out, and a new file opened. 58 * Successively older files are named by adding "0", "1", "2", 59 * etc. into the base filename. 60 * <p> 61 * By default buffering is enabled in the IO libraries but each log 62 * record is flushed out when it is complete. 63 * <p> 64 * By default the {@code XMLFormatter} class is used for formatting. 65 * <p> 66 * <b>Configuration:</b> 67 * By default each {@code FileHandler} is initialized using the following 68 * {@code LogManager} configuration properties where {@code <handler-name>} 69 * refers to the fully-qualified class name of the handler. 70 * If properties are not defined 71 * (or have invalid values) then the specified default values are used. 72 * <ul> 73 * <li> <handler-name>.level 74 * specifies the default level for the {@code Handler} 75 * (defaults to {@code Level.ALL}). </li> 76 * <li> <handler-name>.filter 77 * specifies the name of a {@code Filter} class to use 78 * (defaults to no {@code Filter}). </li> 79 * <li> <handler-name>.formatter 80 * specifies the name of a {@code Formatter} class to use 81 * (defaults to {@code java.util.logging.XMLFormatter}) </li> 82 * <li> <handler-name>.encoding 83 * the name of the character set encoding to use (defaults to 84 * the default platform encoding). </li> 85 * <li> <handler-name>.limit 86 * specifies an approximate maximum amount to write (in bytes) 87 * to any one file. If this is zero, then there is no limit. 88 * (Defaults to no limit). </li> 89 * <li> <handler-name>.count 90 * specifies how many output files to cycle through (defaults to 1). </li> 91 * <li> <handler-name>.pattern 92 * specifies a pattern for generating the output file name. See 93 * below for details. (Defaults to "%h/java%u.log"). </li> 94 * <li> <handler-name>.append 95 * specifies whether the FileHandler should append onto 96 * any existing files (defaults to false). </li> 97 * <li> <handler-name>.maxLocks 98 * specifies the maximum number of concurrent locks held by 99 * FileHandler (defaults to 100). </li> 100 * </ul> 101 * <p> 102 * For example, the properties for {@code FileHandler} would be: 103 * <ul> 104 * <li> java.util.logging.FileHandler.level=INFO </li> 105 * <li> java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter </li> 106 * </ul> 107 * <p> 108 * For a custom handler, e.g. com.foo.MyHandler, the properties would be: 109 * <ul> 110 * <li> com.foo.MyHandler.level=INFO </li> 111 * <li> com.foo.MyHandler.formatter=java.util.logging.SimpleFormatter </li> 112 * </ul> 113 * <p> 114 * A pattern consists of a string that includes the following special 115 * components that will be replaced at runtime: 116 * <ul> 117 * <li> "/" the local pathname separator </li> 118 * <li> "%t" the system temporary directory </li> 119 * <li> "%h" the value of the "user.home" system property </li> 120 * <li> "%g" the generation number to distinguish rotated logs </li> 121 * <li> "%u" a unique number to resolve conflicts </li> 122 * <li> "%%" translates to a single percent sign "%" </li> 123 * </ul> 124 * If no "%g" field has been specified and the file count is greater 125 * than one, then the generation number will be added to the end of 126 * the generated filename, after a dot. 127 * <p> 128 * Thus for example a pattern of "%t/java%g.log" with a count of 2 129 * would typically cause log files to be written on Solaris to 130 * /var/tmp/java0.log and /var/tmp/java1.log whereas on Windows 95 they 131 * would be typically written to C:\TEMP\java0.log and C:\TEMP\java1.log 132 * <p> 133 * Generation numbers follow the sequence 0, 1, 2, etc. 134 * <p> 135 * Normally the "%u" unique field is set to 0. However, if the {@code FileHandler} 136 * tries to open the filename and finds the file is currently in use by 137 * another process it will increment the unique number field and try 138 * again. This will be repeated until {@code FileHandler} finds a file name that 139 * is not currently in use. If there is a conflict and no "%u" field has 140 * been specified, it will be added at the end of the filename after a dot. 141 * (This will be after any automatically added generation number.) 142 * <p> 143 * Thus if three processes were all trying to log to fred%u.%g.txt then 144 * they might end up using fred0.0.txt, fred1.0.txt, fred2.0.txt as 145 * the first file in their rotating sequences. 146 * <p> 147 * Note that the use of unique ids to avoid conflicts is only guaranteed 148 * to work reliably when using a local disk file system. 149 * 150 * @since 1.4 151 */ 152 153 public class FileHandler extends StreamHandler { 154 private MeteredStream meter; 155 private boolean append; 156 private long limit; // zero => no limit. 157 private int count; 158 private String pattern; 159 private String lockFileName; 160 private FileChannel lockFileChannel; 161 private File files[]; 162 private static final int MAX_LOCKS = 100; 163 private int maxLocks = MAX_LOCKS; 164 private static final Set<String> locks = new HashSet<>(); 165 166 /** 167 * A metered stream is a subclass of OutputStream that 168 * (a) forwards all its output to a target stream 169 * (b) keeps track of how many bytes have been written 170 */ 171 private static final class MeteredStream extends OutputStream { 172 final OutputStream out; 173 long written; 174 175 MeteredStream(OutputStream out, long written) { 176 this.out = out; 177 this.written = written; 178 } 179 180 @Override 181 public void write(int b) throws IOException { 182 out.write(b); 183 written++; 184 } 185 186 @Override 187 public void write(byte buff[]) throws IOException { 188 out.write(buff); 189 written += buff.length; 190 } 191 192 @Override 193 public void write(byte buff[], int off, int len) throws IOException { 194 out.write(buff,off,len); 195 written += len; 196 } 197 198 @Override 199 public void flush() throws IOException { 200 out.flush(); 201 } 202 203 @Override 204 public void close() throws IOException { 205 out.close(); 206 } 207 } 208 209 private void open(File fname, boolean append) throws IOException { 210 long len = 0; 211 if (append) { 212 len = fname.length(); 213 } 214 FileOutputStream fout = new FileOutputStream(fname.toString(), append); 215 BufferedOutputStream bout = new BufferedOutputStream(fout); 216 meter = new MeteredStream(bout, len); 217 setOutputStream(meter); 218 } 219 220 /** 221 * Configure a FileHandler from LogManager properties and/or default values 222 * as specified in the class javadoc. 223 */ 224 private void configure() { 225 LogManager manager = LogManager.getLogManager(); 226 227 String cname = getClass().getName(); 228 229 pattern = manager.getStringProperty(cname + ".pattern", "%h/java%u.log"); 230 limit = manager.getLongProperty(cname + ".limit", 0); 231 if (limit < 0) { 232 limit = 0; 233 } 234 count = manager.getIntProperty(cname + ".count", 1); 235 if (count <= 0) { 236 count = 1; 237 } 238 append = manager.getBooleanProperty(cname + ".append", false); 239 setLevel(manager.getLevelProperty(cname + ".level", Level.ALL)); 240 setFilter(manager.getFilterProperty(cname + ".filter", null)); 241 setFormatter(manager.getFormatterProperty(cname + ".formatter", new XMLFormatter())); 242 // Initialize maxLocks from the logging.properties file. 243 // If invalid/no property is provided 100 will be used as a default value. 244 maxLocks = manager.getIntProperty(cname + ".maxLocks", MAX_LOCKS); 245 if(maxLocks <= 0) { 246 maxLocks = MAX_LOCKS; 247 } 248 try { 249 setEncoding(manager.getStringProperty(cname +".encoding", null)); 250 } catch (Exception ex) { 251 try { 252 setEncoding(null); 253 } catch (Exception ex2) { 254 // doing a setEncoding with null should always work. 255 // assert false; 256 } 257 } 258 } 259 260 261 /** 262 * Construct a default {@code FileHandler}. This will be configured 263 * entirely from {@code LogManager} properties (or their default values). 264 * 265 * @exception IOException if there are IO problems opening the files. 266 * @exception SecurityException if a security manager exists and if 267 * the caller does not have {@code LoggingPermission("control"))}. 268 * @exception NullPointerException if pattern property is an empty String. 269 */ 270 public FileHandler() throws IOException, SecurityException { 271 checkPermission(); 272 configure(); 273 // pattern will have been set by configure. check that it's not 274 // empty. 275 if (pattern.isEmpty()) { 276 throw new NullPointerException(); 277 } 278 openFiles(); 279 } 280 281 /** 282 * Initialize a {@code FileHandler} to write to the given filename. 283 * <p> 284 * The {@code FileHandler} is configured based on {@code LogManager} 285 * properties (or their default values) except that the given pattern 286 * argument is used as the filename pattern, the file limit is 287 * set to no limit, and the file count is set to one. 288 * <p> 289 * There is no limit on the amount of data that may be written, 290 * so use this with care. 291 * 292 * @param pattern the name of the output file 293 * @exception IOException if there are IO problems opening the files. 294 * @exception SecurityException if a security manager exists and if 295 * the caller does not have {@code LoggingPermission("control")}. 296 * @exception IllegalArgumentException if pattern is an empty string 297 */ 298 public FileHandler(String pattern) throws IOException, SecurityException { 299 if (pattern.length() < 1 ) { 300 throw new IllegalArgumentException(); 301 } 302 checkPermission(); 303 configure(); 304 this.pattern = pattern; 305 this.limit = 0; 306 this.count = 1; 307 openFiles(); 308 } 309 310 /** 311 * Initialize a {@code FileHandler} to write to the given filename, 312 * with optional append. 313 * <p> 314 * The {@code FileHandler} is configured based on {@code LogManager} 315 * properties (or their default values) except that the given pattern 316 * argument is used as the filename pattern, the file limit is 317 * set to no limit, the file count is set to one, and the append 318 * mode is set to the given {@code append} argument. 319 * <p> 320 * There is no limit on the amount of data that may be written, 321 * so use this with care. 322 * 323 * @param pattern the name of the output file 324 * @param append specifies append mode 325 * @exception IOException if there are IO problems opening the files. 326 * @exception SecurityException if a security manager exists and if 327 * the caller does not have {@code LoggingPermission("control")}. 328 * @exception IllegalArgumentException if pattern is an empty string 329 */ 330 public FileHandler(String pattern, boolean append) throws IOException, 331 SecurityException { 332 if (pattern.length() < 1 ) { 333 throw new IllegalArgumentException(); 334 } 335 checkPermission(); 336 configure(); 337 this.pattern = pattern; 338 this.limit = 0; 339 this.count = 1; 340 this.append = append; 341 openFiles(); 342 } 343 344 /** 345 * Initialize a {@code FileHandler} to write to a set of files. When 346 * (approximately) the given limit has been written to one file, 347 * another file will be opened. The output will cycle through a set 348 * of count files. 349 * <p> 350 * The {@code FileHandler} is configured based on {@code LogManager} 351 * properties (or their default values) except that the given pattern 352 * argument is used as the filename pattern, the file limit is 353 * set to the limit argument, and the file count is set to the 354 * given count argument. 355 * <p> 356 * The count must be at least 1. 357 * 358 * @param pattern the pattern for naming the output file 359 * @param limit the maximum number of bytes to write to any one file 360 * @param count the number of files to use 361 * @exception IOException if there are IO problems opening the files. 362 * @exception SecurityException if a security manager exists and if 363 * the caller does not have {@code LoggingPermission("control")}. 364 * @exception IllegalArgumentException if {@code limit < 0}, or {@code count < 1}. 365 * @exception IllegalArgumentException if pattern is an empty string 366 */ 367 public FileHandler(String pattern, int limit, int count) 368 throws IOException, SecurityException { 369 if (limit < 0 || count < 1 || pattern.length() < 1) { 370 throw new IllegalArgumentException(); 371 } 372 checkPermission(); 373 configure(); 374 this.pattern = pattern; 375 this.limit = limit; 376 this.count = count; 377 openFiles(); 378 } 379 380 /** 381 * Initialize a {@code FileHandler} to write to a set of files 382 * with optional append. When (approximately) the given limit has 383 * been written to one file, another file will be opened. The 384 * output will cycle through a set of count files. 385 * <p> 386 * The {@code FileHandler} is configured based on {@code LogManager} 387 * properties (or their default values) except that the given pattern 388 * argument is used as the filename pattern, the file limit is 389 * set to the limit argument, and the file count is set to the 390 * given count argument, and the append mode is set to the given 391 * {@code append} argument. 392 * <p> 393 * The count must be at least 1. 394 * 395 * @param pattern the pattern for naming the output file 396 * @param limit the maximum number of bytes to write to any one file 397 * @param count the number of files to use 398 * @param append specifies append mode 399 * @exception IOException if there are IO problems opening the files. 400 * @exception SecurityException if a security manager exists and if 401 * the caller does not have {@code LoggingPermission("control")}. 402 * @exception IllegalArgumentException if {@code limit < 0}, or {@code count < 1}. 403 * @exception IllegalArgumentException if pattern is an empty string 404 * 405 */ 406 public FileHandler(String pattern, int limit, int count, boolean append) 407 throws IOException, SecurityException { 408 this(pattern, (long)limit, count, append); 409 } 410 411 /** 412 * Initialize a {@code FileHandler} to write to a set of files 413 * with optional append. When (approximately) the given limit has 414 * been written to one file, another file will be opened. The 415 * output will cycle through a set of count files. 416 * <p> 417 * The {@code FileHandler} is configured based on {@code LogManager} 418 * properties (or their default values) except that the given pattern 419 * argument is used as the filename pattern, the file limit is 420 * set to the limit argument, and the file count is set to the 421 * given count argument, and the append mode is set to the given 422 * {@code append} argument. 423 * <p> 424 * The count must be at least 1. 425 * 426 * @param pattern the pattern for naming the output file 427 * @param limit the maximum number of bytes to write to any one file 428 * @param count the number of files to use 429 * @param append specifies append mode 430 * @exception IOException if there are IO problems opening the files. 431 * @exception SecurityException if a security manager exists and if 432 * the caller does not have {@code LoggingPermission("control")}. 433 * @exception IllegalArgumentException if {@code limit < 0}, or {@code count < 1}. 434 * @exception IllegalArgumentException if pattern is an empty string 435 * 436 * @since 9 437 * 438 */ 439 public FileHandler(String pattern, long limit, int count, boolean append) 440 throws IOException { 441 if (limit < 0 || count < 1 || pattern.length() < 1) { 442 throw new IllegalArgumentException(); 443 } 444 checkPermission(); 445 configure(); 446 this.pattern = pattern; 447 this.limit = limit; 448 this.count = count; 449 this.append = append; 450 openFiles(); 451 } 452 453 private boolean isParentWritable(Path path) { 454 Path parent = path.getParent(); 455 if (parent == null) { 456 parent = path.toAbsolutePath().getParent(); 457 } 458 return parent != null && Files.isWritable(parent); 459 } 460 461 /** 462 * Open the set of output files, based on the configured 463 * instance variables. 464 */ 465 private void openFiles() throws IOException { 466 LogManager manager = LogManager.getLogManager(); 467 manager.checkPermission(); 468 if (count < 1) { 469 throw new IllegalArgumentException("file count = " + count); 470 } 471 if (limit < 0) { 472 limit = 0; 473 } 474 475 // All constructors check that pattern is neither null nor empty. 476 assert pattern != null : "pattern should not be null"; 477 assert !pattern.isEmpty() : "pattern should not be empty"; 478 479 // We register our own ErrorManager during initialization 480 // so we can record exceptions. 481 InitializationErrorManager em = new InitializationErrorManager(); 482 setErrorManager(em); 483 484 // Create a lock file. This grants us exclusive access 485 // to our set of output files, as long as we are alive. 486 int unique = -1; 487 for (;;) { 488 unique++; 489 if (unique > maxLocks) { 490 throw new IOException("Couldn't get lock for " + pattern); 491 } 492 // Generate a lock file name from the "unique" int. 493 lockFileName = generate(pattern, 0, unique).toString() + ".lck"; 494 // Now try to lock that filename. 495 // Because some systems (e.g., Solaris) can only do file locks 496 // between processes (and not within a process), we first check 497 // if we ourself already have the file locked. 498 synchronized(locks) { 499 if (locks.contains(lockFileName)) { 500 // We already own this lock, for a different FileHandler 501 // object. Try again. 502 continue; 503 } 504 505 final Path lockFilePath = Paths.get(lockFileName); 506 FileChannel channel = null; 507 int retries = -1; 508 boolean fileCreated = false; 509 while (channel == null && retries++ < 1) { 510 try { 511 channel = FileChannel.open(lockFilePath, 512 CREATE_NEW, WRITE); 513 fileCreated = true; 514 } catch (FileAlreadyExistsException ix) { 515 // This may be a zombie file left over by a previous 516 // execution. Reuse it - but only if we can actually 517 // write to its directory. 518 // Note that this is a situation that may happen, 519 // but not too frequently. 520 if (Files.isRegularFile(lockFilePath, LinkOption.NOFOLLOW_LINKS) 521 && isParentWritable(lockFilePath)) { 522 try { 523 channel = FileChannel.open(lockFilePath, 524 WRITE, APPEND); 525 } catch (NoSuchFileException x) { 526 // Race condition - retry once, and if that 527 // fails again just try the next name in 528 // the sequence. 529 continue; 530 } catch(IOException x) { 531 // the file may not be writable for us. 532 // try the next name in the sequence 533 break; 534 } 535 } else { 536 // at this point channel should still be null. 537 // break and try the next name in the sequence. 538 break; 539 } 540 } 541 } 542 543 if (channel == null) continue; // try the next name; 544 lockFileChannel = channel; 545 546 boolean available; 547 try { 548 available = lockFileChannel.tryLock() != null; 549 // We got the lock OK. 550 // At this point we could call File.deleteOnExit(). 551 // However, this could have undesirable side effects 552 // as indicated by JDK-4872014. So we will instead 553 // rely on the fact that close() will remove the lock 554 // file and that whoever is creating FileHandlers should 555 // be responsible for closing them. 556 } catch (IOException ix) { 557 // We got an IOException while trying to get the lock. 558 // This normally indicates that locking is not supported 559 // on the target directory. We have to proceed without 560 // getting a lock. Drop through, but only if we did 561 // create the file... 562 available = fileCreated; 563 } catch (OverlappingFileLockException x) { 564 // someone already locked this file in this VM, through 565 // some other channel - that is - using something else 566 // than new FileHandler(...); 567 // continue searching for an available lock. 568 available = false; 569 } 570 if (available) { 571 // We got the lock. Remember it. 572 locks.add(lockFileName); 573 break; 574 } 575 576 // We failed to get the lock. Try next file. 577 lockFileChannel.close(); 578 } 579 } 580 581 files = new File[count]; 582 for (int i = 0; i < count; i++) { 583 files[i] = generate(pattern, i, unique); 584 } 585 586 // Create the initial log file. 587 if (append) { 588 open(files[0], true); 589 } else { 590 rotate(); 591 } 592 593 // Did we detect any exceptions during initialization? 594 Exception ex = em.lastException; 595 if (ex != null) { 596 if (ex instanceof IOException) { 597 throw (IOException) ex; 598 } else if (ex instanceof SecurityException) { 599 throw (SecurityException) ex; 600 } else { 601 throw new IOException("Exception: " + ex); 602 } 603 } 604 605 // Install the normal default ErrorManager. 606 setErrorManager(new ErrorManager()); 607 } 608 609 /** 610 * Generate a file based on a user-supplied pattern, generation number, 611 * and an integer uniqueness suffix 612 * @param pattern the pattern for naming the output file 613 * @param generation the generation number to distinguish rotated logs 614 * @param unique a unique number to resolve conflicts 615 * @return the generated File 616 * @throws IOException 617 */ 618 private File generate(String pattern, int generation, int unique) 619 throws IOException { 620 File file = null; 621 String word = ""; 622 int ix = 0; 623 boolean sawg = false; 624 boolean sawu = false; 625 while (ix < pattern.length()) { 626 char ch = pattern.charAt(ix); 627 ix++; 628 char ch2 = 0; 629 if (ix < pattern.length()) { 630 ch2 = Character.toLowerCase(pattern.charAt(ix)); 631 } 632 if (ch == '/') { 633 if (file == null) { 634 file = new File(word); 635 } else { 636 file = new File(file, word); 637 } 638 word = ""; 639 continue; 640 } else if (ch == '%') { 641 if (ch2 == 't') { 642 String tmpDir = System.getProperty("java.io.tmpdir"); 643 if (tmpDir == null) { 644 tmpDir = System.getProperty("user.home"); 645 } 646 file = new File(tmpDir); 647 ix++; 648 word = ""; 649 continue; 650 } else if (ch2 == 'h') { 651 file = new File(System.getProperty("user.home")); 652 if (jdk.internal.misc.VM.isSetUID()) { 653 // Ok, we are in a set UID program. For safety's sake 654 // we disallow attempts to open files relative to %h. 655 throw new IOException("can't use %h in set UID program"); 656 } 657 ix++; 658 word = ""; 659 continue; 660 } else if (ch2 == 'g') { 661 word = word + generation; 662 sawg = true; 663 ix++; 664 continue; 665 } else if (ch2 == 'u') { 666 word = word + unique; 667 sawu = true; 668 ix++; 669 continue; 670 } else if (ch2 == '%') { 671 word = word + "%"; 672 ix++; 673 continue; 674 } 675 } 676 word = word + ch; 677 } 678 if (count > 1 && !sawg) { 679 word = word + "." + generation; 680 } 681 if (unique > 0 && !sawu) { 682 word = word + "." + unique; 683 } 684 if (word.length() > 0) { 685 if (file == null) { 686 file = new File(word); 687 } else { 688 file = new File(file, word); 689 } 690 } 691 return file; 692 } 693 694 /** 695 * Rotate the set of output files 696 */ 697 private synchronized void rotate() { 698 Level oldLevel = getLevel(); 699 setLevel(Level.OFF); 700 701 super.close(); 702 for (int i = count-2; i >= 0; i--) { 703 File f1 = files[i]; 704 File f2 = files[i+1]; 705 if (f1.exists()) { 706 if (f2.exists()) { 707 f2.delete(); 708 } 709 f1.renameTo(f2); 710 } 711 } 712 try { 713 open(files[0], false); 714 } catch (IOException ix) { 715 // We don't want to throw an exception here, but we 716 // report the exception to any registered ErrorManager. 717 reportError(null, ix, ErrorManager.OPEN_FAILURE); 718 719 } 720 setLevel(oldLevel); 721 } 722 723 /** 724 * Format and publish a {@code LogRecord}. 725 * 726 * @param record description of the log event. A null record is 727 * silently ignored and is not published 728 */ 729 @Override 730 public synchronized void publish(LogRecord record) { 731 if (!isLoggable(record)) { 732 return; 733 } 734 super.publish(record); 735 flush(); 736 if (limit > 0 && (meter.written >= limit || meter.written < 0)) { 737 // We performed access checks in the "init" method to make sure 738 // we are only initialized from trusted code. So we assume 739 // it is OK to write the target files, even if we are 740 // currently being called from untrusted code. 741 // So it is safe to raise privilege here. 742 AccessController.doPrivileged(new PrivilegedAction<Object>() { 743 @Override 744 public Object run() { 745 rotate(); 746 return null; 747 } 748 }); 749 } 750 } 751 752 /** 753 * Close all the files. 754 * 755 * @exception SecurityException if a security manager exists and if 756 * the caller does not have {@code LoggingPermission("control")}. 757 */ 758 @Override 759 public synchronized void close() throws SecurityException { 760 super.close(); 761 // Unlock any lock file. 762 if (lockFileName == null) { 763 return; 764 } 765 try { 766 // Close the lock file channel (which also will free any locks) 767 lockFileChannel.close(); 768 } catch (Exception ex) { 769 // Problems closing the stream. Punt. 770 } 771 synchronized(locks) { 772 locks.remove(lockFileName); 773 } 774 new File(lockFileName).delete(); 775 lockFileName = null; 776 lockFileChannel = null; 777 } 778 779 private static class InitializationErrorManager extends ErrorManager { 780 Exception lastException; 781 @Override 782 public void error(String msg, Exception ex, int code) { 783 lastException = ex; 784 } 785 } 786 }