LogRecord.java, [...]: New files from classpath.

2003-06-21  Michael Koch  <konqueror@gmx.de>

	* java/util/logging/LogRecord.java,
	java/util/logging/Logger.java,
	java/util/logging/SocketHandler.java,
	java/util/logging/SimpleFormatter.java,
	java/util/logging/Formatter.java,
	java/util/logging/ErrorManager.java,
	java/util/logging/Handler.java,
	java/util/logging/FileHandler.java,
	java/util/logging/LogManager.java,
	java/util/logging/Level.java,
	java/util/logging/ConsoleHandler.java,
	java/util/logging/StreamHandler.java,
	java/util/logging/LoggingPermission.java,
	java/util/logging/Filter.java,
	java/util/logging/MemoryHandler.java,
	java/util/logging/XMLFormatter.java:
	New files from classpath.

From-SVN: r68295
This commit is contained in:
Michael Koch 2003-06-21 10:31:55 +00:00 committed by Michael Koch
parent c18cd64247
commit 2d0c9050c3
17 changed files with 6247 additions and 0 deletions

View File

@ -1,3 +1,23 @@
2003-06-21 Michael Koch <konqueror@gmx.de>
* java/util/logging/LogRecord.java,
java/util/logging/Logger.java,
java/util/logging/SocketHandler.java,
java/util/logging/SimpleFormatter.java,
java/util/logging/Formatter.java,
java/util/logging/ErrorManager.java,
java/util/logging/Handler.java,
java/util/logging/FileHandler.java,
java/util/logging/LogManager.java,
java/util/logging/Level.java,
java/util/logging/ConsoleHandler.java,
java/util/logging/StreamHandler.java,
java/util/logging/LoggingPermission.java,
java/util/logging/Filter.java,
java/util/logging/MemoryHandler.java,
java/util/logging/XMLFormatter.java:
New files from classpath.
2003-06-20 Michael Koch <konqueror@gmx.de>
* java/io/ObjectStreamField.java

View File

@ -0,0 +1,129 @@
/* ConsoleHandler.java
-- a class for publishing log messages to System.err
Copyright (C) 2002 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;
/**
* A <code>ConsoleHandler</code> publishes log records to
* <code>System.err</code>.
*
* <p><strong>Configuration:</strong> Values of the subsequent
* <code>LogManager</code> properties are taken into consideration
* when a <code>ConsoleHandler</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.logging.ConsoleHandler.level</code> - specifies
* the initial severity level threshold. Default value:
* <code>Level.INFO</code>.</li>
*
* <li><code>java.util.logging.ConsoleHandler.filter</code> - specifies
* the name of a Filter class. Default value: No Filter.</li>
*
* <li><code>java.util.logging.ConsoleHandler.formatter</code> - specifies
* the name of a Formatter class. Default value:
* <code>java.util.logging.SimpleFormatter</code>.</li>
*
* <li><code>java.util.logging.ConsoleHandler.encoding</code> - specifies
* the name of the character encoding. Default value:
* the default platform encoding.
*
* </ul>
*
* @author Sascha Brawer (brawer@acm.org)
*/
public class ConsoleHandler
extends StreamHandler
{
/**
* Constructs a <code>StreamHandler</code> that publishes
* log records to <code>System.err</code>. The initial
* configuration is determined by the <code>LogManager</code>
* properties described above.
*/
public ConsoleHandler()
{
super(System.err, "java.util.logging.ConsoleHandler", Level.INFO,
/* formatter */ null, SimpleFormatter.class);
}
/**
* Forces any data that may have been buffered to the underlying
* output device, but does <i>not</i> close <code>System.err</code>.
*
* <p>In case of an I/O failure, the <code>ErrorManager</code>
* of this <code>ConsoleHandler</code> will be informed, but the caller
* of this method will not receive an exception.
*/
public void close()
{
flush();
}
/**
* Publishes a <code>LogRecord</code> to the console, provided the
* record passes all tests for being loggable.
*
* <p>Most applications do not need to call this method directly.
* Instead, they will use use a <code>Logger</code>, which will
* create LogRecords and distribute them to registered handlers.
*
* <p>In case of an I/O failure, the <code>ErrorManager</code>
* of this <code>SocketHandler</code> will be informed, but the caller
* of this method will not receive an exception.
*
* <p>The GNU implementation of <code>ConsoleHandler.publish</code>
* calls flush() for every request to publish a record, so
* they appear immediately on the console.
*
* @param record the log event to be published.
*/
public void publish(LogRecord record)
{
super.publish(record);
flush();
}
}

View File

@ -0,0 +1,182 @@
/* ErrorManager.java
-- a class for dealing with errors that a Handler encounters
during logging
Copyright (C) 2002 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;
/**
* An <code>ErrorManager</code> deals with errors that a <code>Handler</code>
* encounters while logging.
*
* @see Handler#setErrorManager(ErrorManager)
*
* @author Sascha Brawer (brawer@acm.org)
*/
public class ErrorManager
{
/* The values have been taken from Sun's public J2SE 1.4 API
* documentation.
* See http://java.sun.com/j2se/1.4/docs/api/constant-values.html
*/
/**
* Indicates that there was a failure that does not readily
* fall into any of the other categories.
*/
public static final int GENERIC_FAILURE = 0;
/**
* Indicates that there was a problem upon writing to
* an output stream.
*/
public static final int WRITE_FAILURE = 1;
/**
* Indicates that there was a problem upon flushing
* an output stream.
*/
public static final int FLUSH_FAILURE = 2;
/**
* Indicates that there was a problem upon closing
* an output stream.
*/
public static final int CLOSE_FAILURE = 3;
/**
* Indicates that there was a problem upon opening
* an output stream.
*/
public static final int OPEN_FAILURE = 4;
/**
* Indicates that there was a problem upon formatting
* the message of a log record.
*/
public static final int FORMAT_FAILURE = 5;
private boolean everUsed = false;
public ErrorManager()
{
}
/**
* Reports an error that occured upon logging. The default implementation
* emits the very first error to System.err, ignoring subsequent errors.
*
* @param message a message describing the error, or <code>null</code> if
* there is no suitable description.
*
* @param ex an exception, or <code>null</code> if the error is not
* related to an exception.
*
* @param errorCode one of the defined error codes, for example
* <code>ErrorManager.CLOSE_FAILURE</code>.
*/
public void error(String message, Exception ex, int errorCode)
{
if (everUsed)
return;
synchronized (ErrorManager.class)
{
/* The double check is intentional. If the first check was
* omitted, the monitor would have to be entered every time
* error() method was called. If the second check was
* omitted, the code below could be executed by multiple
* threads simultaneously.
*/
if (everUsed)
return;
everUsed = true;
}
String codeMsg;
switch (errorCode)
{
case GENERIC_FAILURE:
codeMsg = "GENERIC_FAILURE";
break;
case WRITE_FAILURE:
codeMsg = "WRITE_FAILURE";
break;
case FLUSH_FAILURE:
codeMsg = "FLUSH_FAILURE";
break;
case CLOSE_FAILURE:
codeMsg = "CLOSE_FAILURE";
break;
case OPEN_FAILURE:
codeMsg = "OPEN_FAILURE";
break;
case FORMAT_FAILURE:
codeMsg = "FORMAT_FAILURE";
break;
default:
codeMsg = String.valueOf(errorCode);
break;
}
System.err.println("Error upon logging: " + codeMsg);
if ((message != null) && (message.length() > 0))
System.err.println(message);
if (ex != null)
ex.printStackTrace();
}
}

View File

@ -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.
*/
}
}

View File

@ -0,0 +1,68 @@
/* Filter.java
-- an interface for filters that decide whether a LogRecord should
be published or discarded
Copyright (C) 2002 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;
/**
* By implementing the <code>Filter</code> interface, applications
* can control what is being logged based on arbitrary properties,
* not just the severity level. Both <code>Handler</code> and
* <code>Logger</code> allow to register Filters whose
* <code>isLoggable</code> method will be called when a
* <code>LogRecord</code> has passed the test based on the
* severity level.
*
* @author Sascha Brawer (brawer@acm.org)
*/
public interface Filter
{
/**
* Determines whether a LogRecord should be published or discarded.
*
* @param record the <code>LogRecord</code> to be inspected.
*
* @return <code>true</code> if the record should be published,
* <code>false</code> if it should be discarded.
*/
public boolean isLoggable(LogRecord record);
}

View File

@ -0,0 +1,174 @@
/* Formatter.java
-- a class for formatting log messages by localizing message texts
and performing substitution of parameters
Copyright (C) 2002 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.util.ResourceBundle;
import java.text.MessageFormat;
/**
* A <code>Formatter</code> supports handlers by localizing
* message texts and by subsituting parameter values for their
* placeholders.
*
* @author Sascha Brawer (brawer@acm.org)
*/
public abstract class Formatter
{
/**
* Constructs a new Formatter.
*/
protected Formatter()
{
}
/**
* Formats a LogRecord into a string. Usually called by handlers
* which need a string for a log record, for example to append
* a record to a log file or to transmit a record over the network.
*
* @param record the log record for which a string form is requested.
*/
public abstract String format(LogRecord record);
/**
* Returns a string that handlers are supposed to emit before
* the first log record. The base implementation returns an
* empty string, but subclasses such as {@link XMLFormatter}
* override this method in order to provide a suitable header.
*
* @return a string for the header.
*
* @param handler the handler which will prepend the returned
* string in front of the first log record. This method
* may inspect certain properties of the handler, for
* example its encoding, in order to construct the header.
*/
public String getHead(Handler handler)
{
return "";
}
/**
* Returns a string that handlers are supposed to emit after
* the last log record. The base implementation returns an
* empty string, but subclasses such as {@link XMLFormatter}
* override this method in order to provide a suitable tail.
*
* @return a string for the header.
*
* @param handler the handler which will append the returned
* string after the last log record. This method
* may inspect certain properties of the handler
* in order to construct the tail.
*/
public String getTail(Handler handler)
{
return "";
}
/**
* Formats the message part of a log record.
*
* <p>First, the Formatter localizes the record message to the
* default locale by looking up the message in the record's
* localization resource bundle. If this step fails because there
* is no resource bundle associated with the record, or because the
* record message is not a key in the bundle, the raw message is
* used instead.
*
* <p>Second, the Formatter substitutes appropriate strings for
* the message parameters. If the record returns a non-empty
* array for <code>getParameters()</code> and the localized
* message string contains the character sequence "{0", the
* formatter uses <code>java.text.MessageFormat</code> to format
* the message. Otherwise, no parameter substitution is performed.
*
* @param record the log record to be localized and formatted.
*
* @return the localized message text where parameters have been
* substituted by suitable strings.
*
* @throws NullPointerException if <code>record</code>
* is <code>null</code>.
*/
public String formatMessage(LogRecord record)
{
String msg;
ResourceBundle bundle;
Object[] params;
/* This will throw a NullPointerExceptionif record is null. */
msg = record.getMessage();
if (msg == null)
msg = "";
/* Try to localize the message. */
bundle = record.getResourceBundle();
if (bundle != null)
{
try
{
msg = bundle.getString(msg);
}
catch (java.util.MissingResourceException _)
{
}
}
/* Format the message if there are parameters. */
params = record.getParameters();
if ((params != null)
&& (params.length > 0)
&& (msg.indexOf("{0") >= 0))
{
msg = MessageFormat.format(msg, params);
}
return msg;
}
}

View File

@ -0,0 +1,390 @@
/* Handler.java
-- a class for publishing log messages
Copyright (C) 2002 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.UnsupportedEncodingException;
import java.security.AccessController;
/**
* A <code>Handler</code> publishes <code>LogRecords</code> to
* a sink, for example a file, the console or a network socket.
* There are different subclasses of <code>Handler</code>
* to deal with different kinds of sinks.
*
* <p>FIXME: Are handlers thread-safe, or is the assumption that only
* loggers are, and a handler can belong only to one single logger? If
* the latter, should we enforce it? (Spec not clear). In any
* case, it needs documentation.
*
* @author Sascha Brawer (brawer@acm.org)
*/
public abstract class Handler
{
Formatter formatter;
Filter filter;
Level level;
ErrorManager errorManager;
String encoding;
/**
* Constructs a Handler with a logging severity level of
* <code>Level.ALL</code>, no formatter, no filter, and
* an instance of <code>ErrorManager</code> managing errors.
*
* <p><strong>Specification Note:</strong> The specification of the
* Java<sup>TM</sup> Logging API does not mention which character
* encoding is to be used by freshly constructed Handlers. The GNU
* implementation uses the default platform encoding, but other
* Java implementations might behave differently.
*
* <p><strong>Specification Note:</strong> While a freshly constructed
* Handler is required to have <em>no filter</em> according to the
* specification, <code>null</code> is not a valid parameter for
* <code>Handler.setFormatter</code>. Therefore, the following
* code will throw a <code>java.lang.NullPointerException</code>:
*
* <p><pre>Handler h = new MyConcreteSubclassOfHandler();
h.setFormatter(h.getFormatter());</pre>
*
* It seems strange that a freshly constructed Handler is not
* supposed to provide a Formatter, but this is what the specification
* says.
*/
{
level = Level.ALL;
}
/**
* Publishes a <code>LogRecord</code> to an appropriate sink,
* provided the record passes all tests for being loggable. The
* <code>Handler</code> will localize the message of the log
* record and substitute any message parameters.
*
* <p>Most applications do not need to call this method directly.
* Instead, they will use use a {@link Logger}, which will
* create LogRecords and distribute them to registered handlers.
*
* <p>In case of an I/O failure, the <code>ErrorManager</code>
* of this <code>Handler</code> will be informed, but the caller
* of this method will not receive an exception.
*
* @param record the log event to be published.
*/
public abstract void publish(LogRecord record);
/**
* Forces any data that may have been buffered to the underlying
* output device.
*
* <p>In case of an I/O failure, the <code>ErrorManager</code>
* of this <code>Handler</code> will be informed, but the caller
* of this method will not receive an exception.
*/
public abstract void flush();
/**
* Closes this <code>Handler</code> after having flushed
* the buffers. As soon as <code>close</code> has been called,
* a <code>Handler</code> should not be used anymore. Attempts
* to publish log records, to flush buffers, or to modify the
* <code>Handler</code> in any other way may throw runtime
* exceptions after calling <code>close</code>.
*
* <p>In case of an I/O failure, the <code>ErrorManager</code>
* of this <code>Handler</code> will be informed, but the caller
* of this method will not receive an exception.
*
* @throws SecurityException if a security manager exists and
* the caller is not granted the permission to control
* the logging infrastructure.
*/
public abstract void close()
throws SecurityException;
/**
* Returns the <code>Formatter</code> which will be used to
* localize the text of log messages and to substitute
* message parameters. A <code>Handler</code> is encouraged,
* but not required to actually use an assigned
* <code>Formatter</code>.
*
* @return the <code>Formatter</code> being used, or
* <code>null</code> if this <code>Handler</code>
* does not use formatters and no formatter has
* ever been set by calling <code>setFormatter</code>.
*/
public Formatter getFormatter()
{
return formatter;
}
/**
* Sets the <code>Formatter</code> which will be used to
* localize the text of log messages and to substitute
* message parameters. A <code>Handler</code> is encouraged,
* but not required to actually use an assigned
* <code>Formatter</code>.
*
* @param formatter the new <code>Formatter</code> to use.
*
* @throws SecurityException if a security manager exists and
* the caller is not granted the permission to control
* the logging infrastructure.
*
* @throws NullPointerException if <code>formatter</code> is
* <code>null</code>.
*/
public void setFormatter(Formatter formatter)
throws SecurityException
{
LogManager.getLogManager().checkAccess();
/* Throws a NullPointerException if formatter is null. */
formatter.getClass();
this.formatter = formatter;
}
/**
* Returns the character encoding which this handler uses for publishing
* log records.
*
* @param encoding the name of a character encoding, or <code>null</code>
* for the default platform encoding.
*/
public String getEncoding()
{
return encoding;
}
/**
* Sets the character encoding which this handler uses for publishing
* log records. The encoding of a <code>Handler</code> must be
* set before any log records have been published.
*
* @param encoding the name of a character encoding, or <code>null</code>
* for the default encoding.
*
* @exception SecurityException if a security manager exists and
* the caller is not granted the permission to control
* the logging infrastructure.
*
*/
public void setEncoding(String encoding)
throws SecurityException, UnsupportedEncodingException
{
/* Should any developer ever change this implementation, they are
* advised to have a look at StreamHandler.setEncoding(String),
* which overrides this method without calling super.setEncoding.
*/
LogManager.getLogManager().checkAccess();
/* Simple check for supported encodings. This is more expensive
* than it could be, but this method is overwritten by StreamHandler
* anyway.
*/
if (encoding != null)
new String(new byte[0], encoding);
this.encoding = encoding;
}
/**
* Returns the <code>Filter</code> that currently controls which
* log records are being published by this <code>Handler</code>.
*
* @return the currently active <code>Filter</code>, or
* <code>null</code> if no filter has been associated.
* In the latter case, log records are filtered purely
* based on their severity level.
*/
public Filter getFilter()
{
return filter;
}
/**
* Sets the <code>Filter</code> for controlling which
* log records will be published by this <code>Handler</code>.
*
* @return the <code>Filter</code> to use, or
* <code>null</code> to filter log records purely based
* on their severity level.
*/
public void setFilter(Filter filter)
throws SecurityException
{
LogManager.getLogManager().checkAccess();
this.filter = filter;
}
/**
* Returns the <code>ErrorManager</code> that currently deals
* with errors originating from this Handler.
*
* @exception SecurityException if a security manager exists and
* the caller is not granted the permission to control
* the logging infrastructure.
*/
public ErrorManager getErrorManager()
{
LogManager.getLogManager().checkAccess();
/* Developers wanting to change the subsequent code should
* have a look at Handler.reportError -- it also can create
* an ErrorManager, but does so without checking permissions
* to control the logging infrastructure.
*/
if (errorManager == null)
errorManager = new ErrorManager();
return errorManager;
}
public void setErrorManager(ErrorManager manager)
{
LogManager.getLogManager().checkAccess();
/* Make sure manager is not null. */
manager.getClass();
this.errorManager = manager;
}
protected void reportError(String message, Exception ex, int code)
{
if (errorManager == null)
errorManager = new ErrorManager();
errorManager.error(message, ex, code);
}
/**
* Returns the severity level threshold for this <code>Handler</code>
* All log records with a lower severity level will be discarded;
* a log record of the same or a higher level will be published
* unless an installed <code>Filter</code> decides to discard it.
*
* @return the severity level below which all log messages
* will be discarded.
*/
public Level getLevel()
{
return level;
}
/**
* Sets the severity level threshold for this <code>Handler</code>.
* All log records with a lower severity level will be discarded;
* a log record of the same or a higher level will be published
* unless an installed <code>Filter</code> decides to discard it.
*
* @param level the severity level below which all log messages
* will be discarded.
*
* @exception SecurityException if a security manager exists and
* the caller is not granted the permission to control
* the logging infrastructure.
*
* @exception NullPointerException if <code>level</code> is
* <code>null</code>.
*/
public void setLevel(Level level)
{
LogManager.getLogManager().checkAccess();
/* Throw NullPointerException if level is null. */
level.getClass();
this.level = level;
}
/**
* Checks whether a <code>LogRecord</code> would be logged
* if it was passed to this <code>Handler</code> for publication.
*
* <p>The <code>Handler</code> implementation considers a record as
* loggable if its level is greater than or equal to the severity
* level threshold. In a second step, if a {@link Filter} has
* been installed, its {@link Filter#isLoggable(LogRecord) isLoggable}
* method is invoked. Subclasses of <code>Handler</code> can override
* this method to impose their own constraints.
*
* @param record the <code>LogRecord</code> to be checked.
*
* @return <code>true</code> if <code>record</code> would
* be published by {@link #publish(LogRecord) publish},
* <code>false</code> if it would be discarded.
*
* @see #setLevel(Level)
* @see #setFilter(Filter)
* @see Filter#isLoggable(LogRecord)
*
* @throws NullPointerException if <code>record</code>
* is <code>null</code>.
*/
public boolean isLoggable(LogRecord record)
{
if (record.getLevel().intValue() < level.intValue())
return false;
if (filter != null)
return filter.isLoggable(record);
else
return true;
}
}

View File

@ -0,0 +1,417 @@
/* Level.java -- a class for indicating logging levels
Copyright (C) 2002 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.util.ResourceBundle;
/**
* A class for indicating logging levels. A number of commonly used
* levels is pre-defined (such as <code>java.util.logging.Level.INFO</code>),
* and applications should utilize those whenever possible. For specialized
* purposes, however, applications can sub-class Level in order to define
* custom logging levels.
*
* @author Sascha Brawer <brawer@acm.org>
*/
public class Level
implements java.io.Serializable
{
/* The integer values are the same as in the Sun J2SE 1.4.
* They have been obtained with a test program. In J2SE 1.4.1,
* Sun has amended the API documentation; these values are now
* publicly documented.
*/
/**
* The <code>OFF</code> level is used as a threshold for filtering
* log records, meaning that no message should be logged.
*
* @see Logger#setLevel(java.util.logging.Level)
*/
public static final Level OFF = new Level ("OFF", Integer.MAX_VALUE);
/**
* Log records whose level is <code>SEVERE</code> indicate a serious
* failure that prevents normal program execution. Messages at this
* level should be understandable to an inexperienced, non-technical
* end user. Ideally, they explain in simple words what actions the
* user can take in order to resolve the problem.
*/
public static final Level SEVERE = new Level ("SEVERE", 1000);
/**
* Log records whose level is <code>WARNING</code> indicate a
* potential problem that does not prevent normal program execution.
* Messages at this level should be understandable to an
* inexperienced, non-technical end user. Ideally, they explain in
* simple words what actions the user can take in order to resolve
* the problem.
*/
public static final Level WARNING = new Level ("WARNING", 900);
/**
* Log records whose level is <code>INFO</code> are used in purely
* informational situations that do not constitute serious errors or
* potential problems. In the default logging configuration, INFO
* messages will be written to the system console. For this reason,
* the INFO level should be used only for messages that are
* important to end users and system administrators. Messages at
* this level should be understandable to an inexperienced,
* non-technical user.
*/
public static final Level INFO = new Level ("INFO", 800);
/**
* Log records whose level is <code>CONFIG</code> are used for
* describing the static configuration, for example the windowing
* environment, the operating system version, etc.
*/
public static final Level CONFIG = new Level ("CONFIG", 700);
/**
* Log records whose level is <code>FINE</code> are typically used
* for messages that are relevant for developers using
* the component generating log messages. Examples include minor,
* recoverable failures, or possible inefficiencies.
*/
public static final Level FINE = new Level ("FINE", 500);
/**
* Log records whose level is <code>FINER</code> are intended for
* rather detailed tracing, for example entering a method, returning
* from a method, or throwing an exception.
*/
public static final Level FINER = new Level ("FINER", 400);
/**
* Log records whose level is <code>FINEST</code> are used for
* highly detailed tracing, for example to indicate that a certain
* point inside the body of a method has been reached.
*/
public static final Level FINEST = new Level ("FINEST", 300);
/**
* The <code>ALL</code> level is used as a threshold for filtering
* log records, meaning that every message should be logged.
*
* @see Logger#setLevel(java.util.logging.Level)
*/
public static final Level ALL = new Level ("ALL", Integer.MIN_VALUE);
private static final Level[] knownLevels = {
ALL, FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE, OFF
};
/**
* The name of the Level without localizing it, for example
* "WARNING".
*/
private String name;
/**
* The integer value of this <code>Level</code>.
*/
private int value;
/**
* The name of the resource bundle used for localizing the level
* name, or <code>null</code> if the name does not undergo
* localization.
*/
private String resourceBundleName;
/**
* Creates a logging level given a name and an integer value.
* It rarely is necessary to create custom levels,
* as most applications should be well served with one of the
* standard levels such as <code>Level.CONFIG</code>,
* <code>Level.INFO</code>, or <code>Level.FINE</code>.
*
* @param name the name of the level.
*
* @param value the integer value of the level. Please note
* that the Java<small><sup>TM</sup></small>
* Logging API does not specify integer
* values for standard levels (such as
* Level.FINE). Therefore, a custom
* level should pass an integer value that
* is calculated at run-time, e.g.
* <code>(Level.FINE.intValue() + Level.CONFIG.intValue())
* / 2</code> for a level between FINE and CONFIG.
*/
protected Level(String name, int value)
{
this(name, value, null);
}
/**
* Create a logging level given a name, an integer value and a name
* of a resource bundle for localizing the level name. It rarely
* is necessary to create custom levels, as most applications
* should be well served with one of the standard levels such as
* <code>Level.CONFIG</code>, <code>Level.INFO</code>, or
* <code>Level.FINE</code>.
*
* @param name the name of the level.
*
* @param value the integer value of the level. Please note
* that the Java<small><sup>TM</sup></small>
* Logging API does not specify integer
* values for standard levels (such as
* Level.FINE). Therefore, a custom
* level should pass an integer value that
* is calculated at run-time, e.g.
* <code>(Level.FINE.intValue() + Level.CONFIG.intValue())
* / 2</code> for a level between FINE and CONFIG.
*
* @param resourceBundleName the name of a resource bundle
* for localizing the level name, or <code>null</code>
* if the name does not need to be localized.
*/
protected Level(String name, int value, String resourceBundleName)
{
this.name = name;
this.value = value;
this.resourceBundleName = resourceBundleName;
}
static final long serialVersionUID = -8176160795706313070L;
/**
* Checks whether the Level has the same intValue as one of the
* pre-defined levels. If so, the pre-defined level object is
* returned.
*
* <br/>Since the resource bundle name is not taken into
* consideration, it is possible to resolve Level objects that have
* been de-serialized by another implementation, even if the other
* implementation uses a different resource bundle for localizing
* the names of pre-defined levels.
*/
private Object readResolve()
{
for (int i = 0; i < knownLevels.length; i++)
if (value == knownLevels[i].intValue())
return knownLevels[i];
return this;
}
/**
* Returns the name of the resource bundle used for localizing the
* level name.
*
* @return the name of the resource bundle used for localizing the
* level name, or <code>null</code> if the name does not undergo
* localization.
*/
public String getResourceBundleName()
{
return resourceBundleName;
}
/**
* Returns the name of the Level without localizing it, for example
* "WARNING".
*/
public String getName()
{
return name;
}
/**
* Returns the name of the Level after localizing it, for example
* "WARNUNG".
*/
public String getLocalizedName()
{
String localizedName = null;
if (resourceBundleName != null)
{
try
{
ResourceBundle b = ResourceBundle.getBundle(resourceBundleName);
localizedName = b.getString(name);
}
catch (Exception _)
{
}
}
if (localizedName != null)
return localizedName;
else
return name;
}
/**
* Returns the name of the Level without localizing it, for example
* "WARNING".
*/
public final String toString()
{
return getName();
}
/**
* Returns the integer value of the Level.
*/
public final int intValue()
{
return value;
}
/**
* Returns one of the standard Levels given either its name or its
* integer value. Custom subclasses of Level will not be returned
* by this method.
*
* @throws IllegalArgumentException if <code>name</code> is neither
* the name nor the integer value of one of the pre-defined standard
* logging levels.
*
* @throws NullPointerException if <code>name</code> is null.
*
*/
public static Level parse(String name)
throws IllegalArgumentException
{
/* This will throw a NullPointerException if name is null,
* as required by the API specification.
*/
name = name.intern();
for (int i = 0; i < knownLevels.length; i++)
{
if (name == knownLevels[i].name)
return knownLevels[i];
}
try
{
int num = Integer.parseInt(name);
for (int i = 0; i < knownLevels.length; i++)
if (num == knownLevels[i].value)
return knownLevels[i];
}
catch (NumberFormatException _)
{
}
String msg = "Not the name of a standard logging level: \"" + name + "\"";
throw new IllegalArgumentException(msg);
}
/**
* Checks whether this Level's integer value is equal to that of
* another object.
*
* @return <code>true</code> if <code>other</code> is an instance of
* <code>java.util.logging.Level</code> and has the same integer
* value, <code>false</code> otherwise.
*/
public boolean equals(Object other)
{
if (!(other instanceof Level))
return false;
return value == ((Level) other).value;
}
/**
* Returns a hash code for this Level which is based on its numeric
* value.
*/
public int hashCode()
{
return value;
}
/**
* Determines whether or not this Level is one of the standard
* levels specified in the Logging API.
*
* <p>This method is package-private because it is not part
* of the logging API specification. However, an XMLFormatter
* is supposed to emit the numeric value for a custom log
* level, but the name for a pre-defined level. It seems
* cleaner to put this method to Level than to write some
* procedural code for XMLFormatter.
*
* @return <code>true</code> if this Level is a standard level,
* <code>false</code> otherwise.
*/
final boolean isStandardLevel()
{
for (int i = 0; i < knownLevels.length; i++)
if (knownLevels[i] == this)
return true;
return false;
}
}

View File

@ -0,0 +1,821 @@
/* LogManager.java
-- a class for maintaining Loggers and managing configuration
properties
Copyright (C) 2002 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.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collections;
import java.util.Properties;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.lang.ref.WeakReference;
/**
* The <code>LogManager</code> maintains a hierarchical namespace
* of Logger objects and manages properties for configuring the logging
* framework. There exists only one single <code>LogManager</code>
* per virtual machine. This instance can be retrieved using the
* static method {@link #getLogManager()}.
*
* <p><strong>Configuration Process:</strong> The global LogManager
* object is created and configured when the class
* <code>java.util.logging.LogManager</code> is initialized.
* The configuration process includes the subsequent steps:
*
* <ol>
* <li>If the system property <code>java.util.logging.manager</code>
* is set to the name of a subclass of
* <code>java.util.logging.LogManager</code>, an instance of
* that subclass is created and becomes the global LogManager.
* Otherwise, a new instance of LogManager is created.</li>
*
* <li>The <code>LogManager</code> constructor tries to create
* a new instance of the class specified by the system
* property <code>java.util.logging.config.class</code>.
* Typically, the constructor of this class will call
* <code>LogManager.getLogManager().readConfiguration(java.io.InputStream)</code>
* for configuring the logging framework.
* The configuration process stops at this point if
* the system property <code>java.util.logging.config.class</code>
* is set (irrespective of whether the class constructor
* could be called or an exception was thrown).</li>
*
* <li>If the system property <code>java.util.logging.config.class</code>
* is <em>not</em> set, the configuration parameters are read in from
* a file and passed to
* {@link #readConfiguration(java.io.InputStream)}.
* The name and location of this file are specified by the system
* property <code>java.util.logging.config.file</code>.</li>
*
* <li>If the system property <code>java.util.logging.config.file</code>
* is not set, however, the contents of the URL
* "{gnu.classpath.home.url}/logging.properties" are passed to
* {@link #readConfiguration(java.io.InputStream)}.
* Here, "{gnu.classpath.home.url}" stands for the value of
* the system property <code>gnu.classpath.home.url</code>.</li>
* </ol>
*
* @author Sascha Brawer (brawer@acm.org)
*/
public class LogManager
{
/**
* The singleton LogManager instance.
*/
private static LogManager logManager;
/**
* The registered named loggers; maps the name of a Logger to
* a WeakReference to it.
*/
private Map loggers;
final Logger rootLogger;
/**
* The properties for the logging framework which have been
* read in last.
*/
private Properties properties;
/**
* A delegate object that provides support for handling
* PropertyChangeEvents. The API specification does not
* mention which bean should be the source in the distributed
* PropertyChangeEvents, but Mauve test code has determined that
* the Sun J2SE 1.4 reference implementation uses the LogManager
* class object. This is somewhat strange, as the class object
* is not the bean with which listeners have to register, but
* there is no reason for the GNU Classpath implementation to
* behave differently from the reference implementation in
* this case.
*/
private final PropertyChangeSupport pcs
= new PropertyChangeSupport(/* source bean */ LogManager.class);
protected LogManager()
{
if (logManager != null)
throw new IllegalStateException(
"there can be only one LogManager; use LogManager.getLogManager()");
logManager = this;
loggers = new java.util.HashMap();
rootLogger = new Logger("", null);
addLogger(rootLogger);
/* Make sure that Logger.global has the rootLogger as its parent.
*
* Logger.global is set during class initialization of Logger,
* which may or may not be before this code is being executed.
* For example, on the Sun 1.3.1 and 1.4.0 JVMs, Logger.global
* has been set before this code is being executed. In contrast,
* Logger.global still is null on GCJ 3.2. Since the LogManager
* and Logger classes are mutually dependent, both behaviors are
* correct.
*
* This means that we cannot depend on Logger.global to have its
* value when this code executes, although that variable is final.
* Since Logger.getLogger will always return the same logger for
* the same name, the subsequent line works fine irrespective of
* the order in which classes are initialized.
*/
Logger.getLogger("global").setParent(rootLogger);
}
/**
* Returns the globally shared LogManager instance.
*/
public static LogManager getLogManager()
{
return logManager;
}
static
{
makeLogManager();
/* The Javadoc description of the class explains
* what is going on here.
*/
Object configurator = createInstance(
System.getProperty("java.util.logging.config.class"),
/* must be instance of */ Object.class);
try
{
if (configurator == null)
getLogManager().readConfiguration();
}
catch (IOException ex)
{
/* FIXME: Is it ok to ignore exceptions here? */
}
};
private static LogManager makeLogManager()
{
String managerClassName;
LogManager manager;
managerClassName = System.getProperty("java.util.logging.manager");
manager = (LogManager) createInstance(managerClassName, LogManager.class);
if (manager != null)
return manager;
if (managerClassName != null)
System.err.println("WARNING: System property \"java.util.logging.manager\""
+ " should be the name of a subclass of java.util.logging.LogManager");
return new LogManager();
}
/**
* Registers a listener which will be notified when the
* logging properties are re-read.
*/
public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
{
/* do not register null. */
listener.getClass();
pcs.addPropertyChangeListener(listener);
}
/**
* Unregisters a listener.
*
* If <code>listener</code> has not been registered previously,
* nothing happens. Also, no exception is thrown if
* <code>listener</code> is <code>null</code>.
*/
public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
{
if (listener != null)
pcs.removePropertyChangeListener(listener);
}
/**
* Adds a named logger. If a logger with the same name has
* already been registered, the method returns <code>false</code>
* without adding the logger.
*
* <p>The <code>LogManager</code> only keeps weak references
* to registered loggers. Therefore, names can become available
* after automatic garbage collection.
*
* @param logger the logger to be added.
*
* @return <code>true<code>if <code>logger</code> was added,
* <code>false</code> otherwise.
*
* @throws NullPointerException if <code>name<code> is
* <code>null</code>.
*/
public synchronized boolean addLogger(Logger logger)
{
/* To developers thinking about to remove the 'synchronized'
* declaration from this method: Please read the comment
* in java.util.logging.Logger.getLogger(String, String)
* and make sure that whatever you change wrt. synchronization
* does not endanger thread-safety of Logger.getLogger.
* The current implementation of Logger.getLogger assumes
* that LogManager does its synchronization on the globally
* shared instance of LogManager.
*/
String name;
WeakReference ref;
/* This will throw a NullPointerException if logger is null,
* as required by the API specification.
*/
name = logger.getName();
ref = (WeakReference) loggers.get(name);
if (ref != null)
{
if (ref.get() != null)
return false;
/* There has been a logger under this name in the past,
* but it has been garbage collected.
*/
loggers.remove(ref);
}
/* Adding a named logger requires a security permission. */
if ((name != null) && !name.equals(""))
checkAccess();
Logger parent = findAncestor(logger);
loggers.put(name, new WeakReference(logger));
if (parent != logger.getParent())
logger.setParent(parent);
/* It can happen that existing loggers should be children of
* the newly added logger. For example, assume that there
* already exist loggers under the names "", "foo", and "foo.bar.baz".
* When adding "foo.bar", the logger "foo.bar.baz" should change
* its parent to "foo.bar".
*/
if (parent != rootLogger)
{
for (Iterator iter = loggers.keySet().iterator(); iter.hasNext();)
{
Logger possChild = (Logger) ((WeakReference) loggers.get(iter.next())).get();
if ((possChild == null) || (possChild == logger) || (possChild.getParent() != parent))
continue;
if (!possChild.getName().startsWith(name))
continue;
if (possChild.getName().charAt(name.length()) != '.')
continue;
possChild.setParent(logger);
}
}
return true;
}
/**
* Finds the closest ancestor for a logger among the currently
* registered ones. For example, if the currently registered
* loggers have the names "", "foo", and "foo.bar", the result for
* "foo.bar.baz" will be the logger whose name is "foo.bar".
*
* @param child a logger for whose name no logger has been
* registered.
*
* @return the closest ancestor for <code>child</code>,
* or <code>null</code> if <code>child</code>
* is the root logger.
*
* @throws NullPointerException if <code>child</code>
* is <code>null</code>.
*/
private synchronized Logger findAncestor(Logger child)
{
String childName = child.getName();
Logger best = rootLogger;
int bestNameLength = 0;
Logger cand;
String candName;
int candNameLength;
if (child == rootLogger)
return null;
for (Iterator iter = loggers.keySet().iterator(); iter.hasNext();)
{
candName = (String) iter.next();
candNameLength = candName.length();
if ((candNameLength > bestNameLength)
&& childName.startsWith(candName)
&& (childName.charAt(candNameLength) == '.'))
{
cand = (Logger) ((WeakReference) loggers.get(candName)).get();
if ((cand == null) || (cand == child))
continue;
bestNameLength = candName.length();
best = cand;
}
}
return best;
}
/**
* Returns a Logger given its name.
*
* @param name the name of the logger.
*
* @return a named Logger, or <code>null</code> if there is no
* logger with that name.
*
* @throw java.lang.NullPointerException if <code>name</code>
* is <code>null</code>.
*/
public synchronized Logger getLogger(String name)
{
WeakReference ref;
/* Throw a NullPointerException if name is null. */
name.getClass();
ref = (WeakReference) loggers.get(name);
if (ref != null)
return (Logger) ref.get();
else
return null;
}
/**
* Returns an Enumeration of currently registered Logger names.
* Since other threads can register loggers at any time, the
* result could be different any time this method is called.
*
* @return an Enumeration with the names of the currently
* registered Loggers.
*/
public synchronized Enumeration getLoggerNames()
{
return Collections.enumeration(loggers.keySet());
}
/**
* Resets the logging configuration by removing all handlers for
* registered named loggers and setting their level to <code>null</code>.
* The level of the root logger will be set to <code>Level.INFO</code>.
*
* @throws SecurityException if a security manager exists and
* the caller is not granted the permission to control
* the logging infrastructure.
*/
public synchronized void reset()
throws SecurityException
{
/* Throw a SecurityException if the caller does not have the
* permission to control the logging infrastructure.
*/
checkAccess();
properties = new Properties();
Iterator iter = loggers.values().iterator();
while (iter.hasNext())
{
WeakReference ref;
Logger logger;
ref = (WeakReference) iter.next();
if (ref != null)
{
logger = (Logger) ref.get();
if (logger == null)
iter.remove();
else if (logger != rootLogger)
logger.setLevel(null);
}
}
rootLogger.setLevel(Level.INFO);
}
/**
* Configures the logging framework by reading a configuration file.
* The name and location of this file are specified by the system
* property <code>java.util.logging.config.file</code>. If this
* property is not set, the URL
* "{gnu.classpath.home.url}/logging.properties" is taken, where
* "{gnu.classpath.home.url}" stands for the value of the system
* property <code>gnu.classpath.home.url</code>.
*
* <p>The task of configuring the framework is then delegated to
* {@link #readConfiguration(java.io.InputStream)}, which will
* notify registered listeners after having read the properties.
*
* @throws SecurityException if a security manager exists and
* the caller is not granted the permission to control
* the logging infrastructure, or if the caller is
* not granted the permission to read the configuration
* file.
*
* @throws IOException if there is a problem reading in the
* configuration file.
*/
public synchronized void readConfiguration()
throws IOException, SecurityException
{
String path;
InputStream inputStream;
path = System.getProperty("java.util.logging.config.file");
if ((path == null) || (path.length() == 0))
{
String url = (System.getProperty("gnu.classpath.home.url")
+ "/logging.properties");
inputStream = new URL(url).openStream();
}
else
{
inputStream = new java.io.FileInputStream(path);
}
try
{
readConfiguration(inputStream);
}
finally
{
/* Close the stream in order to save
* resources such as file descriptors.
*/
inputStream.close();
}
}
public synchronized void readConfiguration(InputStream inputStream)
throws IOException, SecurityException
{
Properties newProperties;
Enumeration keys;
checkAccess();
newProperties = new Properties();
newProperties.load(inputStream);
this.properties = newProperties;
keys = newProperties.propertyNames();
while (keys.hasMoreElements())
{
String key = (String) keys.nextElement();
String value = newProperties.getProperty(key);
if (value == null)
continue;
if (key.endsWith(".level"))
{
String loggerName = key.substring(0, key.length() - 6);
Logger logger = getLogger(loggerName);
if (logger != null)
{
try
{
logger.setLevel(Level.parse(value));
}
catch (Exception _)
{
}
continue;
}
}
}
/* The API specification does not talk about the
* property name that is distributed with the
* PropertyChangeEvent. With test code, it could
* be determined that the Sun J2SE 1.4 reference
* implementation uses null for the property name.
*/
pcs.firePropertyChange(null, null, null);
}
/**
* Returns the value of a configuration property as a String.
*/
public synchronized String getProperty(String name)
{
if (properties != null)
return properties.getProperty(name);
else
return null;
}
/**
* Returns the value of a configuration property as an integer.
* This function is a helper used by the Classpath implementation
* of java.util.logging, it is <em>not</em> specified in the
* logging API.
*
* @param name the name of the configuration property.
*
* @param defaultValue the value that will be returned if the
* property is not defined, or if its value is not an integer
* number.
*/
static int getIntProperty(String name, int defaultValue)
{
try
{
return Integer.parseInt(getLogManager().getProperty(name));
}
catch (Exception ex)
{
return defaultValue;
}
}
/**
* Returns the value of a configuration property as an integer,
* provided it is inside the acceptable range.
* This function is a helper used by the Classpath implementation
* of java.util.logging, it is <em>not</em> specified in the
* logging API.
*
* @param name the name of the configuration property.
*
* @param minValue the lowest acceptable value.
*
* @param maxValue the highest acceptable value.
*
* @param defaultValue the value that will be returned if the
* property is not defined, or if its value is not an integer
* number, or if it is less than the minimum value,
* or if it is greater than the maximum value.
*/
static int getIntPropertyClamped(String name, int defaultValue,
int minValue, int maxValue)
{
int val = getIntProperty(name, defaultValue);
if ((val < minValue) || (val > maxValue))
val = defaultValue;
return val;
}
/**
* Returns the value of a configuration property as a boolean.
* This function is a helper used by the Classpath implementation
* of java.util.logging, it is <em>not</em> specified in the
* logging API.
*
* @param name the name of the configuration property.
*
* @param defaultValue the value that will be returned if the
* property is not defined, or if its value is neither
* <code>"true"</code> nor <code>"false"</code>.
*/
static boolean getBooleanProperty(String name, boolean defaultValue)
{
try
{
return (new Boolean(getLogManager().getProperty(name)))
.booleanValue();
}
catch (Exception ex)
{
return defaultValue;
}
}
/**
* Returns the value of a configuration property as a Level.
* This function is a helper used by the Classpath implementation
* of java.util.logging, it is <em>not</em> specified in the
* logging API.
*
* @param propertyName the name of the configuration property.
*
* @param defaultValue the value that will be returned if the
* property is not defined, or if
* {@link Level.parse(java.lang.String)} does not like
* the property value.
*/
static Level getLevelProperty(String propertyName, Level defaultValue)
{
try
{
return Level.parse(getLogManager().getProperty(propertyName));
}
catch (Exception ex)
{
return defaultValue;
}
}
/**
* Returns the value of a configuration property as a Class.
* This function is a helper used by the Classpath implementation
* of java.util.logging, it is <em>not</em> specified in the
* logging API.
*
* @param propertyName the name of the configuration property.
*
* @param defaultValue the value that will be returned if the
* property is not defined, or if it does not specify
* the name of a loadable class.
*/
static final Class getClassProperty(String propertyName, Class defaultValue)
{
Class usingClass = null;
try
{
String propertyValue = logManager.getProperty(propertyName);
if (propertyValue != null)
usingClass = Class.forName(propertyValue);
if (usingClass != null)
return usingClass;
}
catch (Exception _)
{
}
return defaultValue;
}
static final Object getInstanceProperty(String propertyName,
Class ofClass,
Class defaultClass)
{
Class klass = getClassProperty(propertyName, defaultClass);
if (klass == null)
return null;
try
{
Object obj = klass.newInstance();
if (ofClass.isInstance(obj))
return obj;
}
catch (Exception _)
{
}
if (defaultClass == null)
return null;
try
{
return defaultClass.newInstance();
}
catch (java.lang.InstantiationException ex)
{
throw new RuntimeException(ex.getMessage());
}
catch (java.lang.IllegalAccessException ex)
{
throw new RuntimeException(ex.getMessage());
}
}
/**
* An instance of <code>LoggingPermission("control")</code>
* that is shared between calls to <code>checkAccess()</code>.
*/
private static final LoggingPermission controlPermission
= new LoggingPermission("control", null);
/**
* Checks whether the current security context allows changing
* the configuration of the logging framework. For the security
* context to be trusted, it has to be granted
* a LoggingPermission("control").
*
* @throws SecurityException if a security manager exists and
* the caller is not granted the permission to control
* the logging infrastructure.
*/
public void checkAccess()
throws SecurityException
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(controlPermission);
}
/**
* Creates a new instance of a class specified by name.
*
* @param className the name of the class of which a new instance
* should be created.
*
* @param ofClass the class to which the new instance should
* be either an instance or an instance of a subclass.
* FIXME: This description is just terrible.
*
* @return the new instance, or <code>null</code> if
* <code>className</code> is <code>null</code>, if no class
* with that name could be found, if there was an error
* loading that class, or if the constructor of the class
* has thrown an exception.
*/
static final Object createInstance(String className, Class ofClass)
{
Class klass;
if ((className == null) || (className.length() == 0))
return null;
try
{
klass = Class.forName(className);
if (!ofClass.isAssignableFrom(klass))
return null;
return klass.newInstance();
}
catch (Exception _)
{
return null;
}
catch (java.lang.LinkageError _)
{
return null;
}
}
}

View File

@ -0,0 +1,675 @@
/* LogRecord.java
-- a class for the state associated with individual logging events
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.util.ResourceBundle;
/**
* A <code>LogRecord</code> contains the state for an individual
* event to be logged.
*
* <p>As soon as a LogRecord instance has been handed over to the
* logging framework, applications should not manipulate it anymore.
*
* @author Sascha Brawer (brawer@acm.org)
*/
public class LogRecord
implements java.io.Serializable
{
/**
* The severity level of this <code>LogRecord</code>.
*/
private Level level;
/**
* The sequence number of this <code>LogRecord</code>.
*/
private long sequenceNumber;
/**
* The name of the class that issued the logging request, or
* <code>null</code> if this information could not be obtained.
*/
private String sourceClassName;
/**
* The name of the method that issued the logging request, or
* <code>null</code> if this information could not be obtained.
*/
private String sourceMethodName;
/**
* The message for this <code>LogRecord</code> before
* any localization or formatting.
*/
private String message;
/**
* An identifier for the thread in which this <code>LogRecord</code>
* was created. The identifier is not necessarily related to any
* thread identifiers used by the operating system.
*/
private int threadID;
/**
* The time when this <code>LogRecord</code> was created,
* in milliseconds since the beginning of January 1, 1970.
*/
private long millis;
/**
* The Throwable associated with this <code>LogRecord</code>, or
* <code>null</code> if the logged event is not related to an
* exception or error.
*/
private Throwable thrown;
/**
* The name of the logger where this <code>LogRecord</code> has
* originated, or <code>null</code> if this <code>LogRecord</code>
* does not originate from a <code>Logger</code>.
*/
private String loggerName;
/**
* The name of the resource bundle used for localizing log messages,
* or <code>null</code> if no bundle has been specified.
*/
private String resourceBundleName;
private transient Object[] parameters;
private transient ResourceBundle bundle;
/**
* Constructs a <code>LogRecord</code> given a severity level and
* an unlocalized message text. In addition, the sequence number,
* creation time (as returned by <code>getMillis()</code>) and
* thread ID are assigned. All other properties are set to
* <code>null</code>.
*
* @param level the severity level, for example <code>Level.WARNING</code>.
*
* @param message the message text (which will be used as key
* for looking up the localized message text
* if a resource bundle has been associated).
*/
public LogRecord(Level level, String message)
{
this.level = level;
this.message = message;
this.millis = System.currentTimeMillis();
/* A subclass of java.lang.Thread could override hashCode(),
* in which case the result would not be guaranteed anymore
* to be unique among all threads. While System.identityHashCode
* is not necessarily unique either, it at least cannot be
* overridden by user code. However, is might be a good idea
* to use something better for generating thread IDs.
*/
this.threadID = System.identityHashCode(Thread.currentThread());
sequenceNumber = allocateSeqNum();
}
/**
* Determined with the serialver tool of the Sun J2SE 1.4.
*/
static final long serialVersionUID = 5372048053134512534L;
private void readObject(java.io.ObjectInputStream in)
throws java.io.IOException, java.lang.ClassNotFoundException
{
in.defaultReadObject();
/* We assume that future versions will be downwards compatible,
* so we can ignore the versions.
*/
byte majorVersion = in.readByte();
byte minorVersion = in.readByte();
int numParams = in.readInt();
if (numParams >= 0)
{
parameters = new Object[numParams];
for (int i = 0; i < numParams; i++)
parameters[i] = in.readObject();
}
}
/**
* @serialData The default fields, followed by a major byte version
* number, followed by a minor byte version number, followed by
* information about the log record parameters. If
* <code>parameters</code> is <code>null</code>, the integer -1 is
* written, otherwise the length of the <code>parameters</code>
* array (which can be zero), followed by the result of calling
* {@link Object#toString() toString()} on the parameter (or
* <code>null</code> if the parameter is <code>null</code>).
*
* <p><strong>Specification Note:</strong> The Javadoc for the
* Sun reference implementation does not specify the version
* number. FIXME: Reverse-engineer the JDK and file a bug
* report with Sun, asking for amendment of the specification.
*/
private void writeObject(java.io.ObjectOutputStream out)
throws java.io.IOException
{
out.defaultWriteObject();
/* Major, minor version number: The Javadoc for J2SE1.4 does not
* specify the values.
*/
out.writeByte(0);
out.writeByte(0);
if (parameters == null)
out.writeInt(-1);
else
{
out.writeInt(parameters.length);
for (int i = 0; i < parameters.length; i++)
{
if (parameters[i] == null)
out.writeObject(null);
else
out.writeObject(parameters[i].toString());
}
}
}
/**
* Returns the name of the logger where this <code>LogRecord</code>
* has originated.
*
* @return the name of the source {@link Logger}, or
* <code>null</code> if this <code>LogRecord</code>
* does not originate from a <code>Logger</code>.
*/
public String getLoggerName()
{
return loggerName;
}
/**
* Sets the name of the logger where this <code>LogRecord</code>
* has originated.
*
* <p>As soon as a <code>LogRecord</code> has been handed over
* to the logging framework, applications should not modify it
* anymore. Therefore, this method should only be called on
* freshly constructed LogRecords.
*
* @param name the name of the source logger, or <code>null</code> to
* indicate that this <code>LogRecord</code> does not
* originate from a <code>Logger</code>.
*/
public void setLoggerName(String name)
{
loggerName = name;
}
/**
* Returns the resource bundle that is used when the message
* of this <code>LogRecord</code> needs to be localized.
*
* @return the resource bundle used for localization,
* or <code>null</code> if this message does not need
* to be localized.
*/
public ResourceBundle getResourceBundle()
{
return bundle;
}
/**
* Sets the resource bundle that is used when the message
* of this <code>LogRecord</code> needs to be localized.
*
* <p>As soon as a <code>LogRecord</code> has been handed over
* to the logging framework, applications should not modify it
* anymore. Therefore, this method should only be called on
* freshly constructed LogRecords.
*
* @param bundle the resource bundle to be used, or
* <code>null</code> to indicate that this
* message does not need to be localized.
*/
public void setResourceBundle(ResourceBundle bundle)
{
this.bundle = bundle;
/* FIXME: Is there a way to infer the name
* of a resource bundle from a ResourceBundle object?
*/
this.resourceBundleName = null;
}
/**
* Returns the name of the resource bundle that is used when the
* message of this <code>LogRecord</code> needs to be localized.
*
* @return the name of the resource bundle used for localization,
* or <code>null</code> if this message does not need
* to be localized.
*/
public String getResourceBundleName()
{
return resourceBundleName;
}
/**
* Sets the name of the resource bundle that is used when the
* message of this <code>LogRecord</code> needs to be localized.
*
* <p>As soon as a <code>LogRecord</code> has been handed over
* to the logging framework, applications should not modify it
* anymore. Therefore, this method should only be called on
* freshly constructed LogRecords.
*
* @param name the name of the resource bundle to be used, or
* <code>null</code> to indicate that this message
* does not need to be localized.
*/
public void setResourceBundleName(String name)
{
resourceBundleName = name;
bundle = null;
try
{
if (resourceBundleName != null)
bundle = ResourceBundle.getBundle(resourceBundleName);
}
catch (java.util.MissingResourceException _)
{
}
}
/**
* Returns the level of the LogRecord.
*
* <p>Applications should be aware of the possibility that the
* result is not necessarily one of the standard logging levels,
* since the logging framework allows to create custom subclasses
* of <code>java.util.logging.Level</code>. Therefore, filters
* should perform checks like <code>theRecord.getLevel().intValue()
* == Level.INFO.intValue()</code> instead of <code>theRecord.getLevel()
* == Level.INFO</code>.
*/
public Level getLevel()
{
return level;
}
/**
* Sets the severity level of this <code>LogRecord</code> to a new
* value.
*
* <p>As soon as a <code>LogRecord</code> has been handed over
* to the logging framework, applications should not modify it
* anymore. Therefore, this method should only be called on
* freshly constructed LogRecords.
*
* @param level the new severity level, for example
* <code>Level.WARNING</code>.
*/
public void setLevel(Level level)
{
this.level = level;
}
/**
* The last used sequence number for any LogRecord.
*/
private static long lastSeqNum = 0;
/**
* Allocates a sequence number for a new LogRecord. This class
* method is only called by the LogRecord constructor.
*/
private synchronized static long allocateSeqNum()
{
lastSeqNum += 1;
return lastSeqNum;
}
/**
* Returns the sequence number of this <code>LogRecord</code>.
*/
public long getSequenceNumber()
{
return sequenceNumber;
}
/**
* Sets the sequence number of this <code>LogRecord</code> to a new
* value.
*
* <p>As soon as a <code>LogRecord</code> has been handed over
* to the logging framework, applications should not modify it
* anymore. Therefore, this method should only be called on
* freshly constructed LogRecords.
*
* @param seqNum the new sequence number.
*/
public void setSequenceNumber(long seqNum)
{
this.sequenceNumber = seqNum;
}
/**
* Returns the name of the class where the event being logged
* has had its origin. This information can be passed as
* parameter to some logging calls, and in certain cases, the
* logging framework tries to determine an approximation
* (which may or may not be accurate).
*
* @return the name of the class that issued the logging request,
* or <code>null</code> if this information could not
* be obtained.
*/
public String getSourceClassName()
{
if (sourceClassName != null)
return sourceClassName;
/* FIXME: Should infer this information from the call stack. */
return null;
}
/**
* Sets the name of the class where the event being logged
* has had its origin.
*
* <p>As soon as a <code>LogRecord</code> has been handed over
* to the logging framework, applications should not modify it
* anymore. Therefore, this method should only be called on
* freshly constructed LogRecords.
*
* @param sourceClassName the name of the class that issued the
* logging request, or <code>null</code> to indicate that
* this information could not be obtained.
*/
public void setSourceClassName(String sourceClassName)
{
this.sourceClassName = sourceClassName;
}
/**
* Returns the name of the method where the event being logged
* has had its origin. This information can be passed as
* parameter to some logging calls, and in certain cases, the
* logging framework tries to determine an approximation
* (which may or may not be accurate).
*
* @return the name of the method that issued the logging request,
* or <code>null</code> if this information could not
* be obtained.
*/
public String getSourceMethodName()
{
if (sourceMethodName != null)
return sourceMethodName;
/* FIXME: Should infer this information from the call stack. */
return null;
}
/**
* Sets the name of the method where the event being logged
* has had its origin.
*
* <p>As soon as a <code>LogRecord</code> has been handed over
* to the logging framework, applications should not modify it
* anymore. Therefore, this method should only be called on
* freshly constructed LogRecords.
*
* @param sourceMethodName the name of the method that issued the
* logging request, or <code>null</code> to indicate that
* this information could not be obtained.
*/
public void setSourceMethodName(String sourceMethodName)
{
this.sourceMethodName = sourceMethodName;
}
/**
* Returns the message for this <code>LogRecord</code> before
* any localization or parameter substitution.
*
* <p>A {@link Logger} will try to localize the message
* if a resource bundle has been associated with this
* <code>LogRecord</code>. In this case, the logger will call
* <code>getMessage()</code> and use the result as the key
* for looking up the localized message in the bundle.
* If no bundle has been associated, or if the result of
* <code>getMessage()</code> is not a valid key in the
* bundle, the logger will use the raw message text as
* returned by this method.
*
* @return the message text, or <code>null</code> if there
* is no message text.
*/
public String getMessage()
{
return message;
}
/**
* Sets the message for this <code>LogRecord</code>.
*
* <p>A <code>Logger</code> will try to localize the message
* if a resource bundle has been associated with this
* <code>LogRecord</code>. In this case, the logger will call
* <code>getMessage()</code> and use the result as the key
* for looking up the localized message in the bundle.
* If no bundle has been associated, or if the result of
* <code>getMessage()</code> is not a valid key in the
* bundle, the logger will use the raw message text as
* returned by this method.
*
* <p>It is possible to set the message to either an empty String or
* <code>null</code>, although this does not make the the message
* very helpful to human users.
*
* @param message the message text (which will be used as key
* for looking up the localized message text
* if a resource bundle has been associated).
*/
public void setMessage(String message)
{
this.message = message;
}
/**
* Returns the parameters to the log message.
*
* @return the parameters to the message, or <code>null</code> if
* the message has no parameters.
*/
public Object[] getParameters()
{
return parameters;
}
/**
* Sets the parameters to the log message.
*
* <p>As soon as a <code>LogRecord</code> has been handed over
* to the logging framework, applications should not modify it
* anymore. Therefore, this method should only be called on
* freshly constructed LogRecords.
*
* @param parameters the parameters to the message, or <code>null</code>
* to indicate that the message has no parameters.
*/
public void setParameters(Object[] parameters)
{
this.parameters = parameters;
}
/**
* Returns an identifier for the thread in which this
* <code>LogRecord</code> was created. The identifier is not
* necessarily related to any thread identifiers used by the
* operating system.
*
* @return an identifier for the source thread.
*/
public int getThreadID()
{
return threadID;
}
/**
* Sets the identifier indicating in which thread this
* <code>LogRecord</code> was created. The identifier is not
* necessarily related to any thread identifiers used by the
* operating system.
*
* <p>As soon as a <code>LogRecord</code> has been handed over
* to the logging framework, applications should not modify it
* anymore. Therefore, this method should only be called on
* freshly constructed LogRecords.
*
* @param threadID the identifier for the source thread.
*/
public void setThreadID(int threadID)
{
this.threadID = threadID;
}
/**
* Returns the time when this <code>LogRecord</code> was created.
*
* @return the time of creation in milliseconds since the beginning
* of January 1, 1970.
*/
public long getMillis()
{
return millis;
}
/**
* Sets the time when this <code>LogRecord</code> was created.
*
* <p>As soon as a <code>LogRecord</code> has been handed over
* to the logging framework, applications should not modify it
* anymore. Therefore, this method should only be called on
* freshly constructed LogRecords.
*
* @param millis the time of creation in milliseconds since the
* beginning of January 1, 1970.
*/
public void setMillis(long millis)
{
this.millis = millis;
}
/**
* Returns the Throwable associated with this <code>LogRecord</code>,
* or <code>null</code> if the logged event is not related to an exception
* or error.
*/
public Throwable getThrown()
{
return thrown;
}
/**
* Associates this <code>LogRecord</code> with an exception or error.
*
* <p>As soon as a <code>LogRecord</code> has been handed over
* to the logging framework, applications should not modify it
* anymore. Therefore, this method should only be called on
* freshly constructed LogRecords.
*
* @param thrown the exception or error to associate with, or
* <code>null</code> if this <code>LogRecord</code>
* should be made unrelated to an exception or error.
*/
public void setThrown(Throwable thrown)
{
this.thrown = thrown;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,75 @@
/* LoggingPermission.java -- a class for logging permissions.
Copyright (C) 2002 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;
public final class LoggingPermission
extends java.security.BasicPermission
{
/**
* Creates a new LoggingPermission.
*
* @param name the name of the permission, which must be "control".
*
* @param actions the list of actions for the permission, which
* must be either <code>null</code> or an empty
* string.
*
* @exception IllegalArgumentException if <code>name</code>
* is not "control", or <code>actions</code> is
* neither <code>null</code> nor empty.
*/
public LoggingPermission(String name, String actions)
{
super("control", "");
if (!"control".equals(name))
{
throw new IllegalArgumentException(
"name of LoggingPermission must be \"control\"");
}
if ((actions != null) && (actions.length() != 0))
{
throw new IllegalArgumentException(
"actions of LoggingPermissions must be null or empty");
}
}
}

View File

@ -0,0 +1,356 @@
/* MemoryHandler.java
-- a class for buffering log messages in a memory buffer
Copyright (C) 2002 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;
/**
* A <code>MemoryHandler</code> maintains a circular buffer of
* log records.
*
* <p><strong>Configuration:</strong> Values of the subsequent
* <code>LogManager</code> properties are taken into consideration
* when a <code>MemoryHandler</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.MemoryHandler.level</code> - specifies
* the initial severity level threshold. Default value:
* <code>Level.ALL</code>.</li>
*
* <li><code>java.util.MemoryHandler.filter</code> - specifies
* the name of a Filter class. Default value: No Filter.</li>
*
* <li><code>java.util.MemoryHandler.size</code> - specifies the
* maximum number of log records that are kept in the circular
* buffer. Default value: 1000.</li>
*
* <li><code>java.util.MemoryHandler.push</code> - specifies the
* <code>pushLevel</code>. Default value:
* <code>Level.SEVERE</code>.</li>
*
* <li><code>java.util.MemoryHandler.target</code> - specifies the
* name of a subclass of {@link Handler} that will be used as the
* target handler. There is no default value for this property;
* if it is not set, the no-argument MemoryHandler constructor
* will throw an exception.</li>
*
* </ul>
*
* @author Sascha Brawer (brawer@acm.org)
*/
public class MemoryHandler
extends Handler
{
/**
* The storage area used for buffering the unpushed log records in
* memory.
*/
private final LogRecord[] buffer;
/**
* The current position in the circular buffer. For a new
* MemoryHandler, or immediately after {@link #push()} was called,
* the value of this variable is zero. Each call to {@link
* #publish(LogRecord)} will store the published LogRecord into
* <code>buffer[position]</code> before position is incremented by
* one. If position becomes greater than the size of the buffer, it
* is reset to zero.
*/
private int position;
/**
* The number of log records which have been published, but not
* pushed yet to the target handler.
*/
private int numPublished;
/**
* The push level threshold for this <code>Handler</code>. When a
* record is published whose severity level is greater than or equal
* to the <code>pushLevel</code> of this <code>MemoryHandler</code>,
* the {@link #push()} method will be invoked for pushing the buffer
* contents to the target <code>Handler</code>.
*/
private Level pushLevel;
/**
* The Handler to which log records are forwarded for actual
* publication.
*/
private final Handler target;
/**
* Constructs a <code>MemoryHandler</code> for keeping a circular
* buffer of LogRecords; the initial configuration is determined by
* the <code>LogManager</code> properties described above.
*/
public MemoryHandler()
{
this((Handler) LogManager.getInstanceProperty(
"java.util.logging.MemoryHandler.target",
Handler.class, /* default */ null),
LogManager.getIntPropertyClamped(
"java.util.logging.MemoryHandler.size",
/* default */ 1000,
/* minimum value */ 1,
/* maximum value */ Integer.MAX_VALUE),
LogManager.getLevelProperty(
"java.util.logging.MemoryHandler.push",
/* default push level */ Level.SEVERE));
}
/**
* Constructs a <code>MemoryHandler</code> for keeping a circular
* buffer of LogRecords, given some parameters. The values of the
* other parameters are taken from LogManager properties, as
* described above.
*
* @param target the target handler that will receive those
* log records that are passed on for publication.
*
* @param size the number of log records that are kept in the buffer.
* The value must be a at least one.
*
* @param pushLevel the push level threshold for this
* <code>MemoryHandler</code>. When a record is published whose
* severity level is greater than or equal to
* <code>pushLevel</code>, the {@link #push()} method will be
* invoked in order to push the bufffer contents to
* <code>target</code>.
*
* @throws java.lang.IllegalArgumentException if <code>size</code>
* is negative or zero. The GNU implementation also throws
* an IllegalArgumentException if <code>target</code> or
* <code>pushLevel</code> are <code>null</code>, but the
* API specification does not prescribe what should happen
* in those cases.
*/
public MemoryHandler(Handler target, int size, Level pushLevel)
{
if ((target == null) || (size <= 0) || (pushLevel == null))
throw new IllegalArgumentException();
buffer = new LogRecord[size];
this.pushLevel = pushLevel;
this.target = target;
setLevel(LogManager.getLevelProperty(
"java.util.logging.MemoryHandler.level",
/* default value */ Level.ALL));
setFilter((Filter) LogManager.getInstanceProperty(
"java.util.logging.MemoryHandler.filter",
/* must be instance of */ Filter.class,
/* default value */ null));
}
/**
* Stores a <code>LogRecord</code> in a fixed-size circular buffer,
* provided the record passes all tests for being loggable. If the
* buffer is full, the oldest record will be discarded.
*
* <p>If the record has a severity level which is greater than or
* equal to the <code>pushLevel</code> of this
* <code>MemoryHandler</code>, the {@link #push()} method will be
* invoked for pushing the buffer contents to the target
* <code>Handler</code>.
*
* <p>Most applications do not need to call this method directly.
* Instead, they will use use a {@link Logger}, which will create
* LogRecords and distribute them to registered handlers.
*
* @param record the log event to be published.
*/
public void publish(LogRecord record)
{
if (!isLoggable(record))
return;
buffer[position] = record;
position = (position + 1) % buffer.length;
numPublished = numPublished + 1;
if (record.getLevel().intValue() >= pushLevel.intValue())
push();
}
/**
* Pushes the contents of the memory buffer to the target
* <code>Handler</code> and clears the buffer. Note that
* the target handler will discard those records that do
* not satisfy its own severity level threshold, or that are
* not considered loggable by an installed {@link Filter}.
*
* <p>In case of an I/O failure, the {@link ErrorManager} of the
* target <code>Handler</code> will be notified, but the caller of
* this method will not receive an exception.
*/
public void push()
{
int i;
if (numPublished < buffer.length)
{
for (i = 0; i < position; i++)
target.publish(buffer[i]);
}
else
{
for (i = position; i < buffer.length; i++)
target.publish(buffer[i]);
for (i = 0; i < position; i++)
target.publish(buffer[i]);
}
numPublished = 0;
position = 0;
}
/**
* Forces any data that may have been buffered by the target
* <code>Handler</code> to the underlying output device, but
* does <em>not</em> push the contents of the circular memory
* buffer to the target handler.
*
* <p>In case of an I/O failure, the {@link ErrorManager} of the
* target <code>Handler</code> will be notified, but the caller of
* this method will not receive an exception.
*
* @see #push()
*/
public void flush()
{
target.flush();
}
/**
* Closes this <code>MemoryHandler</code> and its associated target
* handler, discarding the contents of the memory buffer. However,
* any data that may have been buffered by the target
* <code>Handler</code> is forced to the underlying output device.
*
* <p>As soon as <code>close</code> has been called,
* a <code>Handler</code> should not be used anymore. Attempts
* to publish log records, to flush buffers, or to modify the
* <code>Handler</code> in any other way may throw runtime
* exceptions after calling <code>close</code>.</p>
*
* <p>In case of an I/O failure, the <code>ErrorManager</code> of
* the associated target <code>Handler</code> will be informed, but
* the caller of this method will not receive an exception.</p>
*
* @throws SecurityException if a security manager exists and
* the caller is not granted the permission to control
* the logging infrastructure.
*
* @see #push()
*/
public void close()
throws SecurityException
{
push();
/* This will check for LoggingPermission("control"). If the
* current security context does not grant this permission,
* push() has been executed, but this does not impose a
* security risk.
*/
target.close();
}
/**
* Returns the push level threshold for this <code>Handler</code>.
* When a record is published whose severity level is greater
* than or equal to the <code>pushLevel</code> of this
* <code>MemoryHandler</code>, the {@link #push()} method will be
* invoked for pushing the buffer contents to the target
* <code>Handler</code>.
*
* @return the push level threshold for automatic pushing.
*/
public Level getPushLevel()
{
return pushLevel;
}
/**
* Sets the push level threshold for this <code>Handler</code>.
* When a record is published whose severity level is greater
* than or equal to the <code>pushLevel</code> of this
* <code>MemoryHandler</code>, the {@link #push()} method will be
* invoked for pushing the buffer contents to the target
* <code>Handler</code>.
*
* @param pushLevel the push level threshold for automatic pushing.
*
* @exception SecurityException if a security manager exists and
* the caller is not granted the permission to control
* the logging infrastructure.
*
* @exception NullPointerException if <code>pushLevel</code> is
* <code>null</code>.
*/
public void setPushLevel(Level pushLevel)
{
LogManager.getLogManager().checkAccess();
/* Throws a NullPointerException if pushLevel is null. */
pushLevel.getClass();
this.pushLevel = pushLevel;
}
}

View File

@ -0,0 +1,120 @@
/* SimpleFormatter.java
-- a class for formatting log records into short human-readable messages
Copyright (C) 2002 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.util.Date;
import java.text.DateFormat;
/**
* A <code>SimpleFormatter</code> formats log records into
* short human-readable messages, typically one or two lines.
*
* @author Sascha Brawer (brawer@acm.org)
*/
public class SimpleFormatter
extends Formatter
{
/**
* Constructs a SimpleFormatter.
*/
public SimpleFormatter()
{
}
/**
* An instance of a DateFormatter that is used for formatting
* the time of a log record into a human-readable string,
* according to the rules of the current locale. The value
* is set after the first invocation of format, since it is
* common that a JVM will instantiate a SimpleFormatter without
* ever using it.
*/
private DateFormat dateFormat;
/**
* The character sequence that is used to separate lines in the
* generated stream. Somewhat surprisingly, the Sun J2SE 1.4
* reference implementation always uses UNIX line endings, even on
* platforms that have different line ending conventions (i.e.,
* DOS). The GNU implementation does not replicate this bug.
*
* @see Sun bug parade, bug #4462871,
* "java.util.logging.SimpleFormatter uses hard-coded line separator".
*/
static final String lineSep = System.getProperty("line.separator");
/**
* Formats a log record into a String.
*
* @param the log record to be formatted.
*
* @return a short human-readable message, typically one or two
* lines. Lines are separated using the default platform line
* separator.
*
* @throws NullPointerException if <code>record</code>
* is <code>null</code>.
*/
public String format(LogRecord record)
{
StringBuffer buf = new StringBuffer(180);
if (dateFormat == null)
dateFormat = DateFormat.getDateTimeInstance();
buf.append(dateFormat.format(new Date(record.getMillis())));
buf.append(' ');
buf.append(record.getLoggerName());
buf.append(lineSep);
buf.append(record.getLevel());
buf.append(": ");
buf.append(formatMessage(record));
buf.append(lineSep);
return buf.toString();
}
}

View File

@ -0,0 +1,225 @@
/* SocketHandler.java
-- a class for publishing log messages to network sockets
Copyright (C) 2002 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;
/**
* A <code>SocketHandler</code> publishes log records to
* a TCP/IP socket.
*
* <p><strong>Configuration:</strong> Values of the subsequent
* <code>LogManager</code> properties are taken into consideration
* when a <code>SocketHandler</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.SocketHandler.level</code> - specifies
* the initial severity level threshold. Default value:
* <code>Level.ALL</code>.</li>
*
* <li><code>java.util.SocketHandler.filter</code> - specifies
* the name of a Filter class. Default value: No Filter.</li>
*
* <li><code>java.util.SocketHandler.formatter</code> - specifies
* the name of a Formatter class. Default value:
* <code>java.util.logging.XMLFormatter</code>.</li>
*
* <li><code>java.util.SocketHandler.encoding</code> - specifies
* the name of the character encoding. Default value:
* the default platform encoding.
*
* <li><code>java.util.SocketHandler.host</code> - specifies
* the name of the host to which records are published.
* There is no default value for this property; if it is
* not set, the SocketHandler constructor will throw
* an exception.</li>
*
* <li><code>java.util.SocketHandler.port</code> - specifies
* the TCP/IP port to which records are published.
* There is no default value for this property; if it is
* not set, the SocketHandler constructor will throw
* an exception.</li>
*
* </ul>
*
* @author Sascha Brawer (brawer@acm.org)
*/
public class SocketHandler
extends StreamHandler
{
/**
* Constructs a <code>SocketHandler</code> that publishes log
* records to a TCP/IP socket. Tthe initial configuration is
* determined by the <code>LogManager</code> properties described
* above.
*
* @throws java.io.IOException if the connection to the specified
* network host and port cannot be established.
*
* @throws java.lang.IllegalArgumentException if either the
* <code>java.util.logging.SocketHandler.host</code>
* or <code>java.util.logging.SocketHandler.port</code>
* LogManager properties is not defined, or specifies
* an invalid value.
*/
public SocketHandler()
throws java.io.IOException
{
this(LogManager.getLogManager().getProperty("java.util.logging.SocketHandler.host"),
getPortNumber());
}
/**
* Constructs a <code>SocketHandler</code> that publishes log
* records to a TCP/IP socket. With the exception of the internet
* host and port, the initial configuration is determined by the
* <code>LogManager</code> properties described above.
*
* @param host the Internet host to which log records will be
* forwarded.
*
* @param port the port at the host which will accept a request
* for a TCP/IP connection.
*
* @throws java.io.IOException if the connection to the specified
* network host and port cannot be established.
*
* @throws java.lang.IllegalArgumentException if either
* <code>host</code> or <code>port</code> specify
* an invalid value.
*/
public SocketHandler(String host, int port)
throws java.io.IOException
{
super(createSocket(host, port),
"java.util.logging.SocketHandler",
/* default level */ Level.ALL,
/* formatter */ null,
/* default formatter */ XMLFormatter.class);
}
/**
* Retrieves the port number from the java.util.logging.SocketHandler.port
* LogManager property.
*
* @throws IllegalArgumentException if the property is not defined or
* does not specify an integer value.
*/
private static int getPortNumber()
{
try {
return Integer.parseInt(LogManager.getLogManager().getProperty("java.util.logging.SocketHandler.port"));
} catch (Exception ex) {
throw new IllegalArgumentException();
}
}
/**
* Creates an OutputStream for publishing log records to an Internet
* host and port. This private method is a helper for use by the
* constructor of SocketHandler.
*
* @param host the Internet host to which log records will be
* forwarded.
*
* @param port the port at the host which will accept a request
* for a TCP/IP connection.
*
* @throws java.io.IOException if the connection to the specified
* network host and port cannot be established.
*
* @throws java.lang.IllegalArgumentException if either
* <code>host</code> or <code>port</code> specify
* an invalid value.
*/
private static java.io.OutputStream createSocket(String host, int port)
throws java.io.IOException, java.lang.IllegalArgumentException
{
java.net.Socket socket;
if ((host == null) || (port < 1))
throw new IllegalArgumentException();
socket = new java.net.Socket(host, port);
socket.shutdownInput();
/* The architecture of the logging framework provides replaceable
* formatters. Because these formatters perform their task by
* returning one single String for each LogRecord to be formatted,
* there is no need to buffer.
*/
socket.setTcpNoDelay(true);
return socket.getOutputStream();
}
/**
* Publishes a <code>LogRecord</code> to the network socket,
* provided the record passes all tests for being loggable.
* In addition, all data that may have been buffered will
* be forced to the network stream.
*
* <p>Most applications do not need to call this method directly.
* Instead, they will use a {@link Logger} instance, which will
* create LogRecords and distribute them to registered handlers.
*
* <p>In case of an I/O failure, the <code>ErrorManager</code>
* of this <code>SocketHandler</code> will be informed, but the caller
* of this method will not receive an exception.
*
* @param record the log event to be published.
*/
public void publish(LogRecord record)
{
super.publish(record);
flush();
}
}

View File

@ -0,0 +1,524 @@
/* StreamHandler.java
-- a class for publishing log messages to instances of java.io.OutputStream
Copyright (C) 2002 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.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
/**
* A <code>StreamHandler</code> publishes <code>LogRecords</code> to
* a instances of <code>java.io.OutputStream</code>.
*
* @author Sascha Brawer (brawer@acm.org)
*/
public class StreamHandler
extends Handler
{
private OutputStream out;
private Writer writer;
/**
* Indicates the current state of this StreamHandler. The value
* should be one of STATE_FRESH, STATE_PUBLISHED, or STATE_CLOSED.
*/
private int streamState = STATE_FRESH;
/**
* streamState having this value indicates that the StreamHandler
* has been created, but the publish(LogRecord) method has not been
* called yet. If the StreamHandler has been constructed without an
* OutputStream, writer will be null, otherwise it is set to a
* freshly created OutputStreamWriter.
*/
private static final int STATE_FRESH = 0;
/**
* streamState having this value indicates that the publish(LocRecord)
* method has been called at least once.
*/
private static final int STATE_PUBLISHED = 1;
/**
* streamState having this value indicates that the close() method
* has been called.
*/
private static final int STATE_CLOSED = 2;
/**
* Creates a <code>StreamHandler</code> without an output stream.
* Subclasses can later use {@link
* #setOutputStream(java.io.OutputStream)} to associate an output
* stream with this StreamHandler.
*/
public StreamHandler()
{
this(null, null);
}
/**
* Creates a <code>StreamHandler</code> that formats log messages
* with the specified Formatter and publishes them to the specified
* output stream.
*
* @param out the output stream to which the formatted log messages
* are published.
*
* @param formatter the <code>Formatter</code> that will be used
* to format log messages.
*/
public StreamHandler(OutputStream out, Formatter formatter)
{
this(out, "java.util.logging.StreamHandler", Level.INFO,
formatter, SimpleFormatter.class);
}
StreamHandler(
OutputStream out,
String propertyPrefix,
Level defaultLevel,
Formatter formatter, Class defaultFormatterClass)
{
this.level = LogManager.getLevelProperty(propertyPrefix + ".level",
defaultLevel);
this.filter = (Filter) LogManager.getInstanceProperty(
propertyPrefix + ".filter",
/* must be instance of */ Filter.class,
/* default: new instance of */ null);
if (formatter != null)
this.formatter = formatter;
else
this.formatter = (Formatter) LogManager.getInstanceProperty(
propertyPrefix + ".formatter",
/* must be instance of */ Formatter.class,
/* default: new instance of */ defaultFormatterClass);
try
{
String enc = LogManager.getLogManager().getProperty(propertyPrefix
+ ".encoding");
/* make sure enc actually is a valid encoding */
if ((enc != null) && (enc.length() > 0))
new String(new byte[0], enc);
this.encoding = enc;
}
catch (Exception _)
{
}
if (out != null)
{
try
{
changeWriter(out, getEncoding());
}
catch (UnsupportedEncodingException uex)
{
/* This should never happen, since the validity of the encoding
* name has been checked above.
*/
throw new RuntimeException(uex.getMessage());
}
}
}
private void checkOpen()
{
if (streamState == STATE_CLOSED)
throw new IllegalStateException(this.toString() + " has been closed");
}
private void checkFresh()
{
checkOpen();
if (streamState != STATE_FRESH)
throw new IllegalStateException("some log records have been published to " + this);
}
private void changeWriter(OutputStream out, String encoding)
throws UnsupportedEncodingException
{
OutputStreamWriter writer;
/* The logging API says that a null encoding means the default
* platform encoding. However, java.io.OutputStreamWriter needs
* another constructor for the default platform encoding,
* passing null would throw an exception.
*/
if (encoding == null)
writer = new OutputStreamWriter(out);
else
writer = new OutputStreamWriter(out, encoding);
/* Closing the stream has side effects -- do this only after
* creating a new writer has been successful.
*/
if ((streamState != STATE_FRESH) || (this.writer != null))
close();
this.writer = writer;
this.out = out;
this.encoding = encoding;
streamState = STATE_FRESH;
}
/**
* Sets the character encoding which this handler uses for publishing
* log records. The encoding of a <code>StreamHandler</code> must be
* set before any log records have been published.
*
* @param encoding the name of a character encoding, or <code>null</code>
* for the default encoding.
*
* @throws SecurityException if a security manager exists and
* the caller is not granted the permission to control the
* the logging infrastructure.
*
* @exception IllegalStateException if any log records have been
* published to this <code>StreamHandler</code> before. Please
* be aware that this is a pecularity of the GNU implementation.
* While the API specification indicates that it is an error
* if the encoding is set after records have been published,
* it does not mandate any specific behavior for that case.
*/
public void setEncoding(String encoding)
throws SecurityException, UnsupportedEncodingException
{
/* The inherited implementation first checks whether the invoking
* code indeed has the permission to control the logging infra-
* structure, and throws a SecurityException if this was not the
* case.
*
* Next, it verifies that the encoding is supported and throws
* an UnsupportedEncodingExcpetion otherwise. Finally, it remembers
* the name of the encoding.
*/
super.setEncoding(encoding);
checkFresh();
/* If out is null, setEncoding is being called before an output
* stream has been set. In that case, we need to check that the
* encoding is valid, and remember it if this is the case. Since
* this is exactly what the inherited implementation of
* Handler.setEncoding does, we can delegate.
*/
if (out != null)
{
/* The logging API says that a null encoding means the default
* platform encoding. However, java.io.OutputStreamWriter needs
* another constructor for the default platform encoding, passing
* null would throw an exception.
*/
if (encoding == null)
writer = new OutputStreamWriter(out);
else
writer = new OutputStreamWriter(out, encoding);
}
}
/**
* Changes the output stream to which this handler publishes
* logging records.
*
* @throws SecurityException if a security manager exists and
* the caller is not granted the permission to control
* the logging infrastructure.
*
* @throws NullPointerException if <code>out</code>
* is <code>null</code>.
*/
protected void setOutputStream(OutputStream out)
throws SecurityException
{
LogManager.getLogManager().checkAccess();
/* Throw a NullPointerException if out is null. */
out.getClass();
try
{
changeWriter(out, getEncoding());
}
catch (UnsupportedEncodingException ex)
{
/* This seems quite unlikely to happen, unless the underlying
* implementation of java.io.OutputStreamWriter changes its
* mind (at runtime) about the set of supported character
* encodings.
*/
throw new RuntimeException(ex.getMessage());
}
}
/**
* Publishes a <code>LogRecord</code> to the associated output
* stream, provided the record passes all tests for being loggable.
* The <code>StreamHandler</code> will localize the message of the
* log record and substitute any message parameters.
*
* <p>Most applications do not need to call this method directly.
* Instead, they will use use a {@link Logger}, which will create
* LogRecords and distribute them to registered handlers.
*
* <p>In case of an I/O failure, the <code>ErrorManager</code>
* of this <code>Handler</code> will be informed, but the caller
* of this method will not receive an exception.
*
* <p>If a log record is being published to a
* <code>StreamHandler</code> that has been closed earlier, the Sun
* J2SE 1.4 reference can be observed to silently ignore the
* call. The GNU implementation, however, intentionally behaves
* differently by informing the <code>ErrorManager</code> associated
* with this <code>StreamHandler</code>. Since the condition
* indicates a programming error, the programmer should be
* informed. It also seems extremely unlikely that any application
* would depend on the exact behavior in this rather obscure,
* erroneous case -- especially since the API specification does not
* prescribe what is supposed to happen.
*
* @param record the log event to be published.
*/
public void publish(LogRecord record)
{
String formattedMessage;
if (!isLoggable(record))
return;
if (streamState == STATE_FRESH)
{
try
{
writer.write(formatter.getHead(this));
}
catch (java.io.IOException ex)
{
reportError(null, ex, ErrorManager.WRITE_FAILURE);
return;
}
catch (Exception ex)
{
reportError(null, ex, ErrorManager.GENERIC_FAILURE);
return;
}
streamState = STATE_PUBLISHED;
}
try
{
formattedMessage = formatter.format(record);
}
catch (Exception ex)
{
reportError(null, ex, ErrorManager.FORMAT_FAILURE);
return;
}
try
{
writer.write(formattedMessage);
}
catch (Exception ex)
{
reportError(null, ex, ErrorManager.WRITE_FAILURE);
}
}
/**
* Checks whether or not a <code>LogRecord</code> would be logged
* if it was passed to this <code>StreamHandler</code> for publication.
*
* <p>The <code>StreamHandler</code> implementation first checks
* whether a writer is present and the handler's level is greater
* than or equal to the severity level threshold. In a second step,
* if a {@link Filter} has been installed, its {@link
* Filter#isLoggable(LogRecord) isLoggable} method is
* invoked. Subclasses of <code>StreamHandler</code> can override
* this method to impose their own constraints.
*
* @param record the <code>LogRecord</code> to be checked.
*
* @return <code>true</code> if <code>record</code> would
* be published by {@link #publish(LogRecord) publish},
* <code>false</code> if it would be discarded.
*
* @see #setLevel(Level)
* @see #setFilter(Filter)
* @see Filter#isLoggable(LogRecord)
*
* @throws NullPointerException if <code>record</code> is
* <code>null</code>. */
public boolean isLoggable(LogRecord record)
{
return (writer != null) && super.isLoggable(record);
}
/**
* Forces any data that may have been buffered to the underlying
* output device.
*
* <p>In case of an I/O failure, the <code>ErrorManager</code>
* of this <code>Handler</code> will be informed, but the caller
* of this method will not receive an exception.
*
* <p>If a <code>StreamHandler</code> that has been closed earlier
* is closed a second time, the Sun J2SE 1.4 reference can be
* observed to silently ignore the call. The GNU implementation,
* however, intentionally behaves differently by informing the
* <code>ErrorManager</code> associated with this
* <code>StreamHandler</code>. Since the condition indicates a
* programming error, the programmer should be informed. It also
* seems extremely unlikely that any application would depend on the
* exact behavior in this rather obscure, erroneous case --
* especially since the API specification does not prescribe what is
* supposed to happen.
*/
public void flush()
{
try
{
checkOpen();
if (writer != null)
writer.flush();
}
catch (Exception ex)
{
reportError(null, ex, ErrorManager.FLUSH_FAILURE);
}
}
/**
* Closes this <code>StreamHandler</code> after having forced any
* data that may have been buffered to the underlying output
* device.
*
* <p>As soon as <code>close</code> has been called,
* a <code>Handler</code> should not be used anymore. Attempts
* to publish log records, to flush buffers, or to modify the
* <code>Handler</code> in any other way may throw runtime
* exceptions after calling <code>close</code>.</p>
*
* <p>In case of an I/O failure, the <code>ErrorManager</code>
* of this <code>Handler</code> will be informed, but the caller
* of this method will not receive an exception.</p>
*
* <p>If a <code>StreamHandler</code> that has been closed earlier
* is closed a second time, the Sun J2SE 1.4 reference can be
* observed to silently ignore the call. The GNU implementation,
* however, intentionally behaves differently by informing the
* <code>ErrorManager</code> associated with this
* <code>StreamHandler</code>. Since the condition indicates a
* programming error, the programmer should be informed. It also
* seems extremely unlikely that any application would depend on the
* exact behavior in this rather obscure, erroneous case --
* especially since the API specification does not prescribe what is
* supposed to happen.
*
* @throws SecurityException if a security manager exists and
* the caller is not granted the permission to control
* the logging infrastructure.
*/
public void close()
throws SecurityException
{
LogManager.getLogManager().checkAccess();
try
{
/* Although flush also calls checkOpen, it catches
* any exceptions and reports them to the ErrorManager
* as flush failures. However, we want to report
* a closed stream as a close failure, not as a
* flush failure here. Therefore, we call checkOpen()
* before flush().
*/
checkOpen();
flush();
if (writer != null)
{
if (formatter != null)
{
/* Even if the StreamHandler has never published a record,
* it emits head and tail upon closing. An earlier version
* of the GNU Classpath implementation did not emitted
* anything. However, this had caused XML log files to be
* entirely empty instead of containing no log records.
*/
if (streamState == STATE_FRESH)
writer.write(formatter.getHead(this));
if (streamState != STATE_CLOSED)
writer.write(formatter.getTail(this));
}
streamState = STATE_CLOSED;
writer.close();
}
}
catch (Exception ex)
{
reportError(null, ex, ErrorManager.CLOSE_FAILURE);
}
}
}

View File

@ -0,0 +1,395 @@
/* XMLFormatter.java
-- a class for formatting log messages into a standard XML format
Copyright (C) 2002 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.util.Date;
import java.util.ResourceBundle;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
/**
* An <code>XMLFormatter</code> formats LogRecords into
* a standard XML format.
*
* @author Sascha Brawer (brawer@acm.org)
*/
public class XMLFormatter
extends Formatter
{
/**
* Constructs a new XMLFormatter.
*/
public XMLFormatter()
{
}
/**
* The character sequence that is used to separate lines in the
* generated XML stream. Somewhat surprisingly, the Sun J2SE 1.4
* reference implementation always uses UNIX line endings, even on
* platforms that have different line ending conventions (i.e.,
* DOS). The GNU Classpath implementation does not replicates this
* bug.
*
* See also the Sun bug parade, bug #4462871,
* "java.util.logging.SimpleFormatter uses hard-coded line separator".
*/
private static final String lineSep = SimpleFormatter.lineSep;
/**
* A DateFormat for emitting time in the ISO 8601 format.
* Since the API specification of SimpleDateFormat does not talk
* about its thread-safety, we cannot share a singleton instance.
*/
private final SimpleDateFormat iso8601
= new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
/**
* Appends a line consisting of indentation, opening element tag,
* element content, closing element tag and line separator to
* a StringBuffer, provided that the element content is
* actually existing.
*
* @param buf the StringBuffer to which the line will be appended.
*
* @param indent the indentation level.
*
* @param tag the element tag name, for instance <code>method</code>.
*
* @param content the element content, or <code>null</code> to
* have no output whatsoever appended to <code>buf</code>.
*/
private static final void appendTag(StringBuffer buf,
int indent,
String tag,
String content)
{
int i;
if (content == null)
return;
for (i = 0; i < indent * 2; i++)
buf.append(' ');
buf.append("<");
buf.append(tag);
buf.append('>');
/* Append the content, but escape for XML by replacing
* '&', '<', '>' and all non-ASCII characters with
* appropriate escape sequences.
* The Sun J2SE 1.4 reference implementation does not
* escape non-ASCII characters. This is a bug in their
* implementation which has been reported in the Java
* bug parade as bug number (FIXME: Insert number here).
*/
for (i = 0; i < content.length(); i++)
{
char c = content.charAt(i);
switch (c)
{
case '&':
buf.append("&amp;");
break;
case '<':
buf.append("&lt;");
break;
case '>':
buf.append("&gt;");
break;
default:
if (((c >= 0x20) && (c <= 0x7e))
|| (c == /* line feed */ 10)
|| (c == /* carriage return */ 13))
buf.append(c);
else
{
buf.append("&#");
buf.append((int) c);
buf.append(';');
}
break;
} /* switch (c) */
} /* for i */
buf.append("</");
buf.append(tag);
buf.append(">");
buf.append(lineSep);
}
/**
* Appends a line consisting of indentation, opening element tag,
* numeric element content, closing element tag and line separator
* to a StringBuffer.
*
* @param buf the StringBuffer to which the line will be appended.
*
* @param indent the indentation level.
*
* @param tag the element tag name, for instance <code>method</code>.
*
* @param content the element content.
*/
private static final void appendTag(StringBuffer buf,
int indent,
String tag,
long content)
{
appendTag(buf, indent, tag, Long.toString(content));
}
public String format(LogRecord record)
{
StringBuffer buf = new StringBuffer(400);
Level level = record.getLevel();
long millis = record.getMillis();
Object[] params = record.getParameters();
ResourceBundle bundle = record.getResourceBundle();
String key, message;
buf.append("<record>");
buf.append(lineSep);
appendTag(buf, 1, "date", iso8601.format(new Date(millis)));
appendTag(buf, 1, "millis", record.getMillis());
appendTag(buf, 1, "sequence", record.getSequenceNumber());
appendTag(buf, 1, "logger", record.getLoggerName());
if (level.isStandardLevel())
appendTag(buf, 1, "level", level.toString());
else
appendTag(buf, 1, "level", level.intValue());
appendTag(buf, 1, "class", record.getSourceClassName());
appendTag(buf, 1, "method", record.getSourceMethodName());
appendTag(buf, 1, "thread", record.getThreadID());
/* The Sun J2SE 1.4 reference implementation does not emit the
* message in localized form. This is in violation of the API
* specification. The GNU Classpath implementation intentionally
* replicates the buggy behavior of the Sun implementation, as
* different log files might be a big nuisance to users.
*/
try
{
record.setResourceBundle(null);
message = formatMessage(record);
}
finally
{
record.setResourceBundle(bundle);
}
appendTag(buf, 1, "message", message);
/* The Sun J2SE 1.4 reference implementation does not
* emit key, catalog and param tags. This is in violation
* of the API specification. The Classpath implementation
* intentionally replicates the buggy behavior of the
* Sun implementation, as different log files might be
* a big nuisance to users.
*
* FIXME: File a bug report with Sun. Insert bug number here.
*
*
* key = record.getMessage();
* if (key == null)
* key = "";
*
* if ((bundle != null) && !key.equals(message))
* {
* appendTag(buf, 1, "key", key);
* appendTag(buf, 1, "catalog", record.getResourceBundleName());
* }
*
* if (params != null)
* {
* for (int i = 0; i < params.length; i++)
* appendTag(buf, 1, "param", params[i].toString());
* }
*/
/* FIXME: We have no way to obtain the stacktrace before free JVMs
* support the corresponding method in java.lang.Throwable. Well,
* it would be possible to parse the output of printStackTrace,
* but this would be pretty kludgy. Instead, we postpose the
* implementation until Throwable has made progress.
*/
Throwable thrown = record.getThrown();
if (thrown != null)
{
buf.append(" <exception>");
buf.append(lineSep);
/* The API specification is not clear about what exactly
* goes into the XML record for a thrown exception: It
* could be the result of getMessage(), getLocalizedMessage(),
* or toString(). Therefore, it was necessary to write a
* Mauve testlet and run it with the Sun J2SE 1.4 reference
* implementation. It turned out that the we need to call
* toString().
*
* FIXME: File a bug report with Sun, asking for clearer
* specs.
*/
appendTag(buf, 2, "message", thrown.toString());
/* FIXME: The Logging DTD specifies:
*
* <!ELEMENT exception (message?, frame+)>
*
* However, java.lang.Throwable.getStackTrace() is
* allowed to return an empty array. So, what frame should
* be emitted for an empty stack trace? We probably
* should file a bug report with Sun, asking for the DTD
* to be changed.
*/
buf.append(" </exception>");
buf.append(lineSep);
}
buf.append("</record>");
buf.append(lineSep);
return buf.toString();
}
/**
* Returns a string that handlers are supposed to emit before
* the first log record. The base implementation returns an
* empty string, but subclasses such as {@link XMLFormatter}
* override this method in order to provide a suitable header.
*
* @return a string for the header.
*
* @param handler the handler which will prepend the returned
* string in front of the first log record. This method
* will inspect certain properties of the handler, for
* example its encoding, in order to construct the header.
*/
public String getHead(Handler h)
{
StringBuffer buf;
String encoding;
buf = new StringBuffer(80);
buf.append("<?xml version=\"1.0\" encoding=\"");
encoding = h.getEncoding();
/* file.encoding is a system property with the Sun JVM, indicating
* the platform-default file encoding. Unfortunately, the API
* specification for java.lang.System.getProperties() does not
* list this property.
*/
if (encoding == null)
encoding = System.getProperty("file.encoding");
/* Since file.encoding is not listed with the API specification of
* java.lang.System.getProperties(), there might be some VMs that
* do not define this system property. Therefore, we use UTF-8 as
* a reasonable default. Please note that if the platform encoding
* uses the same codepoints as US-ASCII for the US-ASCII character
* set (e.g, 65 for A), it does not matter whether we emit the
* wrong encoding into the XML header -- the GNU Classpath will
* emit XML escape sequences like &#1234; for any non-ASCII
* character. Virtually all character encodings use the same code
* points as US-ASCII for ASCII characters. Probably, EBCDIC is
* the only exception.
*/
if (encoding == null)
encoding = "UTF-8";
/* On Windows XP localized for Swiss German (this is one of
* my [Sascha Brawer's] test machines), the default encoding
* has the canonical name "windows-1252". The "historical" name
* of this encoding is "Cp1252" (see the Javadoc for the class
* java.nio.charset.Charset for the distinction). Now, that class
* does have a method for mapping historical to canonical encoding
* names. However, if we used it here, we would be come dependent
* on java.nio.*, which was only introduced with J2SE 1.4.
* Thus, we do this little hack here. As soon as Classpath supports
* java.nio.charset.CharSet, this hack should be replaced by
* code that correctly canonicalizes the encoding name.
*/
if ((encoding.length() > 2) && encoding.startsWith("Cp"))
encoding = "windows-" + encoding.substring(2);
buf.append(encoding);
buf.append("\" standalone=\"no\"?>");
buf.append(lineSep);
/* SYSTEM is not a fully qualified URL so that validating
* XML parsers do not need to connect to the Internet in
* order to read in a log file. See also the Sun Bug Parade,
* bug #4372790, "Logging APIs: need to use relative URL for XML
* doctype".
*/
buf.append("<!DOCTYPE log SYSTEM \"logger.dtd\">");
buf.append(lineSep);
buf.append("<log>");
buf.append(lineSep);
return buf.toString();
}
public String getTail(Handler h)
{
return "</log>" + lineSep;
}
}