OpenCores
URL https://opencores.org/ocsvn/scarts/scarts/trunk

Subversion Repositories scarts

[/] [scarts/] [trunk/] [toolchain/] [scarts-gcc/] [gcc-4.1.1/] [libjava/] [classpath/] [java/] [util/] [logging/] [FileHandler.java] - Blame information for rev 14

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 14 jlechner
/* FileHandler.java -- a class for publishing log messages to log files
2
   Copyright (C) 2002, 2003, 2004, 2005  Free Software Foundation, Inc.
3
 
4
This file is part of GNU Classpath.
5
 
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.
10
 
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.
15
 
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., 51 Franklin Street, Fifth Floor, Boston, MA
19
02110-1301 USA.
20
 
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.
25
 
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. */
37
 
38
 
39
package java.util.logging;
40
 
41
import java.io.File;
42
import java.io.FileOutputStream;
43
import java.io.FilterOutputStream;
44
import java.io.IOException;
45
import java.io.OutputStream;
46
import java.util.LinkedList;
47
import java.util.ListIterator;
48
 
49
/**
50
 * A <code>FileHandler</code> publishes log records to a set of log
51
 * files.  A maximum file size can be specified; as soon as a log file
52
 * reaches the size limit, it is closed and the next file in the set
53
 * is taken.
54
 *
55
 * <p><strong>Configuration:</strong> Values of the subsequent
56
 * <code>LogManager</code> properties are taken into consideration
57
 * when a <code>FileHandler</code> is initialized.  If a property is
58
 * not defined, or if it has an invalid value, a default is taken
59
 * without an exception being thrown.
60
 *
61
 * <ul>
62
 *
63
 * <li><code>java.util.FileHandler.level</code> - specifies
64
 *     the initial severity level threshold. Default value:
65
 *     <code>Level.ALL</code>.</li>
66
 *
67
 * <li><code>java.util.FileHandler.filter</code> - specifies
68
 *     the name of a Filter class. Default value: No Filter.</li>
69
 *
70
 * <li><code>java.util.FileHandler.formatter</code> - specifies
71
 *     the name of a Formatter class. Default value:
72
 *     <code>java.util.logging.XMLFormatter</code>.</li>
73
 *
74
 * <li><code>java.util.FileHandler.encoding</code> - specifies
75
 *     the name of the character encoding. Default value:
76
 *     the default platform encoding.</li>
77
 *
78
 * <li><code>java.util.FileHandler.limit</code> - specifies the number
79
 *     of bytes a log file is approximately allowed to reach before it
80
 *     is closed and the handler switches to the next file in the
81
 *     rotating set.  A value of zero means that files can grow
82
 *     without limit.  Default value: 0 (unlimited growth).</li>
83
 *
84
 * <li><code>java.util.FileHandler.count</code> - specifies the number
85
 *     of log files through which this handler cycles.  Default value:
86
 *     1.</li>
87
 *
88
 * <li><code>java.util.FileHandler.pattern</code> - specifies a
89
 *     pattern for the location and name of the produced log files.
90
 *     See the section on <a href="#filePatterns">file name
91
 *     patterns</a> for details.  Default value:
92
 *     <code>"%h/java%u.log"</code>.</li>
93
 *
94
 * <li><code>java.util.FileHandler.append</code> - specifies
95
 *     whether the handler will append log records to existing
96
 *     files, or whether the handler will clear log files
97
 *     upon switching to them. Default value: <code>false</code>,
98
 *     indicating that files will be cleared.</li>
99
 *
100
 * </ul>
101
 *
102
 * <p><a name="filePatterns"><strong>File Name Patterns:</strong></a>
103
 * The name and location and log files are specified with pattern
104
 * strings. The handler will replace the following character sequences
105
 * when opening log files:
106
 *
107
 * <p><ul>
108
 * <li><code>/</code> - replaced by the platform-specific path name
109
 *     separator.  This value is taken from the system property
110
 *     <code>file.separator</code>.</li>
111
 *
112
 * <li><code>%t</code> - replaced by the platform-specific location of
113
 *     the directory intended for temporary files.  This value is
114
 *     taken from the system property <code>java.io.tmpdir</code>.</li>
115
 *
116
 * <li><code>%h</code> - replaced by the location of the home
117
 *     directory of the current user.  This value is taken from the
118
 *     system property <code>file.separator</code>.</li>
119
 *
120
 * <li><code>%g</code> - replaced by a generation number for
121
 *     distinguisthing the individual items in the rotating set
122
 *     of log files.  The generation number cycles through the
123
 *     sequence 0, 1, ..., <code>count</code> - 1.</li>
124
 *
125
 * <li><code>%u</code> - replaced by a unique number for
126
 *     distinguisthing the output files of several concurrently
127
 *     running processes.  The <code>FileHandler</code> starts
128
 *     with 0 when it tries to open a log file.  If the file
129
 *     cannot be opened because it is currently in use,
130
 *     the unique number is incremented by one and opening
131
 *     is tried again.  These steps are repeated until the
132
 *     opening operation succeeds.
133
 *
134
 *     <p>FIXME: Is the following correct? Please review.  The unique
135
 *     number is determined for each log file individually when it is
136
 *     opened upon switching to the next file.  Therefore, it is not
137
 *     correct to assume that all log files in a rotating set bear the
138
 *     same unique number.
139
 *
140
 *     <p>FIXME: The Javadoc for the Sun reference implementation
141
 *     says: "Note that the use of unique ids to avoid conflicts is
142
 *     only guaranteed to work reliably when using a local disk file
143
 *     system." Why? This needs to be mentioned as well, in case
144
 *     the reviewers decide the statement is true.  Otherwise,
145
 *     file a bug report with Sun.</li>
146
 *
147
 * <li><code>%%</code> - replaced by a single percent sign.</li>
148
 * </ul>
149
 *
150
 * <p>If the pattern string does not contain <code>%g</code> and
151
 * <code>count</code> is greater than one, the handler will append
152
 * the string <code>.%g</code> to the specified pattern.
153
 *
154
 * <p>If the handler attempts to open a log file, this log file
155
 * is being used at the time of the attempt, and the pattern string
156
 * does not contain <code>%u</code>, the handler will append
157
 * the string <code>.%u</code> to the specified pattern. This
158
 * step is performed after any generation number has been
159
 * appended.
160
 *
161
 * <p><em>Examples for the GNU platform:</em>
162
 *
163
 * <p><ul>
164
 *
165
 * <li><code>%h/java%u.log</code> will lead to a single log file
166
 *     <code>/home/janet/java0.log</code>, assuming <code>count</code>
167
 *     equals 1, the user's home directory is
168
 *     <code>/home/janet</code>, and the attempt to open the file
169
 *     succeeds.</li>
170
 *
171
 * <li><code>%h/java%u.log</code> will lead to three log files
172
 *     <code>/home/janet/java0.log.0</code>,
173
 *     <code>/home/janet/java0.log.1</code>, and
174
 *     <code>/home/janet/java0.log.2</code>,
175
 *     assuming <code>count</code> equals 3, the user's home
176
 *     directory is <code>/home/janet</code>, and all attempts
177
 *     to open files succeed.</li>
178
 *
179
 * <li><code>%h/java%u.log</code> will lead to three log files
180
 *     <code>/home/janet/java0.log.0</code>,
181
 *     <code>/home/janet/java1.log.1</code>, and
182
 *     <code>/home/janet/java0.log.2</code>,
183
 *     assuming <code>count</code> equals 3, the user's home
184
 *     directory is <code>/home/janet</code>, and the attempt
185
 *     to open <code>/home/janet/java0.log.1</code> fails.</li>
186
 *
187
 * </ul>
188
 *
189
 * @author Sascha Brawer (brawer@acm.org)
190
 */
191
public class FileHandler
192
  extends StreamHandler
193
{
194
  /**
195
   * The number of bytes a log file is approximately allowed to reach
196
   * before it is closed and the handler switches to the next file in
197
   * the rotating set.  A value of zero means that files can grow
198
   * without limit.
199
   */
200
  private final int limit;
201
 
202
 
203
 /**
204
  * The number of log files through which this handler cycles.
205
  */
206
  private final int count;
207
 
208
 
209
  /**
210
   * The pattern for the location and name of the produced log files.
211
   * See the section on <a href="#filePatterns">file name patterns</a>
212
   * for details.
213
   */
214
  private final String pattern;
215
 
216
 
217
  /**
218
   * Indicates whether the handler will append log records to existing
219
   * files (<code>true</code>), or whether the handler will clear log files
220
   * upon switching to them (<code>false</code>).
221
   */
222
  private final boolean append;
223
 
224
 
225
  /**
226
   * The number of bytes that have currently been written to the stream.
227
   * Package private for use in inner classes.
228
   */
229
  long written;
230
 
231
 
232
  /**
233
   * A linked list of files we are, or have written to. The entries
234
   * are file path strings, kept in the order
235
   */
236
  private LinkedList logFiles;
237
 
238
 
239
  /**
240
   * Constructs a <code>FileHandler</code>, taking all property values
241
   * from the current {@link LogManager LogManager} configuration.
242
   *
243
   * @throws java.io.IOException FIXME: The Sun Javadoc says: "if
244
   *         there are IO problems opening the files."  This conflicts
245
   *         with the general principle that configuration errors do
246
   *         not prohibit construction. Needs review.
247
   *
248
   * @throws SecurityException if a security manager exists and
249
   *         the caller is not granted the permission to control
250
   *         the logging infrastructure.
251
   */
252
  public FileHandler()
253
    throws IOException, SecurityException
254
  {
255
    this(/* pattern: use configiguration */ null,
256
 
257
         LogManager.getIntProperty("java.util.logging.FileHandler.limit",
258
                                   /* default */ 0),
259
 
260
         LogManager.getIntProperty("java.util.logging.FileHandler.count",
261
                                   /* default */ 1),
262
 
263
         LogManager.getBooleanProperty("java.util.logging.FileHandler.append",
264
                                       /* default */ false));
265
  }
266
 
267
 
268
  /* FIXME: Javadoc missing. */
269
  public FileHandler(String pattern)
270
    throws IOException, SecurityException
271
  {
272
    this(pattern,
273
         /* limit */ 0,
274
         /* count */ 1,
275
         /* append */ false);
276
  }
277
 
278
 
279
  /* FIXME: Javadoc missing. */
280
  public FileHandler(String pattern, boolean append)
281
    throws IOException, SecurityException
282
  {
283
    this(pattern,
284
         /* limit */ 0,
285
         /* count */ 1,
286
         append);
287
  }
288
 
289
 
290
  /* FIXME: Javadoc missing. */
291
  public FileHandler(String pattern, int limit, int count)
292
    throws IOException, SecurityException
293
  {
294
    this(pattern, limit, count,
295
         LogManager.getBooleanProperty(
296
           "java.util.logging.FileHandler.append",
297
           /* default */ false));
298
  }
299
 
300
 
301
  /**
302
   * Constructs a <code>FileHandler</code> given the pattern for the
303
   * location and name of the produced log files, the size limit, the
304
   * number of log files thorough which the handler will rotate, and
305
   * the <code>append</code> property.  All other property values are
306
   * taken from the current {@link LogManager LogManager}
307
   * configuration.
308
   *
309
   * @param pattern The pattern for the location and name of the
310
   *        produced log files.  See the section on <a
311
   *        href="#filePatterns">file name patterns</a> for details.
312
   *        If <code>pattern</code> is <code>null</code>, the value is
313
   *        taken from the {@link LogManager LogManager} configuration
314
   *        property
315
   *        <code>java.util.logging.FileHandler.pattern</code>.
316
   *        However, this is a pecularity of the GNU implementation,
317
   *        and Sun's API specification does not mention what behavior
318
   *        is to be expected for <code>null</code>. Therefore,
319
   *        applications should not rely on this feature.
320
   *
321
   * @param limit specifies the number of bytes a log file is
322
   *        approximately allowed to reach before it is closed and the
323
   *        handler switches to the next file in the rotating set.  A
324
   *        value of zero means that files can grow without limit.
325
   *
326
   * @param count specifies the number of log files through which this
327
   *        handler cycles.
328
   *
329
   * @param append specifies whether the handler will append log
330
   *        records to existing files (<code>true</code>), or whether the
331
   *        handler will clear log files upon switching to them
332
   *        (<code>false</code>).
333
   *
334
   * @throws java.io.IOException FIXME: The Sun Javadoc says: "if
335
   *         there are IO problems opening the files."  This conflicts
336
   *         with the general principle that configuration errors do
337
   *         not prohibit construction. Needs review.
338
   *
339
   * @throws SecurityException if a security manager exists and
340
   *         the caller is not granted the permission to control
341
   *         the logging infrastructure.
342
   *         <p>FIXME: This seems in contrast to all other handler
343
   *         constructors -- verify this by running tests against
344
   *         the Sun reference implementation.
345
   */
346
  public FileHandler(String pattern,
347
                     int limit,
348
                     int count,
349
                     boolean append)
350
    throws IOException, SecurityException
351
  {
352
    super(/* output stream, created below */ null,
353
          "java.util.logging.FileHandler",
354
          /* default level */ Level.ALL,
355
          /* formatter */ null,
356
          /* default formatter */ XMLFormatter.class);
357
 
358
    if ((limit <0) || (count < 1))
359
      throw new IllegalArgumentException();
360
 
361
    this.pattern = pattern;
362
    this.limit = limit;
363
    this.count = count;
364
    this.append = append;
365
    this.written = 0;
366
    this.logFiles = new LinkedList ();
367
 
368
    setOutputStream (createFileStream (pattern, limit, count, append,
369
                                       /* generation */ 0));
370
  }
371
 
372
 
373
  /* FIXME: Javadoc missing. */
374
  private OutputStream createFileStream(String pattern,
375
                                        int limit,
376
                                        int count,
377
                                        boolean append,
378
                                        int generation)
379
  {
380
    String  path;
381
    int     unique = 0;
382
 
383
    /* Throws a SecurityException if the caller does not have
384
     * LoggingPermission("control").
385
     */
386
    LogManager.getLogManager().checkAccess();
387
 
388
    /* Default value from the java.util.logging.FileHandler.pattern
389
     * LogManager configuration property.
390
     */
391
    if (pattern == null)
392
      pattern = LogManager.getLogManager().getProperty(
393
                              "java.util.logging.FileHandler.pattern");
394
    if (pattern == null)
395
      pattern = "%h/java%u.log";
396
 
397
    if (count > 1 && !has (pattern, 'g'))
398
      pattern = pattern + ".%g";
399
 
400
    do
401
    {
402
      path = replaceFileNameEscapes(pattern, generation, unique, count);
403
 
404
      try
405
      {
406
        File file = new File(path);
407
        if (!file.exists () || append)
408
          {
409
            FileOutputStream fout = new FileOutputStream (file, append);
410
            // FIXME we need file locks for this to work properly, but they
411
            // are not implemented yet in Classpath! Madness!
412
//             FileChannel channel = fout.getChannel ();
413
//             FileLock lock = channel.tryLock ();
414
//             if (lock != null) // We've locked the file.
415
//               {
416
                if (logFiles.isEmpty ())
417
                  logFiles.addFirst (path);
418
                return new ostr (fout);
419
//               }
420
          }
421
      }
422
      catch (Exception ex)
423
      {
424
        reportError (null, ex, ErrorManager.OPEN_FAILURE);
425
      }
426
 
427
      unique = unique + 1;
428
      if (!has (pattern, 'u'))
429
        pattern = pattern + ".%u";
430
    }
431
    while (true);
432
  }
433
 
434
 
435
  /**
436
   * Replaces the substrings <code>"/"</code> by the value of the
437
   * system property <code>"file.separator"</code>, <code>"%t"</code>
438
   * by the value of the system property
439
   * <code>"java.io.tmpdir"</code>, <code>"%h"</code> by the value of
440
   * the system property <code>"user.home"</code>, <code>"%g"</code>
441
   * by the value of <code>generation</code>, <code>"%u"</code> by the
442
   * value of <code>uniqueNumber</code>, and <code>"%%"</code> by a
443
   * single percent character.  If <code>pattern</code> does
444
   * <em>not</em> contain the sequence <code>"%g"</code>,
445
   * the value of <code>generation</code> will be appended to
446
   * the result.
447
   *
448
   * @throws NullPointerException if one of the system properties
449
   *         <code>"file.separator"</code>,
450
   *         <code>"java.io.tmpdir"</code>, or
451
   *         <code>"user.home"</code> has no value and the
452
   *         corresponding escape sequence appears in
453
   *         <code>pattern</code>.
454
   */
455
  private static String replaceFileNameEscapes(String pattern,
456
                                               int generation,
457
                                               int uniqueNumber,
458
                                               int count)
459
  {
460
    StringBuffer buf = new StringBuffer(pattern);
461
    String       replaceWith;
462
    boolean      foundGeneration = false;
463
 
464
    int pos = 0;
465
    do
466
    {
467
      // Uncomment the next line for finding bugs.
468
      // System.out.println(buf.substring(0,pos) + '|' + buf.substring(pos));
469
 
470
      if (buf.charAt(pos) == '/')
471
      {
472
        /* The same value is also provided by java.io.File.separator. */
473
        replaceWith = System.getProperty("file.separator");
474
        buf.replace(pos, pos + 1, replaceWith);
475
        pos = pos + replaceWith.length() - 1;
476
        continue;
477
      }
478
 
479
      if (buf.charAt(pos) == '%')
480
      {
481
        switch (buf.charAt(pos + 1))
482
        {
483
        case 't':
484
          replaceWith = System.getProperty("java.io.tmpdir");
485
          break;
486
 
487
        case 'h':
488
          replaceWith = System.getProperty("user.home");
489
          break;
490
 
491
        case 'g':
492
          replaceWith = Integer.toString(generation);
493
          foundGeneration = true;
494
          break;
495
 
496
        case 'u':
497
          replaceWith = Integer.toString(uniqueNumber);
498
          break;
499
 
500
        case '%':
501
          replaceWith = "%";
502
          break;
503
 
504
        default:
505
          replaceWith = "??";
506
          break; // FIXME: Throw exception?
507
        }
508
 
509
        buf.replace(pos, pos + 2, replaceWith);
510
        pos = pos + replaceWith.length() - 1;
511
        continue;
512
      }
513
    }
514
    while (++pos < buf.length() - 1);
515
 
516
    if (!foundGeneration && (count > 1))
517
    {
518
      buf.append('.');
519
      buf.append(generation);
520
    }
521
 
522
    return buf.toString();
523
  }
524
 
525
 
526
  /* FIXME: Javadoc missing. */
527
  public void publish(LogRecord record)
528
  {
529
    if (limit > 0 && written >= limit)
530
      rotate ();
531
    super.publish(record);
532
    flush ();
533
  }
534
 
535
  /**
536
   * Rotates the current log files, possibly removing one if we
537
   * exceed the file count.
538
   */
539
  private synchronized void rotate ()
540
  {
541
    if (logFiles.size () > 0)
542
      {
543
        File f1 = null;
544
        ListIterator lit = null;
545
 
546
        // If we reach the file count, ditch the oldest file.
547
        if (logFiles.size () == count)
548
          {
549
            f1 = new File ((String) logFiles.getLast ());
550
            f1.delete ();
551
            lit = logFiles.listIterator (logFiles.size () - 1);
552
          }
553
        // Otherwise, move the oldest to a new location.
554
        else
555
          {
556
            String path = replaceFileNameEscapes (pattern, logFiles.size (),
557
                                                  /* unique */ 0, count);
558
            f1 = new File (path);
559
            logFiles.addLast (path);
560
            lit = logFiles.listIterator (logFiles.size () - 1);
561
          }
562
 
563
        // Now rotate the files.
564
        while (lit.hasPrevious ())
565
          {
566
            String s = (String) lit.previous ();
567
            File f2 = new File (s);
568
            f2.renameTo (f1);
569
            f1 = f2;
570
          }
571
      }
572
 
573
    setOutputStream (createFileStream (pattern, limit, count, append,
574
                                       /* generation */ 0));
575
 
576
    // Reset written count.
577
    written = 0;
578
  }
579
 
580
  /**
581
   * Tell if <code>pattern</code> contains the pattern sequence
582
   * with character <code>escape</code>. That is, if <code>escape</code>
583
   * is 'g', this method returns true if the given pattern contains
584
   * "%g", and not just the substring "%g" (for example, in the case of
585
   * "%%g").
586
   *
587
   * @param pattern The pattern to test.
588
   * @param escape The escape character to search for.
589
   * @return True iff the pattern contains the escape sequence with the
590
   *  given character.
591
   */
592
  private static boolean has (final String pattern, final char escape)
593
  {
594
    final int len = pattern.length ();
595
    boolean sawPercent = false;
596
    for (int i = 0; i < len; i++)
597
      {
598
        char c = pattern.charAt (i);
599
        if (sawPercent)
600
          {
601
            if (c == escape)
602
              return true;
603
            if (c == '%') // Double percent
604
              {
605
                sawPercent = false;
606
                continue;
607
              }
608
          }
609
        sawPercent = (c == '%');
610
      }
611
    return false;
612
  }
613
 
614
  /**
615
   * An output stream that tracks the number of bytes written to it.
616
   */
617
  private final class ostr extends FilterOutputStream
618
  {
619
    private ostr (OutputStream out)
620
    {
621
      super (out);
622
    }
623
 
624
    public void write (final int b) throws IOException
625
    {
626
      out.write (b);
627
      FileHandler.this.written++; // FIXME: synchronize?
628
    }
629
 
630
    public void write (final byte[] b) throws IOException
631
    {
632
      write (b, 0, b.length);
633
    }
634
 
635
    public void write (final byte[] b, final int offset, final int length)
636
      throws IOException
637
    {
638
      out.write (b, offset, length);
639
      FileHandler.this.written += length; // FIXME: synchronize?
640
    }
641
  }
642
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.