Merge from mainline (gomp-merge-2005-02-26).
[official-gcc.git] / libjava / java / util / logging / FileHandler.java
blobae8eb865ae39eabc5bcee13b1120f6639d80584a
1 /* FileHandler.java -- a class for publishing log messages to log files
2 Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package java.util.logging;
41 import java.io.File;
42 import java.io.FileOutputStream;
43 import java.io.IOException;
45 /**
46 * A <code>FileHandler</code> publishes log records to a set of log
47 * files. A maximum file size can be specified; as soon as a log file
48 * reaches the size limit, it is closed and the next file in the set
49 * is taken.
51 * <p><strong>Configuration:</strong> Values of the subsequent
52 * <code>LogManager</code> properties are taken into consideration
53 * when a <code>FileHandler</code> is initialized. If a property is
54 * not defined, or if it has an invalid value, a default is taken
55 * without an exception being thrown.
57 * <ul>
59 * <li><code>java.util.FileHandler.level</code> - specifies
60 * the initial severity level threshold. Default value:
61 * <code>Level.ALL</code>.</li>
63 * <li><code>java.util.FileHandler.filter</code> - specifies
64 * the name of a Filter class. Default value: No Filter.</li>
66 * <li><code>java.util.FileHandler.formatter</code> - specifies
67 * the name of a Formatter class. Default value:
68 * <code>java.util.logging.XMLFormatter</code>.</li>
70 * <li><code>java.util.FileHandler.encoding</code> - specifies
71 * the name of the character encoding. Default value:
72 * the default platform encoding.</li>
74 * <li><code>java.util.FileHandler.limit</code> - specifies the number
75 * of bytes a log file is approximately allowed to reach before it
76 * is closed and the handler switches to the next file in the
77 * rotating set. A value of zero means that files can grow
78 * without limit. Default value: 0 (unlimited growth).</li>
80 * <li><code>java.util.FileHandler.count</code> - specifies the number
81 * of log files through which this handler cycles. Default value:
82 * 1.</li>
84 * <li><code>java.util.FileHandler.pattern</code> - specifies a
85 * pattern for the location and name of the produced log files.
86 * See the section on <a href="#filePatterns">file name
87 * patterns</a> for details. Default value:
88 * <code>"%h/java%u.log"</code>.</li>
90 * <li><code>java.util.FileHandler.append</code> - specifies
91 * whether the handler will append log records to existing
92 * files, or whether the handler will clear log files
93 * upon switching to them. Default value: <code>false</code>,
94 * indicating that files will be cleared.</li>
96 * </ul>
98 * <p><a name="filePatterns"><strong>File Name Patterns:</strong></a>
99 * The name and location and log files are specified with pattern
100 * strings. The handler will replace the following character sequences
101 * when opening log files:
103 * <p><ul>
104 * <li><code>/</code> - replaced by the platform-specific path name
105 * separator. This value is taken from the system property
106 * <code>file.separator</code>.</li>
108 * <li><code>%t</code> - replaced by the platform-specific location of
109 * the directory intended for temporary files. This value is
110 * taken from the system property <code>java.io.tmpdir</code>.</li>
112 * <li><code>%h</code> - replaced by the location of the home
113 * directory of the current user. This value is taken from the
114 * system property <code>file.separator</code>.</li>
116 * <li><code>%g</code> - replaced by a generation number for
117 * distinguisthing the individual items in the rotating set
118 * of log files. The generation number cycles through the
119 * sequence 0, 1, ..., <code>count</code> - 1.</li>
121 * <li><code>%u</code> - replaced by a unique number for
122 * distinguisthing the output files of several concurrently
123 * running processes. The <code>FileHandler</code> starts
124 * with 0 when it tries to open a log file. If the file
125 * cannot be opened because it is currently in use,
126 * the unique number is incremented by one and opening
127 * is tried again. These steps are repeated until the
128 * opening operation succeeds.
130 * <p>FIXME: Is the following correct? Please review. The unique
131 * number is determined for each log file individually when it is
132 * opened upon switching to the next file. Therefore, it is not
133 * correct to assume that all log files in a rotating set bear the
134 * same unique number.
136 * <p>FIXME: The Javadoc for the Sun reference implementation
137 * says: "Note that the use of unique ids to avoid conflicts is
138 * only guaranteed to work reliably when using a local disk file
139 * system." Why? This needs to be mentioned as well, in case
140 * the reviewers decide the statement is true. Otherwise,
141 * file a bug report with Sun.</li>
143 * <li><code>%%</code> - replaced by a single percent sign.</li>
144 * </ul>
146 * <p>If the pattern string does not contain <code>%g</code> and
147 * <code>count</code> is greater than one, the handler will append
148 * the string <code>.%g</code> to the specified pattern.
150 * <p>If the handler attempts to open a log file, this log file
151 * is being used at the time of the attempt, and the pattern string
152 * does not contain <code>%u</code>, the handler will append
153 * the string <code>.%u</code> to the specified pattern. This
154 * step is performed after any generation number has been
155 * appended.
157 * <p><em>Examples for the GNU platform:</em>
159 * <p><ul>
161 * <li><code>%h/java%u.log</code> will lead to a single log file
162 * <code>/home/janet/java0.log</code>, assuming <code>count</code>
163 * equals 1, the user's home directory is
164 * <code>/home/janet</code>, and the attempt to open the file
165 * succeeds.</li>
167 * <li><code>%h/java%u.log</code> will lead to three log files
168 * <code>/home/janet/java0.log.0</code>,
169 * <code>/home/janet/java0.log.1</code>, and
170 * <code>/home/janet/java0.log.2</code>,
171 * assuming <code>count</code> equals 3, the user's home
172 * directory is <code>/home/janet</code>, and all attempts
173 * to open files succeed.</li>
175 * <li><code>%h/java%u.log</code> will lead to three log files
176 * <code>/home/janet/java0.log.0</code>,
177 * <code>/home/janet/java1.log.1</code>, and
178 * <code>/home/janet/java0.log.2</code>,
179 * assuming <code>count</code> equals 3, the user's home
180 * directory is <code>/home/janet</code>, and the attempt
181 * to open <code>/home/janet/java0.log.1</code> fails.</li>
183 * </ul>
185 * @author Sascha Brawer (brawer@acm.org)
187 public class FileHandler
188 extends StreamHandler
191 * The number of bytes a log file is approximately allowed to reach
192 * before it is closed and the handler switches to the next file in
193 * the rotating set. A value of zero means that files can grow
194 * without limit.
196 private final int limit;
200 * The number of log files through which this handler cycles.
202 private final int count;
206 * The pattern for the location and name of the produced log files.
207 * See the section on <a href="#filePatterns">file name patterns</a>
208 * for details.
210 private final String pattern;
214 * Indicates whether the handler will append log records to existing
215 * files (<code>true</code>), or whether the handler will clear log files
216 * upon switching to them (<code>false</code>).
218 private final boolean append;
222 * Constructs a <code>FileHandler</code>, taking all property values
223 * from the current {@link LogManager LogManager} configuration.
225 * @throws java.io.IOException FIXME: The Sun Javadoc says: "if
226 * there are IO problems opening the files." This conflicts
227 * with the general principle that configuration errors do
228 * not prohibit construction. Needs review.
230 * @throws SecurityException if a security manager exists and
231 * the caller is not granted the permission to control
232 * the logging infrastructure.
234 public FileHandler()
235 throws IOException, SecurityException
237 this(/* pattern: use configiguration */ null,
239 LogManager.getIntProperty("java.util.logging.FileHandler.limit",
240 /* default */ 0),
242 LogManager.getIntProperty("java.util.logging.FileHandler.count",
243 /* default */ 1),
245 LogManager.getBooleanProperty("java.util.logging.FileHandler.append",
246 /* default */ false));
250 /* FIXME: Javadoc missing. */
251 public FileHandler(String pattern)
252 throws IOException, SecurityException
254 this(pattern,
255 /* limit */ 0,
256 /* count */ 1,
257 /* append */ false);
261 /* FIXME: Javadoc missing. */
262 public FileHandler(String pattern, boolean append)
263 throws IOException, SecurityException
265 this(pattern,
266 /* limit */ 0,
267 /* count */ 1,
268 append);
272 /* FIXME: Javadoc missing. */
273 public FileHandler(String pattern, int limit, int count)
274 throws IOException, SecurityException
276 this(pattern, limit, count,
277 LogManager.getBooleanProperty(
278 "java.util.logging.FileHandler.append",
279 /* default */ false));
284 * Constructs a <code>FileHandler</code> given the pattern for the
285 * location and name of the produced log files, the size limit, the
286 * number of log files thorough which the handler will rotate, and
287 * the <code>append</code> property. All other property values are
288 * taken from the current {@link LogManager LogManager}
289 * configuration.
291 * @param pattern The pattern for the location and name of the
292 * produced log files. See the section on <a
293 * href="#filePatterns">file name patterns</a> for details.
294 * If <code>pattern</code> is <code>null</code>, the value is
295 * taken from the {@link LogManager LogManager} configuration
296 * property
297 * <code>java.util.logging.FileHandler.pattern</code>.
298 * However, this is a pecularity of the GNU implementation,
299 * and Sun's API specification does not mention what behavior
300 * is to be expected for <code>null</code>. Therefore,
301 * applications should not rely on this feature.
303 * @param limit specifies the number of bytes a log file is
304 * approximately allowed to reach before it is closed and the
305 * handler switches to the next file in the rotating set. A
306 * value of zero means that files can grow without limit.
308 * @param count specifies the number of log files through which this
309 * handler cycles.
311 * @param append specifies whether the handler will append log
312 * records to existing files (<code>true</code>), or whether the
313 * handler will clear log files upon switching to them
314 * (<code>false</code>).
316 * @throws java.io.IOException FIXME: The Sun Javadoc says: "if
317 * there are IO problems opening the files." This conflicts
318 * with the general principle that configuration errors do
319 * not prohibit construction. Needs review.
321 * @throws SecurityException if a security manager exists and
322 * the caller is not granted the permission to control
323 * the logging infrastructure.
324 * <p>FIXME: This seems in contrast to all other handler
325 * constructors -- verify this by running tests against
326 * the Sun reference implementation.
328 public FileHandler(String pattern,
329 int limit,
330 int count,
331 boolean append)
332 throws IOException, SecurityException
334 super(createFileStream(pattern, limit, count, append,
335 /* generation */ 0),
336 "java.util.logging.FileHandler",
337 /* default level */ Level.ALL,
338 /* formatter */ null,
339 /* default formatter */ XMLFormatter.class);
341 if ((limit <0) || (count < 1))
342 throw new IllegalArgumentException();
344 this.pattern = pattern;
345 this.limit = limit;
346 this.count = count;
347 this.append = append;
351 /* FIXME: Javadoc missing. */
352 private static java.io.OutputStream createFileStream(String pattern,
353 int limit,
354 int count,
355 boolean append,
356 int generation)
358 String path;
359 int unique = 0;
361 /* Throws a SecurityException if the caller does not have
362 * LoggingPermission("control").
364 LogManager.getLogManager().checkAccess();
366 /* Default value from the java.util.logging.FileHandler.pattern
367 * LogManager configuration property.
369 if (pattern == null)
370 pattern = LogManager.getLogManager().getProperty(
371 "java.util.logging.FileHandler.pattern");
372 if (pattern == null)
373 pattern = "%h/java%u.log";
377 path = replaceFileNameEscapes(pattern, generation, unique, count);
381 File file = new File(path);
382 if (file.createNewFile())
383 return new FileOutputStream(path, append);
385 catch (Exception ex)
387 ex.printStackTrace();
390 unique = unique + 1;
391 if (pattern.indexOf("%u") < 0)
392 pattern = pattern + ".%u";
394 while (true);
399 * Replaces the substrings <code>"/"</code> by the value of the
400 * system property <code>"file.separator"</code>, <code>"%t"</code>
401 * by the value of the system property
402 * <code>"java.io.tmpdir"</code>, <code>"%h"</code> by the value of
403 * the system property <code>"user.home"</code>, <code>"%g"</code>
404 * by the value of <code>generation</code>, <code>"%u"</code> by the
405 * value of <code>uniqueNumber</code>, and <code>"%%"</code> by a
406 * single percent character. If <code>pattern</code> does
407 * <em>not</em> contain the sequence <code>"%g"</code>,
408 * the value of <code>generation</code> will be appended to
409 * the result.
411 * @throws NullPointerException if one of the system properties
412 * <code>"file.separator"</code>,
413 * <code>"java.io.tmpdir"</code>, or
414 * <code>"user.home"</code> has no value and the
415 * corresponding escape sequence appears in
416 * <code>pattern</code>.
418 private static String replaceFileNameEscapes(String pattern,
419 int generation,
420 int uniqueNumber,
421 int count)
423 StringBuffer buf = new StringBuffer(pattern);
424 String replaceWith;
425 boolean foundGeneration = false;
427 int pos = 0;
430 // Uncomment the next line for finding bugs.
431 // System.out.println(buf.substring(0,pos) + '|' + buf.substring(pos));
433 if (buf.charAt(pos) == '/')
435 /* The same value is also provided by java.io.File.separator. */
436 replaceWith = System.getProperty("file.separator");
437 buf.replace(pos, pos + 1, replaceWith);
438 pos = pos + replaceWith.length() - 1;
439 continue;
442 if (buf.charAt(pos) == '%')
444 switch (buf.charAt(pos + 1))
446 case 't':
447 replaceWith = System.getProperty("java.io.tmpdir");
448 break;
450 case 'h':
451 replaceWith = System.getProperty("user.home");
452 break;
454 case 'g':
455 replaceWith = Integer.toString(generation);
456 foundGeneration = true;
457 break;
459 case 'u':
460 replaceWith = Integer.toString(uniqueNumber);
461 break;
463 case '%':
464 replaceWith = "%";
465 break;
467 default:
468 replaceWith = "??";
469 break; // FIXME: Throw exception?
472 buf.replace(pos, pos + 2, replaceWith);
473 pos = pos + replaceWith.length() - 1;
474 continue;
477 while (++pos < buf.length() - 1);
479 if (!foundGeneration && (count > 1))
481 buf.append('.');
482 buf.append(generation);
485 return buf.toString();
489 /* FIXME: Javadoc missing, implementation incomplete. */
490 public void publish(LogRecord record)
492 super.publish(record);
494 /* FIXME: Decide when to switch over. How do we get to
495 * the number of bytes published so far? Two possibilities:
496 * 1. File.length, 2. have metering wrapper around
497 * output stream counting the number of written bytes.
500 /* FIXME: Switch over if needed! This implementation always
501 * writes into a single file, i.e. behaves as if limit
502 * always was zero. So, the implementation is somewhat
503 * functional but incomplete.