Home » openjdk-7 » java » util » logging » [javadoc | source]

    1   /*
    2    * Copyright (c) 2000, 2010, 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 java.io;
   29   import java.nio.channels.FileChannel;
   30   import java.nio.channels.FileLock;
   31   import java.security;
   32   
   33   /**
   34    * Simple file logging <tt>Handler</tt>.
   35    * <p>
   36    * The <tt>FileHandler</tt> can either write to a specified file,
   37    * or it can write to a rotating set of files.
   38    * <p>
   39    * For a rotating set of files, as each file reaches a given size
   40    * limit, it is closed, rotated out, and a new file opened.
   41    * Successively older files are named by adding "0", "1", "2",
   42    * etc. into the base filename.
   43    * <p>
   44    * By default buffering is enabled in the IO libraries but each log
   45    * record is flushed out when it is complete.
   46    * <p>
   47    * By default the <tt>XMLFormatter</tt> class is used for formatting.
   48    * <p>
   49    * <b>Configuration:</b>
   50    * By default each <tt>FileHandler</tt> is initialized using the following
   51    * <tt>LogManager</tt> configuration properties.  If properties are not defined
   52    * (or have invalid values) then the specified default values are used.
   53    * <ul>
   54    * <li>   java.util.logging.FileHandler.level
   55    *        specifies the default level for the <tt>Handler</tt>
   56    *        (defaults to <tt>Level.ALL</tt>).
   57    * <li>   java.util.logging.FileHandler.filter
   58    *        specifies the name of a <tt>Filter</tt> class to use
   59    *        (defaults to no <tt>Filter</tt>).
   60    * <li>   java.util.logging.FileHandler.formatter
   61    *        specifies the name of a <tt>Formatter</tt> class to use
   62    *        (defaults to <tt>java.util.logging.XMLFormatter</tt>)
   63    * <li>   java.util.logging.FileHandler.encoding
   64    *        the name of the character set encoding to use (defaults to
   65    *        the default platform encoding).
   66    * <li>   java.util.logging.FileHandler.limit
   67    *        specifies an approximate maximum amount to write (in bytes)
   68    *        to any one file.  If this is zero, then there is no limit.
   69    *        (Defaults to no limit).
   70    * <li>   java.util.logging.FileHandler.count
   71    *        specifies how many output files to cycle through (defaults to 1).
   72    * <li>   java.util.logging.FileHandler.pattern
   73    *        specifies a pattern for generating the output file name.  See
   74    *        below for details. (Defaults to "%h/java%u.log").
   75    * <li>   java.util.logging.FileHandler.append
   76    *        specifies whether the FileHandler should append onto
   77    *        any existing files (defaults to false).
   78    * </ul>
   79    * <p>
   80    * <p>
   81    * A pattern consists of a string that includes the following special
   82    * components that will be replaced at runtime:
   83    * <ul>
   84    * <li>    "/"    the local pathname separator
   85    * <li>     "%t"   the system temporary directory
   86    * <li>     "%h"   the value of the "user.home" system property
   87    * <li>     "%g"   the generation number to distinguish rotated logs
   88    * <li>     "%u"   a unique number to resolve conflicts
   89    * <li>     "%%"   translates to a single percent sign "%"
   90    * </ul>
   91    * If no "%g" field has been specified and the file count is greater
   92    * than one, then the generation number will be added to the end of
   93    * the generated filename, after a dot.
   94    * <p>
   95    * Thus for example a pattern of "%t/java%g.log" with a count of 2
   96    * would typically cause log files to be written on Solaris to
   97    * /var/tmp/java0.log and /var/tmp/java1.log whereas on Windows 95 they
   98    * would be typically written to C:\TEMP\java0.log and C:\TEMP\java1.log
   99    * <p>
  100    * Generation numbers follow the sequence 0, 1, 2, etc.
  101    * <p>
  102    * Normally the "%u" unique field is set to 0.  However, if the <tt>FileHandler</tt>
  103    * tries to open the filename and finds the file is currently in use by
  104    * another process it will increment the unique number field and try
  105    * again.  This will be repeated until <tt>FileHandler</tt> finds a file name that
  106    * is  not currently in use. If there is a conflict and no "%u" field has
  107    * been specified, it will be added at the end of the filename after a dot.
  108    * (This will be after any automatically added generation number.)
  109    * <p>
  110    * Thus if three processes were all trying to log to fred%u.%g.txt then
  111    * they  might end up using fred0.0.txt, fred1.0.txt, fred2.0.txt as
  112    * the first file in their rotating sequences.
  113    * <p>
  114    * Note that the use of unique ids to avoid conflicts is only guaranteed
  115    * to work reliably when using a local disk file system.
  116    *
  117    * @since 1.4
  118    */
  119   
  120   public class FileHandler extends StreamHandler {
  121       private MeteredStream meter;
  122       private boolean append;
  123       private int limit;       // zero => no limit.
  124       private int count;
  125       private String pattern;
  126       private String lockFileName;
  127       private FileOutputStream lockStream;
  128       private File files[];
  129       private static final int MAX_LOCKS = 100;
  130       private static java.util.HashMap<String, String> locks = new java.util.HashMap<>();
  131   
  132       // A metered stream is a subclass of OutputStream that
  133       //   (a) forwards all its output to a target stream
  134       //   (b) keeps track of how many bytes have been written
  135       private class MeteredStream extends OutputStream {
  136           OutputStream out;
  137           int written;
  138   
  139           MeteredStream(OutputStream out, int written) {
  140               this.out = out;
  141               this.written = written;
  142           }
  143   
  144           public void write(int b) throws IOException {
  145               out.write(b);
  146               written++;
  147           }
  148   
  149           public void write(byte buff[]) throws IOException {
  150               out.write(buff);
  151               written += buff.length;
  152           }
  153   
  154           public void write(byte buff[], int off, int len) throws IOException {
  155               out.write(buff,off,len);
  156               written += len;
  157           }
  158   
  159           public void flush() throws IOException {
  160               out.flush();
  161           }
  162   
  163           public void close() throws IOException {
  164               out.close();
  165           }
  166       }
  167   
  168       private void open(File fname, boolean append) throws IOException {
  169           int len = 0;
  170           if (append) {
  171               len = (int)fname.length();
  172           }
  173           FileOutputStream fout = new FileOutputStream(fname.toString(), append);
  174           BufferedOutputStream bout = new BufferedOutputStream(fout);
  175           meter = new MeteredStream(bout, len);
  176           setOutputStream(meter);
  177       }
  178   
  179       // Private method to configure a FileHandler from LogManager
  180       // properties and/or default values as specified in the class
  181       // javadoc.
  182       private void configure() {
  183           LogManager manager = LogManager.getLogManager();
  184   
  185           String cname = getClass().getName();
  186   
  187           pattern = manager.getStringProperty(cname + ".pattern", "%h/java%u.log");
  188           limit = manager.getIntProperty(cname + ".limit", 0);
  189           if (limit < 0) {
  190               limit = 0;
  191           }
  192           count = manager.getIntProperty(cname + ".count", 1);
  193           if (count <= 0) {
  194               count = 1;
  195           }
  196           append = manager.getBooleanProperty(cname + ".append", false);
  197           setLevel(manager.getLevelProperty(cname + ".level", Level.ALL));
  198           setFilter(manager.getFilterProperty(cname + ".filter", null));
  199           setFormatter(manager.getFormatterProperty(cname + ".formatter", new XMLFormatter()));
  200           try {
  201               setEncoding(manager.getStringProperty(cname +".encoding", null));
  202           } catch (Exception ex) {
  203               try {
  204                   setEncoding(null);
  205               } catch (Exception ex2) {
  206                   // doing a setEncoding with null should always work.
  207                   // assert false;
  208               }
  209           }
  210       }
  211   
  212   
  213       /**
  214        * Construct a default <tt>FileHandler</tt>.  This will be configured
  215        * entirely from <tt>LogManager</tt> properties (or their default values).
  216        * <p>
  217        * @exception  IOException if there are IO problems opening the files.
  218        * @exception  SecurityException  if a security manager exists and if
  219        *             the caller does not have <tt>LoggingPermission("control"))</tt>.
  220        * @exception  NullPointerException if pattern property is an empty String.
  221        */
  222       public FileHandler() throws IOException, SecurityException {
  223           checkAccess();
  224           configure();
  225           openFiles();
  226       }
  227   
  228       /**
  229        * Initialize a <tt>FileHandler</tt> to write to the given filename.
  230        * <p>
  231        * The <tt>FileHandler</tt> is configured based on <tt>LogManager</tt>
  232        * properties (or their default values) except that the given pattern
  233        * argument is used as the filename pattern, the file limit is
  234        * set to no limit, and the file count is set to one.
  235        * <p>
  236        * There is no limit on the amount of data that may be written,
  237        * so use this with care.
  238        *
  239        * @param pattern  the name of the output file
  240        * @exception  IOException if there are IO problems opening the files.
  241        * @exception  SecurityException  if a security manager exists and if
  242        *             the caller does not have <tt>LoggingPermission("control")</tt>.
  243        * @exception  IllegalArgumentException if pattern is an empty string
  244        */
  245       public FileHandler(String pattern) throws IOException, SecurityException {
  246           if (pattern.length() < 1 ) {
  247               throw new IllegalArgumentException();
  248           }
  249           checkAccess();
  250           configure();
  251           this.pattern = pattern;
  252           this.limit = 0;
  253           this.count = 1;
  254           openFiles();
  255       }
  256   
  257       /**
  258        * Initialize a <tt>FileHandler</tt> to write to the given filename,
  259        * with optional append.
  260        * <p>
  261        * The <tt>FileHandler</tt> is configured based on <tt>LogManager</tt>
  262        * properties (or their default values) except that the given pattern
  263        * argument is used as the filename pattern, the file limit is
  264        * set to no limit, the file count is set to one, and the append
  265        * mode is set to the given <tt>append</tt> argument.
  266        * <p>
  267        * There is no limit on the amount of data that may be written,
  268        * so use this with care.
  269        *
  270        * @param pattern  the name of the output file
  271        * @param append  specifies append mode
  272        * @exception  IOException if there are IO problems opening the files.
  273        * @exception  SecurityException  if a security manager exists and if
  274        *             the caller does not have <tt>LoggingPermission("control")</tt>.
  275        * @exception  IllegalArgumentException if pattern is an empty string
  276        */
  277       public FileHandler(String pattern, boolean append) throws IOException, SecurityException {
  278           if (pattern.length() < 1 ) {
  279               throw new IllegalArgumentException();
  280           }
  281           checkAccess();
  282           configure();
  283           this.pattern = pattern;
  284           this.limit = 0;
  285           this.count = 1;
  286           this.append = append;
  287           openFiles();
  288       }
  289   
  290       /**
  291        * Initialize a <tt>FileHandler</tt> to write to a set of files.  When
  292        * (approximately) the given limit has been written to one file,
  293        * another file will be opened.  The output will cycle through a set
  294        * of count files.
  295        * <p>
  296        * The <tt>FileHandler</tt> is configured based on <tt>LogManager</tt>
  297        * properties (or their default values) except that the given pattern
  298        * argument is used as the filename pattern, the file limit is
  299        * set to the limit argument, and the file count is set to the
  300        * given count argument.
  301        * <p>
  302        * The count must be at least 1.
  303        *
  304        * @param pattern  the pattern for naming the output file
  305        * @param limit  the maximum number of bytes to write to any one file
  306        * @param count  the number of files to use
  307        * @exception  IOException if there are IO problems opening the files.
  308        * @exception  SecurityException  if a security manager exists and if
  309        *             the caller does not have <tt>LoggingPermission("control")</tt>.
  310        * @exception IllegalArgumentException if limit < 0, or count < 1.
  311        * @exception  IllegalArgumentException if pattern is an empty string
  312        */
  313       public FileHandler(String pattern, int limit, int count)
  314                                           throws IOException, SecurityException {
  315           if (limit < 0 || count < 1 || pattern.length() < 1) {
  316               throw new IllegalArgumentException();
  317           }
  318           checkAccess();
  319           configure();
  320           this.pattern = pattern;
  321           this.limit = limit;
  322           this.count = count;
  323           openFiles();
  324       }
  325   
  326       /**
  327        * Initialize a <tt>FileHandler</tt> to write to a set of files
  328        * with optional append.  When (approximately) the given limit has
  329        * been written to one file, another file will be opened.  The
  330        * output will cycle through a set of count files.
  331        * <p>
  332        * The <tt>FileHandler</tt> is configured based on <tt>LogManager</tt>
  333        * properties (or their default values) except that the given pattern
  334        * argument is used as the filename pattern, the file limit is
  335        * set to the limit argument, and the file count is set to the
  336        * given count argument, and the append mode is set to the given
  337        * <tt>append</tt> argument.
  338        * <p>
  339        * The count must be at least 1.
  340        *
  341        * @param pattern  the pattern for naming the output file
  342        * @param limit  the maximum number of bytes to write to any one file
  343        * @param count  the number of files to use
  344        * @param append  specifies append mode
  345        * @exception  IOException if there are IO problems opening the files.
  346        * @exception  SecurityException  if a security manager exists and if
  347        *             the caller does not have <tt>LoggingPermission("control")</tt>.
  348        * @exception IllegalArgumentException if limit < 0, or count < 1.
  349        * @exception  IllegalArgumentException if pattern is an empty string
  350        *
  351        */
  352       public FileHandler(String pattern, int limit, int count, boolean append)
  353                                           throws IOException, SecurityException {
  354           if (limit < 0 || count < 1 || pattern.length() < 1) {
  355               throw new IllegalArgumentException();
  356           }
  357           checkAccess();
  358           configure();
  359           this.pattern = pattern;
  360           this.limit = limit;
  361           this.count = count;
  362           this.append = append;
  363           openFiles();
  364       }
  365   
  366       // Private method to open the set of output files, based on the
  367       // configured instance variables.
  368       private void openFiles() throws IOException {
  369           LogManager manager = LogManager.getLogManager();
  370           manager.checkAccess();
  371           if (count < 1) {
  372              throw new IllegalArgumentException("file count = " + count);
  373           }
  374           if (limit < 0) {
  375               limit = 0;
  376           }
  377   
  378           // We register our own ErrorManager during initialization
  379           // so we can record exceptions.
  380           InitializationErrorManager em = new InitializationErrorManager();
  381           setErrorManager(em);
  382   
  383           // Create a lock file.  This grants us exclusive access
  384           // to our set of output files, as long as we are alive.
  385           int unique = -1;
  386           for (;;) {
  387               unique++;
  388               if (unique > MAX_LOCKS) {
  389                   throw new IOException("Couldn't get lock for " + pattern);
  390               }
  391               // Generate a lock file name from the "unique" int.
  392               lockFileName = generate(pattern, 0, unique).toString() + ".lck";
  393               // Now try to lock that filename.
  394               // Because some systems (e.g., Solaris) can only do file locks
  395               // between processes (and not within a process), we first check
  396               // if we ourself already have the file locked.
  397               synchronized(locks) {
  398                   if (locks.get(lockFileName) != null) {
  399                       // We already own this lock, for a different FileHandler
  400                       // object.  Try again.
  401                       continue;
  402                   }
  403                   FileChannel fc;
  404                   try {
  405                       lockStream = new FileOutputStream(lockFileName);
  406                       fc = lockStream.getChannel();
  407                   } catch (IOException ix) {
  408                       // We got an IOException while trying to open the file.
  409                       // Try the next file.
  410                       continue;
  411                   }
  412                   boolean available;
  413                   try {
  414                       available = fc.tryLock() != null;
  415                       // We got the lock OK.
  416                   } catch (IOException ix) {
  417                       // We got an IOException while trying to get the lock.
  418                       // This normally indicates that locking is not supported
  419                       // on the target directory.  We have to proceed without
  420                       // getting a lock.   Drop through.
  421                       available = true;
  422                   }
  423                   if (available) {
  424                       // We got the lock.  Remember it.
  425                       locks.put(lockFileName, lockFileName);
  426                       break;
  427                   }
  428   
  429                   // We failed to get the lock.  Try next file.
  430                   fc.close();
  431               }
  432           }
  433   
  434           files = new File[count];
  435           for (int i = 0; i < count; i++) {
  436               files[i] = generate(pattern, i, unique);
  437           }
  438   
  439           // Create the initial log file.
  440           if (append) {
  441               open(files[0], true);
  442           } else {
  443               rotate();
  444           }
  445   
  446           // Did we detect any exceptions during initialization?
  447           Exception ex = em.lastException;
  448           if (ex != null) {
  449               if (ex instanceof IOException) {
  450                   throw (IOException) ex;
  451               } else if (ex instanceof SecurityException) {
  452                   throw (SecurityException) ex;
  453               } else {
  454                   throw new IOException("Exception: " + ex);
  455               }
  456           }
  457   
  458           // Install the normal default ErrorManager.
  459           setErrorManager(new ErrorManager());
  460       }
  461   
  462       // Generate a filename from a pattern.
  463       private File generate(String pattern, int generation, int unique) throws IOException {
  464           File file = null;
  465           String word = "";
  466           int ix = 0;
  467           boolean sawg = false;
  468           boolean sawu = false;
  469           while (ix < pattern.length()) {
  470               char ch = pattern.charAt(ix);
  471               ix++;
  472               char ch2 = 0;
  473               if (ix < pattern.length()) {
  474                   ch2 = Character.toLowerCase(pattern.charAt(ix));
  475               }
  476               if (ch == '/') {
  477                   if (file == null) {
  478                       file = new File(word);
  479                   } else {
  480                       file = new File(file, word);
  481                   }
  482                   word = "";
  483                   continue;
  484               } else  if (ch == '%') {
  485                   if (ch2 == 't') {
  486                       String tmpDir = System.getProperty("java.io.tmpdir");
  487                       if (tmpDir == null) {
  488                           tmpDir = System.getProperty("user.home");
  489                       }
  490                       file = new File(tmpDir);
  491                       ix++;
  492                       word = "";
  493                       continue;
  494                   } else if (ch2 == 'h') {
  495                       file = new File(System.getProperty("user.home"));
  496                       if (isSetUID()) {
  497                           // Ok, we are in a set UID program.  For safety's sake
  498                           // we disallow attempts to open files relative to %h.
  499                           throw new IOException("can't use %h in set UID program");
  500                       }
  501                       ix++;
  502                       word = "";
  503                       continue;
  504                   } else if (ch2 == 'g') {
  505                       word = word + generation;
  506                       sawg = true;
  507                       ix++;
  508                       continue;
  509                   } else if (ch2 == 'u') {
  510                       word = word + unique;
  511                       sawu = true;
  512                       ix++;
  513                       continue;
  514                   } else if (ch2 == '%') {
  515                       word = word + "%";
  516                       ix++;
  517                       continue;
  518                   }
  519               }
  520               word = word + ch;
  521           }
  522           if (count > 1 && !sawg) {
  523               word = word + "." + generation;
  524           }
  525           if (unique > 0 && !sawu) {
  526               word = word + "." + unique;
  527           }
  528           if (word.length() > 0) {
  529               if (file == null) {
  530                   file = new File(word);
  531               } else {
  532                   file = new File(file, word);
  533               }
  534           }
  535           return file;
  536       }
  537   
  538       // Rotate the set of output files
  539       private synchronized void rotate() {
  540           Level oldLevel = getLevel();
  541           setLevel(Level.OFF);
  542   
  543           super.close();
  544           for (int i = count-2; i >= 0; i--) {
  545               File f1 = files[i];
  546               File f2 = files[i+1];
  547               if (f1.exists()) {
  548                   if (f2.exists()) {
  549                       f2.delete();
  550                   }
  551                   f1.renameTo(f2);
  552               }
  553           }
  554           try {
  555               open(files[0], false);
  556           } catch (IOException ix) {
  557               // We don't want to throw an exception here, but we
  558               // report the exception to any registered ErrorManager.
  559               reportError(null, ix, ErrorManager.OPEN_FAILURE);
  560   
  561           }
  562           setLevel(oldLevel);
  563       }
  564   
  565       /**
  566        * Format and publish a <tt>LogRecord</tt>.
  567        *
  568        * @param  record  description of the log event. A null record is
  569        *                 silently ignored and is not published
  570        */
  571       public synchronized void publish(LogRecord record) {
  572           if (!isLoggable(record)) {
  573               return;
  574           }
  575           super.publish(record);
  576           flush();
  577           if (limit > 0 && meter.written >= limit) {
  578               // We performed access checks in the "init" method to make sure
  579               // we are only initialized from trusted code.  So we assume
  580               // it is OK to write the target files, even if we are
  581               // currently being called from untrusted code.
  582               // So it is safe to raise privilege here.
  583               AccessController.doPrivileged(new PrivilegedAction<Object>() {
  584                   public Object run() {
  585                       rotate();
  586                       return null;
  587                   }
  588               });
  589           }
  590       }
  591   
  592       /**
  593        * Close all the files.
  594        *
  595        * @exception  SecurityException  if a security manager exists and if
  596        *             the caller does not have <tt>LoggingPermission("control")</tt>.
  597        */
  598       public synchronized void close() throws SecurityException {
  599           super.close();
  600           // Unlock any lock file.
  601           if (lockFileName == null) {
  602               return;
  603           }
  604           try {
  605               // Closing the lock file's FileOutputStream will close
  606               // the underlying channel and free any locks.
  607               lockStream.close();
  608           } catch (Exception ex) {
  609               // Problems closing the stream.  Punt.
  610           }
  611           synchronized(locks) {
  612               locks.remove(lockFileName);
  613           }
  614           new File(lockFileName).delete();
  615           lockFileName = null;
  616           lockStream = null;
  617       }
  618   
  619       private static class InitializationErrorManager extends ErrorManager {
  620           Exception lastException;
  621           public void error(String msg, Exception ex, int code) {
  622               lastException = ex;
  623           }
  624       }
  625   
  626       // Private native method to check if we are in a set UID program.
  627       private static native boolean isSetUID();
  628   }

Home » openjdk-7 » java » util » logging » [javadoc | source]