aboutsummaryrefslogtreecommitdiff
path: root/libjava/java/util/logging/FileHandler.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/java/util/logging/FileHandler.java')
-rw-r--r--libjava/java/util/logging/FileHandler.java509
1 files changed, 509 insertions, 0 deletions
diff --git a/libjava/java/util/logging/FileHandler.java b/libjava/java/util/logging/FileHandler.java
new file mode 100644
index 0000000..b9f6110
--- /dev/null
+++ b/libjava/java/util/logging/FileHandler.java
@@ -0,0 +1,509 @@
+/* FileHandler.java
+ -- a class for publishing log messages to log files
+
+Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+
+*/
+
+package java.util.logging;
+
+import java.io.IOException;
+import java.io.File;
+import java.io.FileOutputStream;
+
+/**
+ * A <code>FileHandler</code> publishes log records to a set of log
+ * files. A maximum file size can be specified; as soon as a log file
+ * reaches the size limit, it is closed and the next file in the set
+ * is taken.
+ *
+ * <p><strong>Configuration:</strong> Values of the subsequent
+ * <code>LogManager</code> properties are taken into consideration
+ * when a <code>FileHandler</code> is initialized. If a property is
+ * not defined, or if it has an invalid value, a default is taken
+ * without an exception being thrown.
+ *
+ * <ul>
+ *
+ * <li><code>java.util.FileHandler.level</code> - specifies
+ * the initial severity level threshold. Default value:
+ * <code>Level.ALL</code>.</li>
+ *
+ * <li><code>java.util.FileHandler.filter</code> - specifies
+ * the name of a Filter class. Default value: No Filter.</li>
+ *
+ * <li><code>java.util.FileHandler.formatter</code> - specifies
+ * the name of a Formatter class. Default value:
+ * <code>java.util.logging.XMLFormatter</code>.</li>
+ *
+ * <li><code>java.util.FileHandler.encoding</code> - specifies
+ * the name of the character encoding. Default value:
+ * the default platform encoding.</li>
+ *
+ * <li><code>java.util.FileHandler.limit</code> - specifies the number
+ * of bytes a log file is approximately allowed to reach before it
+ * is closed and the handler switches to the next file in the
+ * rotating set. A value of zero means that files can grow
+ * without limit. Default value: 0 (unlimited growth).</li>
+ *
+ * <li><code>java.util.FileHandler.count</code> - specifies the number
+ * of log files through which this handler cycles. Default value:
+ * 1.</li>
+ *
+ * <li><code>java.util.FileHandler.pattern</code> - specifies a
+ * pattern for the location and name of the produced log files.
+ * See the section on <a href="#filePatterns">file name
+ * patterns</a> for details. Default value:
+ * <code>"%h/java%u.log"</code>.</li>
+ *
+ * <li><code>java.util.FileHandler.append</code> - specifies
+ * whether the handler will append log records to existing
+ * files, or whether the handler will clear log files
+ * upon switching to them. Default value: <code>false</code>,
+ * indicating that files will be cleared.</li>
+ *
+ * </ul>
+ *
+ * <p><a name="filePatterns"><strong>File Name Patterns:</strong></a>
+ * The name and location and log files are specified with pattern
+ * strings. The handler will replace the following character sequences
+ * when opening log files:
+ *
+ * <p><ul>
+ * <li><code>/</code> - replaced by the platform-specific path name
+ * separator. This value is taken from the system property
+ * <code>file.separator</code>.</li>
+ *
+ * <li><code>%t</code> - replaced by the platform-specific location of
+ * the directory intended for temporary files. This value is
+ * taken from the system property <code>java.io.tmpdir</code>.</li>
+ *
+ * <li><code>%h</code> - replaced by the location of the home
+ * directory of the current user. This value is taken from the
+ * system property <code>file.separator</code>.</li>
+ *
+ * <li><code>%g</code> - replaced by a generation number for
+ * distinguisthing the individual items in the rotating set
+ * of log files. The generation number cycles through the
+ * sequence 0, 1, ..., <code>count</code> - 1.</li>
+ *
+ * <li><code>%u</code> - replaced by a unique number for
+ * distinguisthing the output files of several concurrently
+ * running processes. The <code>FileHandler</code> starts
+ * with 0 when it tries to open a log file. If the file
+ * cannot be opened because it is currently in use,
+ * the unique number is incremented by one and opening
+ * is tried again. These steps are repeated until the
+ * opening operation succeeds.
+ *
+ * <p>FIXME: Is the following correct? Please review. The unique
+ * number is determined for each log file individually when it is
+ * opened upon switching to the next file. Therefore, it is not
+ * correct to assume that all log files in a rotating set bear the
+ * same unique number.
+ *
+ * <p>FIXME: The Javadoc for the Sun reference implementation
+ * says: "Note that the use of unique ids to avoid conflicts is
+ * only guaranteed to work reliably when using a local disk file
+ * system." Why? This needs to be mentioned as well, in case
+ * the reviewers decide the statement is true. Otherwise,
+ * file a bug report with Sun.</li>
+ *
+ * <li><code>%%</code> - replaced by a single percent sign.</li>
+ * </ul>
+ *
+ * <p>If the pattern string does not contain <code>%g</code> and
+ * <code>count</code> is greater than one, the handler will append
+ * the string <code>.%g</code> to the specified pattern.
+ *
+ * <p>If the handler attempts to open a log file, this log file
+ * is being used at the time of the attempt, and the pattern string
+ * does not contain <code>%u</code>, the handler will append
+ * the string <code>.%u</code> to the specified pattern. This
+ * step is performed after any generation number has been
+ * appended.
+ *
+ * <p><em>Examples for the GNU platform:</em>
+ *
+ * <p><ul>
+ *
+ * <li><code>%h/java%u.log</code> will lead to a single log file
+ * <code>/home/janet/java0.log</code>, assuming <code>count</code>
+ * equals 1, the user's home directory is
+ * <code>/home/janet</code>, and the attempt to open the file
+ * succeeds.</li>
+ *
+ * <li><code>%h/java%u.log</code> will lead to three log files
+ * <code>/home/janet/java0.log.0</code>,
+ * <code>/home/janet/java0.log.1</code>, and
+ * <code>/home/janet/java0.log.2</code>,
+ * assuming <code>count</code> equals 3, the user's home
+ * directory is <code>/home/janet</code>, and all attempts
+ * to open files succeed.</li>
+ *
+ * <li><code>%h/java%u.log</code> will lead to three log files
+ * <code>/home/janet/java0.log.0</code>,
+ * <code>/home/janet/java1.log.1</code>, and
+ * <code>/home/janet/java0.log.2</code>,
+ * assuming <code>count</code> equals 3, the user's home
+ * directory is <code>/home/janet</code>, and the attempt
+ * to open <code>/home/janet/java0.log.1</code> fails.</li>
+ *
+ * </ul>
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class FileHandler
+ extends StreamHandler
+{
+ /**
+ * The number of bytes a log file is approximately allowed to reach
+ * before it is closed and the handler switches to the next file in
+ * the rotating set. A value of zero means that files can grow
+ * without limit.
+ */
+ private final int limit;
+
+
+ /**
+ * The number of log files through which this handler cycles.
+ */
+ private final int count;
+
+
+ /**
+ * The pattern for the location and name of the produced log files.
+ * See the section on <a href="#filePatterns">file name patterns</a>
+ * for details.
+ */
+ private final String pattern;
+
+
+ /**
+ * Indicates whether the handler will append log records to existing
+ * files (<code>true</code>), or whether the handler will clear log files
+ * upon switching to them (<code>false</code>).
+ */
+ private final boolean append;
+
+
+ /**
+ * Constructs a <code>FileHandler</code>, taking all property values
+ * from the current {@link LogManager LogManager} configuration.
+ *
+ * @throws java.io.IOException FIXME: The Sun Javadoc says: "if
+ * there are IO problems opening the files." This conflicts
+ * with the general principle that configuration errors do
+ * not prohibit construction. Needs review.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ */
+ public FileHandler()
+ throws IOException, SecurityException
+ {
+ this(/* pattern: use configiguration */ null,
+
+ LogManager.getIntProperty("java.util.logging.FileHandler.limit",
+ /* default */ 0),
+
+ LogManager.getIntProperty("java.util.logging.FileHandler.count",
+ /* default */ 1),
+
+ LogManager.getBooleanProperty("java.util.logging.FileHandler.append",
+ /* default */ false));
+ }
+
+
+ /* FIXME: Javadoc missing. */
+ public FileHandler(String pattern)
+ throws IOException, SecurityException
+ {
+ this(pattern,
+ /* limit */ 0,
+ /* count */ 1,
+ /* append */ false);
+ }
+
+
+ /* FIXME: Javadoc missing. */
+ public FileHandler(String pattern, boolean append)
+ throws IOException, SecurityException
+ {
+ this(pattern,
+ /* limit */ 0,
+ /* count */ 1,
+ append);
+ }
+
+
+ /* FIXME: Javadoc missing. */
+ public FileHandler(String pattern, int limit, int count)
+ throws IOException, SecurityException
+ {
+ this(pattern, limit, count,
+ LogManager.getBooleanProperty(
+ "java.util.logging.FileHandler.append",
+ /* default */ false));
+ }
+
+
+ /**
+ * Constructs a <code>FileHandler</code> given the pattern for the
+ * location and name of the produced log files, the size limit, the
+ * number of log files thorough which the handler will rotate, and
+ * the <code>append</code> property. All other property values are
+ * taken from the current {@link LogManager LogManager}
+ * configuration.
+ *
+ * @param pattern The pattern for the location and name of the
+ * produced log files. See the section on <a
+ * href="#filePatterns">file name patterns</a> for details.
+ * If <code>pattern</code> is <code>null</code>, the value is
+ * taken from the {@link LogManager LogManager} configuration
+ * property
+ * <code>java.util.logging.FileHandler.pattern</code>.
+ * However, this is a pecularity of the GNU implementation,
+ * and Sun's API specification does not mention what behavior
+ * is to be expected for <code>null</code>. Therefore,
+ * applications should not rely on this feature.
+ *
+ * @param limit specifies the number of bytes a log file is
+ * approximately allowed to reach before it is closed and the
+ * handler switches to the next file in the rotating set. A
+ * value of zero means that files can grow without limit.
+ *
+ * @param count specifies the number of log files through which this
+ * handler cycles.
+ *
+ * @param append specifies whether the handler will append log
+ * records to existing files (<code>true</code>), or whether the
+ * handler will clear log files upon switching to them
+ * (<code>false</code>).
+ *
+ * @throws java.io.IOException FIXME: The Sun Javadoc says: "if
+ * there are IO problems opening the files." This conflicts
+ * with the general principle that configuration errors do
+ * not prohibit construction. Needs review.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ * <p>FIXME: This seems in contrast to all other handler
+ * constructors -- verify this by running tests against
+ * the Sun reference implementation.
+ */
+ public FileHandler(String pattern,
+ int limit,
+ int count,
+ boolean append)
+ throws IOException, SecurityException
+ {
+ super(createFileStream(pattern, limit, count, append,
+ /* generation */ 0),
+ "java.util.logging.FileHandler",
+ /* default level */ Level.ALL,
+ /* formatter */ null,
+ /* default formatter */ XMLFormatter.class);
+
+ if ((limit <0) || (count < 1))
+ throw new IllegalArgumentException();
+
+ this.pattern = pattern;
+ this.limit = limit;
+ this.count = count;
+ this.append = append;
+ }
+
+
+ /* FIXME: Javadoc missing. */
+ private static java.io.OutputStream createFileStream(String pattern,
+ int limit,
+ int count,
+ boolean append,
+ int generation)
+ {
+ String path;
+ int unique = 0;
+
+ /* Throws a SecurityException if the caller does not have
+ * LoggingPermission("control").
+ */
+ LogManager.getLogManager().checkAccess();
+
+ /* Default value from the java.util.logging.FileHandler.pattern
+ * LogManager configuration property.
+ */
+ if (pattern == null)
+ pattern = LogManager.getLogManager().getProperty(
+ "java.util.logging.FileHandler.pattern");
+ if (pattern == null)
+ pattern = "%h/java%u.log";
+
+ do
+ {
+ path = replaceFileNameEscapes(pattern, generation, unique, count);
+
+ try
+ {
+ File file = new File(path);
+ if (file.createNewFile())
+ return new FileOutputStream(path, append);
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+
+ unique = unique + 1;
+ if (pattern.indexOf("%u") < 0)
+ pattern = pattern + ".%u";
+ }
+ while (true);
+ }
+
+
+ /**
+ * Replaces the substrings <code>"/"</code> by the value of the
+ * system property <code>"file.separator"</code>, <code>"%t"</code>
+ * by the value of the system property
+ * <code>"java.io.tmpdir"</code>, <code>"%h"</code> by the value of
+ * the system property <code>"user.home"</code>, <code>"%g"</code>
+ * by the value of <code>generation</code>, <code>"%u"</code> by the
+ * value of <code>uniqueNumber</code>, and <code>"%%"</code> by a
+ * single percent character. If <code>pattern<code> does
+ * <em>not</em> contain the sequence <code>"%g"</code>,
+ * the value of <code>generation</code> will be appended to
+ * the result.
+ *
+ * @throws NullPointerException if one of the system properties
+ * <code>"file.separator"</code>,
+ * <code>"java.io.tmpdir"</code>, or
+ * <code>"user.home"</code> has no value and the
+ * corresponding escape sequence appears in
+ * <code>pattern</code>.
+ */
+ private static String replaceFileNameEscapes(String pattern,
+ int generation,
+ int uniqueNumber,
+ int count)
+ {
+ StringBuffer buf = new StringBuffer(pattern);
+ String replaceWith;
+ boolean foundGeneration = false;
+
+ int pos = 0;
+ do
+ {
+ // Uncomment the next line for finding bugs.
+ // System.out.println(buf.substring(0,pos) + '|' + buf.substring(pos));
+
+ if (buf.charAt(pos) == '/')
+ {
+ /* The same value is also provided by java.io.File.separator. */
+ replaceWith = System.getProperty("file.separator");
+ buf.replace(pos, pos + 1, replaceWith);
+ pos = pos + replaceWith.length() - 1;
+ continue;
+ }
+
+ if (buf.charAt(pos) == '%')
+ {
+ switch (buf.charAt(pos + 1))
+ {
+ case 't':
+ replaceWith = System.getProperty("java.io.tmpdir");
+ break;
+
+ case 'h':
+ replaceWith = System.getProperty("user.home");
+ break;
+
+ case 'g':
+ replaceWith = Integer.toString(generation);
+ foundGeneration = true;
+ break;
+
+ case 'u':
+ replaceWith = Integer.toString(uniqueNumber);
+ break;
+
+ case '%':
+ replaceWith = "%";
+ break;
+
+ default:
+ replaceWith = "??";
+ break; // FIXME: Throw exception?
+ }
+
+ buf.replace(pos, pos + 2, replaceWith);
+ pos = pos + replaceWith.length() - 1;
+ continue;
+ }
+ }
+ while (++pos < buf.length() - 1);
+
+ if (!foundGeneration && (count > 1))
+ {
+ buf.append('.');
+ buf.append(generation);
+ }
+
+ return buf.toString();
+ }
+
+
+ /* FIXME: Javadoc missing, implementation incomplete. */
+ public void publish(LogRecord record)
+ {
+ super.publish(record);
+
+ /* FIXME: Decide when to switch over. How do we get to
+ * the number of bytes published so far? Two possibilities:
+ * 1. File.length, 2. have metering wrapper around
+ * output stream counting the number of written bytes.
+ */
+
+ /* FIXME: Switch over if needed! This implementation always
+ * writes into a single file, i.e. behaves as if limit
+ * always was zero. So, the implementation is somewhat
+ * functional but incomplete.
+ */
+ }
+}