feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

View File

@@ -0,0 +1,129 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.logging;
/**
* This <tt>Handler</tt> publishes log records to <tt>System.err</tt>.
* By default the <tt>SimpleFormatter</tt> is used to generate brief summaries.
* <p>
* <b>Configuration:</b>
* By default each <tt>ConsoleHandler</tt> is initialized using the following
* <tt>LogManager</tt> configuration properties where {@code <handler-name>}
* refers to the fully-qualified class name of the handler.
* If properties are not defined
* (or have invalid values) then the specified default values are used.
* <ul>
* <li> &lt;handler-name&gt;.level
* specifies the default level for the <tt>Handler</tt>
* (defaults to <tt>Level.INFO</tt>). </li>
* <li> &lt;handler-name&gt;.filter
* specifies the name of a <tt>Filter</tt> class to use
* (defaults to no <tt>Filter</tt>). </li>
* <li> &lt;handler-name&gt;.formatter
* specifies the name of a <tt>Formatter</tt> class to use
* (defaults to <tt>java.util.logging.SimpleFormatter</tt>). </li>
* <li> &lt;handler-name&gt;.encoding
* the name of the character set encoding to use (defaults to
* the default platform encoding). </li>
* </ul>
* <p>
* For example, the properties for {@code ConsoleHandler} would be:
* <ul>
* <li> java.util.logging.ConsoleHandler.level=INFO </li>
* <li> java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter </li>
* </ul>
* <p>
* For a custom handler, e.g. com.foo.MyHandler, the properties would be:
* <ul>
* <li> com.foo.MyHandler.level=INFO </li>
* <li> com.foo.MyHandler.formatter=java.util.logging.SimpleFormatter </li>
* </ul>
* <p>
* @since 1.4
*/
public class ConsoleHandler extends StreamHandler {
// Private method to configure a ConsoleHandler from LogManager
// properties and/or default values as specified in the class
// javadoc.
private void configure() {
LogManager manager = LogManager.getLogManager();
String cname = getClass().getName();
setLevel(manager.getLevelProperty(cname +".level", Level.INFO));
setFilter(manager.getFilterProperty(cname +".filter", null));
setFormatter(manager.getFormatterProperty(cname +".formatter", new SimpleFormatter()));
try {
setEncoding(manager.getStringProperty(cname +".encoding", null));
} catch (Exception ex) {
try {
setEncoding(null);
} catch (Exception ex2) {
// doing a setEncoding with null should always work.
// assert false;
}
}
}
/**
* Create a <tt>ConsoleHandler</tt> for <tt>System.err</tt>.
* <p>
* The <tt>ConsoleHandler</tt> is configured based on
* <tt>LogManager</tt> properties (or their default values).
*
*/
public ConsoleHandler() {
sealed = false;
configure();
setOutputStream(System.err);
sealed = true;
}
/**
* Publish a <tt>LogRecord</tt>.
* <p>
* The logging request was made initially to a <tt>Logger</tt> object,
* which initialized the <tt>LogRecord</tt> and forwarded it here.
* <p>
* @param record description of the log event. A null record is
* silently ignored and is not published
*/
@Override
public void publish(LogRecord record) {
super.publish(record);
flush();
}
/**
* Override <tt>StreamHandler.close</tt> to do a flush but not
* to close the output stream. That is, we do <b>not</b>
* close <tt>System.err</tt>.
*/
@Override
public void close() {
flush();
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright (c) 2001, 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.logging;
/**
* ErrorManager objects can be attached to Handlers to process
* any error that occurs on a Handler during Logging.
* <p>
* When processing logging output, if a Handler encounters problems
* then rather than throwing an Exception back to the issuer of
* the logging call (who is unlikely to be interested) the Handler
* should call its associated ErrorManager.
*/
public class ErrorManager {
private boolean reported = false;
/*
* We declare standard error codes for important categories of errors.
*/
/**
* GENERIC_FAILURE is used for failure that don't fit
* into one of the other categories.
*/
public final static int GENERIC_FAILURE = 0;
/**
* WRITE_FAILURE is used when a write to an output stream fails.
*/
public final static int WRITE_FAILURE = 1;
/**
* FLUSH_FAILURE is used when a flush to an output stream fails.
*/
public final static int FLUSH_FAILURE = 2;
/**
* CLOSE_FAILURE is used when a close of an output stream fails.
*/
public final static int CLOSE_FAILURE = 3;
/**
* OPEN_FAILURE is used when an open of an output stream fails.
*/
public final static int OPEN_FAILURE = 4;
/**
* FORMAT_FAILURE is used when formatting fails for any reason.
*/
public final static int FORMAT_FAILURE = 5;
/**
* The error method is called when a Handler failure occurs.
* <p>
* This method may be overridden in subclasses. The default
* behavior in this base class is that the first call is
* reported to System.err, and subsequent calls are ignored.
*
* @param msg a descriptive string (may be null)
* @param ex an exception (may be null)
* @param code an error code defined in ErrorManager
*/
public synchronized void error(String msg, Exception ex, int code) {
if (reported) {
// We only report the first error, to avoid clogging
// the screen.
return;
}
reported = true;
String text = "java.util.logging.ErrorManager: " + code;
if (msg != null) {
text = text + ": " + msg;
}
System.err.println(text);
if (ex != null) {
ex.printStackTrace();
}
}
}

View File

@@ -0,0 +1,775 @@
/*
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.logging;
import static java.nio.file.StandardOpenOption.APPEND;
import static java.nio.file.StandardOpenOption.CREATE_NEW;
import static java.nio.file.StandardOpenOption.WRITE;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.channels.FileChannel;
import java.nio.channels.OverlappingFileLockException;
import java.nio.file.AccessDeniedException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashSet;
import java.util.Set;
/**
* Simple file logging <tt>Handler</tt>.
* <p>
* The <tt>FileHandler</tt> can either write to a specified file,
* or it can write to a rotating set of files.
* <p>
* For a rotating set of files, as each file reaches a given size
* limit, it is closed, rotated out, and a new file opened.
* Successively older files are named by adding "0", "1", "2",
* etc. into the base filename.
* <p>
* By default buffering is enabled in the IO libraries but each log
* record is flushed out when it is complete.
* <p>
* By default the <tt>XMLFormatter</tt> class is used for formatting.
* <p>
* <b>Configuration:</b>
* By default each <tt>FileHandler</tt> is initialized using the following
* <tt>LogManager</tt> configuration properties where <tt>&lt;handler-name&gt;</tt>
* refers to the fully-qualified class name of the handler.
* If properties are not defined
* (or have invalid values) then the specified default values are used.
* <ul>
* <li> &lt;handler-name&gt;.level
* specifies the default level for the <tt>Handler</tt>
* (defaults to <tt>Level.ALL</tt>). </li>
* <li> &lt;handler-name&gt;.filter
* specifies the name of a <tt>Filter</tt> class to use
* (defaults to no <tt>Filter</tt>). </li>
* <li> &lt;handler-name&gt;.formatter
* specifies the name of a <tt>Formatter</tt> class to use
* (defaults to <tt>java.util.logging.XMLFormatter</tt>) </li>
* <li> &lt;handler-name&gt;.encoding
* the name of the character set encoding to use (defaults to
* the default platform encoding). </li>
* <li> &lt;handler-name&gt;.limit
* specifies an approximate maximum amount to write (in bytes)
* to any one file. If this is zero, then there is no limit.
* (Defaults to no limit). </li>
* <li> &lt;handler-name&gt;.count
* specifies how many output files to cycle through (defaults to 1). </li>
* <li> &lt;handler-name&gt;.pattern
* specifies a pattern for generating the output file name. See
* below for details. (Defaults to "%h/java%u.log"). </li>
* <li> &lt;handler-name&gt;.append
* specifies whether the FileHandler should append onto
* any existing files (defaults to false). </li>
* </ul>
* <p>
* For example, the properties for {@code FileHandler} would be:
* <ul>
* <li> java.util.logging.FileHandler.level=INFO </li>
* <li> java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter </li>
* </ul>
* <p>
* For a custom handler, e.g. com.foo.MyHandler, the properties would be:
* <ul>
* <li> com.foo.MyHandler.level=INFO </li>
* <li> com.foo.MyHandler.formatter=java.util.logging.SimpleFormatter </li>
* </ul>
* <p>
* A pattern consists of a string that includes the following special
* components that will be replaced at runtime:
* <ul>
* <li> "/" the local pathname separator </li>
* <li> "%t" the system temporary directory </li>
* <li> "%h" the value of the "user.home" system property </li>
* <li> "%g" the generation number to distinguish rotated logs </li>
* <li> "%u" a unique number to resolve conflicts </li>
* <li> "%%" translates to a single percent sign "%" </li>
* </ul>
* If no "%g" field has been specified and the file count is greater
* than one, then the generation number will be added to the end of
* the generated filename, after a dot.
* <p>
* Thus for example a pattern of "%t/java%g.log" with a count of 2
* would typically cause log files to be written on Solaris to
* /var/tmp/java0.log and /var/tmp/java1.log whereas on Windows 95 they
* would be typically written to C:\TEMP\java0.log and C:\TEMP\java1.log
* <p>
* Generation numbers follow the sequence 0, 1, 2, etc.
* <p>
* Normally the "%u" unique field is set to 0. However, if the <tt>FileHandler</tt>
* tries to open the filename and finds the file is currently in use by
* another process it will increment the unique number field and try
* again. This will be repeated until <tt>FileHandler</tt> finds a file name that
* is not currently in use. If there is a conflict and no "%u" field has
* been specified, it will be added at the end of the filename after a dot.
* (This will be after any automatically added generation number.)
* <p>
* Thus if three processes were all trying to log to fred%u.%g.txt then
* they might end up using fred0.0.txt, fred1.0.txt, fred2.0.txt as
* the first file in their rotating sequences.
* <p>
* Note that the use of unique ids to avoid conflicts is only guaranteed
* to work reliably when using a local disk file system.
*
* @since 1.4
*/
public class FileHandler extends StreamHandler {
private MeteredStream meter;
private boolean append;
private int limit; // zero => no limit.
private int count;
private String pattern;
private String lockFileName;
private FileChannel lockFileChannel;
private File files[];
private static final int DEFAULT_MAX_LOCKS = 100;
private static int maxLocks;
private static final Set<String> locks = new HashSet<>();
/*
* Initialize maxLocks from the System property if set.
* If invalid/no property is provided 100 will be used as a default value.
*/
static {
maxLocks = java.security.AccessController.doPrivileged(
(PrivilegedAction<Integer>) () ->
Integer.getInteger(
"jdk.internal.FileHandlerLogging.maxLocks",
DEFAULT_MAX_LOCKS)
);
if (maxLocks <= 0) {
maxLocks = DEFAULT_MAX_LOCKS;
}
}
/**
* A metered stream is a subclass of OutputStream that
* (a) forwards all its output to a target stream
* (b) keeps track of how many bytes have been written
*/
private class MeteredStream extends OutputStream {
final OutputStream out;
int written;
MeteredStream(OutputStream out, int written) {
this.out = out;
this.written = written;
}
@Override
public void write(int b) throws IOException {
out.write(b);
written++;
}
@Override
public void write(byte buff[]) throws IOException {
out.write(buff);
written += buff.length;
}
@Override
public void write(byte buff[], int off, int len) throws IOException {
out.write(buff,off,len);
written += len;
}
@Override
public void flush() throws IOException {
out.flush();
}
@Override
public void close() throws IOException {
out.close();
}
}
private void open(File fname, boolean append) throws IOException {
int len = 0;
if (append) {
len = (int)fname.length();
}
FileOutputStream fout = new FileOutputStream(fname.toString(), append);
BufferedOutputStream bout = new BufferedOutputStream(fout);
meter = new MeteredStream(bout, len);
setOutputStream(meter);
}
/**
* Configure a FileHandler from LogManager properties and/or default values
* as specified in the class javadoc.
*/
private void configure() {
LogManager manager = LogManager.getLogManager();
String cname = getClass().getName();
pattern = manager.getStringProperty(cname + ".pattern", "%h/java%u.log");
limit = manager.getIntProperty(cname + ".limit", 0);
if (limit < 0) {
limit = 0;
}
count = manager.getIntProperty(cname + ".count", 1);
if (count <= 0) {
count = 1;
}
append = manager.getBooleanProperty(cname + ".append", false);
setLevel(manager.getLevelProperty(cname + ".level", Level.ALL));
setFilter(manager.getFilterProperty(cname + ".filter", null));
setFormatter(manager.getFormatterProperty(cname + ".formatter", new XMLFormatter()));
try {
setEncoding(manager.getStringProperty(cname +".encoding", null));
} catch (Exception ex) {
try {
setEncoding(null);
} catch (Exception ex2) {
// doing a setEncoding with null should always work.
// assert false;
}
}
}
/**
* Construct a default <tt>FileHandler</tt>. This will be configured
* entirely from <tt>LogManager</tt> properties (or their default values).
* <p>
* @exception IOException if there are IO problems opening the files.
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control"))</tt>.
* @exception NullPointerException if pattern property is an empty String.
*/
public FileHandler() throws IOException, SecurityException {
checkPermission();
configure();
openFiles();
}
/**
* Initialize a <tt>FileHandler</tt> to write to the given filename.
* <p>
* The <tt>FileHandler</tt> is configured based on <tt>LogManager</tt>
* properties (or their default values) except that the given pattern
* argument is used as the filename pattern, the file limit is
* set to no limit, and the file count is set to one.
* <p>
* There is no limit on the amount of data that may be written,
* so use this with care.
*
* @param pattern the name of the output file
* @exception IOException if there are IO problems opening the files.
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
* @exception IllegalArgumentException if pattern is an empty string
*/
public FileHandler(String pattern) throws IOException, SecurityException {
if (pattern.length() < 1 ) {
throw new IllegalArgumentException();
}
checkPermission();
configure();
this.pattern = pattern;
this.limit = 0;
this.count = 1;
openFiles();
}
/**
* Initialize a <tt>FileHandler</tt> to write to the given filename,
* with optional append.
* <p>
* The <tt>FileHandler</tt> is configured based on <tt>LogManager</tt>
* properties (or their default values) except that the given pattern
* argument is used as the filename pattern, the file limit is
* set to no limit, the file count is set to one, and the append
* mode is set to the given <tt>append</tt> argument.
* <p>
* There is no limit on the amount of data that may be written,
* so use this with care.
*
* @param pattern the name of the output file
* @param append specifies append mode
* @exception IOException if there are IO problems opening the files.
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
* @exception IllegalArgumentException if pattern is an empty string
*/
public FileHandler(String pattern, boolean append) throws IOException,
SecurityException {
if (pattern.length() < 1 ) {
throw new IllegalArgumentException();
}
checkPermission();
configure();
this.pattern = pattern;
this.limit = 0;
this.count = 1;
this.append = append;
openFiles();
}
/**
* Initialize a <tt>FileHandler</tt> to write to a set of files. When
* (approximately) the given limit has been written to one file,
* another file will be opened. The output will cycle through a set
* of count files.
* <p>
* The <tt>FileHandler</tt> is configured based on <tt>LogManager</tt>
* properties (or their default values) except that the given pattern
* argument is used as the filename pattern, the file limit is
* set to the limit argument, and the file count is set to the
* given count argument.
* <p>
* The count must be at least 1.
*
* @param pattern the pattern for naming the output file
* @param limit the maximum number of bytes to write to any one file
* @param count the number of files to use
* @exception IOException if there are IO problems opening the files.
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
* @exception IllegalArgumentException if {@code limit < 0}, or {@code count < 1}.
* @exception IllegalArgumentException if pattern is an empty string
*/
public FileHandler(String pattern, int limit, int count)
throws IOException, SecurityException {
if (limit < 0 || count < 1 || pattern.length() < 1) {
throw new IllegalArgumentException();
}
checkPermission();
configure();
this.pattern = pattern;
this.limit = limit;
this.count = count;
openFiles();
}
/**
* Initialize a <tt>FileHandler</tt> to write to a set of files
* with optional append. When (approximately) the given limit has
* been written to one file, another file will be opened. The
* output will cycle through a set of count files.
* <p>
* The <tt>FileHandler</tt> is configured based on <tt>LogManager</tt>
* properties (or their default values) except that the given pattern
* argument is used as the filename pattern, the file limit is
* set to the limit argument, and the file count is set to the
* given count argument, and the append mode is set to the given
* <tt>append</tt> argument.
* <p>
* The count must be at least 1.
*
* @param pattern the pattern for naming the output file
* @param limit the maximum number of bytes to write to any one file
* @param count the number of files to use
* @param append specifies append mode
* @exception IOException if there are IO problems opening the files.
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
* @exception IllegalArgumentException if {@code limit < 0}, or {@code count < 1}.
* @exception IllegalArgumentException if pattern is an empty string
*
*/
public FileHandler(String pattern, int limit, int count, boolean append)
throws IOException, SecurityException {
if (limit < 0 || count < 1 || pattern.length() < 1) {
throw new IllegalArgumentException();
}
checkPermission();
configure();
this.pattern = pattern;
this.limit = limit;
this.count = count;
this.append = append;
openFiles();
}
private boolean isParentWritable(Path path) {
Path parent = path.getParent();
if (parent == null) {
parent = path.toAbsolutePath().getParent();
}
return parent != null && Files.isWritable(parent);
}
/**
* Open the set of output files, based on the configured
* instance variables.
*/
private void openFiles() throws IOException {
LogManager manager = LogManager.getLogManager();
manager.checkPermission();
if (count < 1) {
throw new IllegalArgumentException("file count = " + count);
}
if (limit < 0) {
limit = 0;
}
// We register our own ErrorManager during initialization
// so we can record exceptions.
InitializationErrorManager em = new InitializationErrorManager();
setErrorManager(em);
// Create a lock file. This grants us exclusive access
// to our set of output files, as long as we are alive.
int unique = -1;
for (;;) {
unique++;
if (unique > maxLocks) {
throw new IOException("Couldn't get lock for " + pattern
+ ", maxLocks: " + maxLocks);
}
// Generate a lock file name from the "unique" int.
lockFileName = generate(pattern, 0, unique).toString() + ".lck";
// Now try to lock that filename.
// Because some systems (e.g., Solaris) can only do file locks
// between processes (and not within a process), we first check
// if we ourself already have the file locked.
synchronized(locks) {
if (locks.contains(lockFileName)) {
// We already own this lock, for a different FileHandler
// object. Try again.
continue;
}
final Path lockFilePath = Paths.get(lockFileName);
FileChannel channel = null;
int retries = -1;
boolean fileCreated = false;
while (channel == null && retries++ < 1) {
try {
channel = FileChannel.open(lockFilePath,
CREATE_NEW, WRITE);
fileCreated = true;
} catch (AccessDeniedException ade) {
// This can be either a temporary, or a more permanent issue.
// The lock file might be still pending deletion from a previous run
// (temporary), or the parent directory might not be accessible,
// not writable, etc..
// If we can write to the current directory, and this is a regular file,
// let's try again.
if (Files.isRegularFile(lockFilePath, LinkOption.NOFOLLOW_LINKS)
&& isParentWritable(lockFilePath)) {
// Try again. If it doesn't work, then this will
// eventually ensure that we increment "unique" and
// use another file name.
continue;
} else {
throw ade; // no need to retry
}
} catch (FileAlreadyExistsException ix) {
// This may be a zombie file left over by a previous
// execution. Reuse it - but only if we can actually
// write to its directory.
// Note that this is a situation that may happen,
// but not too frequently.
if (Files.isRegularFile(lockFilePath, LinkOption.NOFOLLOW_LINKS)
&& isParentWritable(lockFilePath)) {
try {
channel = FileChannel.open(lockFilePath,
WRITE, APPEND);
} catch (NoSuchFileException x) {
// Race condition - retry once, and if that
// fails again just try the next name in
// the sequence.
continue;
} catch(IOException x) {
// the file may not be writable for us.
// try the next name in the sequence
break;
}
} else {
// at this point channel should still be null.
// break and try the next name in the sequence.
break;
}
}
}
if (channel == null) continue; // try the next name;
lockFileChannel = channel;
boolean available;
try {
available = lockFileChannel.tryLock() != null;
// We got the lock OK.
// At this point we could call File.deleteOnExit().
// However, this could have undesirable side effects
// as indicated by JDK-4872014. So we will instead
// rely on the fact that close() will remove the lock
// file and that whoever is creating FileHandlers should
// be responsible for closing them.
} catch (IOException ix) {
// We got an IOException while trying to get the lock.
// This normally indicates that locking is not supported
// on the target directory. We have to proceed without
// getting a lock. Drop through, but only if we did
// create the file...
available = fileCreated;
} catch (OverlappingFileLockException x) {
// someone already locked this file in this VM, through
// some other channel - that is - using something else
// than new FileHandler(...);
// continue searching for an available lock.
available = false;
}
if (available) {
// We got the lock. Remember it.
locks.add(lockFileName);
break;
}
// We failed to get the lock. Try next file.
lockFileChannel.close();
}
}
files = new File[count];
for (int i = 0; i < count; i++) {
files[i] = generate(pattern, i, unique);
}
// Create the initial log file.
if (append) {
open(files[0], true);
} else {
rotate();
}
// Did we detect any exceptions during initialization?
Exception ex = em.lastException;
if (ex != null) {
if (ex instanceof IOException) {
throw (IOException) ex;
} else if (ex instanceof SecurityException) {
throw (SecurityException) ex;
} else {
throw new IOException("Exception: " + ex);
}
}
// Install the normal default ErrorManager.
setErrorManager(new ErrorManager());
}
/**
* Generate a file based on a user-supplied pattern, generation number,
* and an integer uniqueness suffix
* @param pattern the pattern for naming the output file
* @param generation the generation number to distinguish rotated logs
* @param unique a unique number to resolve conflicts
* @return the generated File
* @throws IOException
*/
private File generate(String pattern, int generation, int unique)
throws IOException {
File file = null;
String word = "";
int ix = 0;
boolean sawg = false;
boolean sawu = false;
while (ix < pattern.length()) {
char ch = pattern.charAt(ix);
ix++;
char ch2 = 0;
if (ix < pattern.length()) {
ch2 = Character.toLowerCase(pattern.charAt(ix));
}
if (ch == '/') {
if (file == null) {
file = new File(word);
} else {
file = new File(file, word);
}
word = "";
continue;
} else if (ch == '%') {
if (ch2 == 't') {
String tmpDir = System.getProperty("java.io.tmpdir");
if (tmpDir == null) {
tmpDir = System.getProperty("user.home");
}
file = new File(tmpDir);
ix++;
word = "";
continue;
} else if (ch2 == 'h') {
file = new File(System.getProperty("user.home"));
if (isSetUID()) {
// Ok, we are in a set UID program. For safety's sake
// we disallow attempts to open files relative to %h.
throw new IOException("can't use %h in set UID program");
}
ix++;
word = "";
continue;
} else if (ch2 == 'g') {
word = word + generation;
sawg = true;
ix++;
continue;
} else if (ch2 == 'u') {
word = word + unique;
sawu = true;
ix++;
continue;
} else if (ch2 == '%') {
word = word + "%";
ix++;
continue;
}
}
word = word + ch;
}
if (count > 1 && !sawg) {
word = word + "." + generation;
}
if (unique > 0 && !sawu) {
word = word + "." + unique;
}
if (word.length() > 0) {
if (file == null) {
file = new File(word);
} else {
file = new File(file, word);
}
}
return file;
}
/**
* Rotate the set of output files
*/
private synchronized void rotate() {
Level oldLevel = getLevel();
setLevel(Level.OFF);
super.close();
for (int i = count-2; i >= 0; i--) {
File f1 = files[i];
File f2 = files[i+1];
if (f1.exists()) {
if (f2.exists()) {
f2.delete();
}
f1.renameTo(f2);
}
}
try {
open(files[0], false);
} catch (IOException ix) {
// We don't want to throw an exception here, but we
// report the exception to any registered ErrorManager.
reportError(null, ix, ErrorManager.OPEN_FAILURE);
}
setLevel(oldLevel);
}
/**
* Format and publish a <tt>LogRecord</tt>.
*
* @param record description of the log event. A null record is
* silently ignored and is not published
*/
@Override
public synchronized void publish(LogRecord record) {
if (!isLoggable(record)) {
return;
}
super.publish(record);
flush();
if (limit > 0 && meter.written >= limit) {
// We performed access checks in the "init" method to make sure
// we are only initialized from trusted code. So we assume
// it is OK to write the target files, even if we are
// currently being called from untrusted code.
// So it is safe to raise privilege here.
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
rotate();
return null;
}
});
}
}
/**
* Close all the files.
*
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
@Override
public synchronized void close() throws SecurityException {
super.close();
// Unlock any lock file.
if (lockFileName == null) {
return;
}
try {
// Close the lock file channel (which also will free any locks)
lockFileChannel.close();
} catch (Exception ex) {
// Problems closing the stream. Punt.
}
synchronized(locks) {
locks.remove(lockFileName);
}
new File(lockFileName).delete();
lockFileName = null;
lockFileChannel = null;
}
private static class InitializationErrorManager extends ErrorManager {
Exception lastException;
@Override
public void error(String msg, Exception ex, int code) {
lastException = ex;
}
}
/**
* check if we are in a set UID program.
*/
private static native boolean isSetUID();
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.logging;
/**
* A Filter can be used to provide fine grain control over
* what is logged, beyond the control provided by log levels.
* <p>
* Each Logger and each Handler can have a filter associated with it.
* The Logger or Handler will call the isLoggable method to check
* if a given LogRecord should be published. If isLoggable returns
* false, the LogRecord will be discarded.
*
* @since 1.4
*/
@FunctionalInterface
public interface Filter {
/**
* Check if a given log record should be published.
* @param record a LogRecord
* @return true if the log record should be published.
*/
public boolean isLoggable(LogRecord record);
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.logging;
/**
* A Formatter provides support for formatting LogRecords.
* <p>
* Typically each logging Handler will have a Formatter associated
* with it. The Formatter takes a LogRecord and converts it to
* a string.
* <p>
* Some formatters (such as the XMLFormatter) need to wrap head
* and tail strings around a set of formatted records. The getHeader
* and getTail methods can be used to obtain these strings.
*
* @since 1.4
*/
public abstract class Formatter {
/**
* Construct a new formatter.
*/
protected Formatter() {
}
/**
* Format the given log record and return the formatted string.
* <p>
* The resulting formatted String will normally include a
* localized and formatted version of the LogRecord's message field.
* It is recommended to use the {@link Formatter#formatMessage}
* convenience method to localize and format the message field.
*
* @param record the log record to be formatted.
* @return the formatted log record
*/
public abstract String format(LogRecord record);
/**
* Return the header string for a set of formatted records.
* <p>
* This base class returns an empty string, but this may be
* overridden by subclasses.
*
* @param h The target handler (can be null)
* @return header string
*/
public String getHead(Handler h) {
return "";
}
/**
* Return the tail string for a set of formatted records.
* <p>
* This base class returns an empty string, but this may be
* overridden by subclasses.
*
* @param h The target handler (can be null)
* @return tail string
*/
public String getTail(Handler h) {
return "";
}
/**
* Localize and format the message string from a log record. This
* method is provided as a convenience for Formatter subclasses to
* use when they are performing formatting.
* <p>
* The message string is first localized to a format string using
* the record's ResourceBundle. (If there is no ResourceBundle,
* or if the message key is not found, then the key is used as the
* format string.) The format String uses java.text style
* formatting.
* <ul>
* <li>If there are no parameters, no formatter is used.
* <li>Otherwise, if the string contains "{0" then
* java.text.MessageFormat is used to format the string.
* <li>Otherwise no formatting is performed.
* </ul>
* <p>
*
* @param record the log record containing the raw message
* @return a localized and formatted message
*/
public synchronized String formatMessage(LogRecord record) {
String format = record.getMessage();
java.util.ResourceBundle catalog = record.getResourceBundle();
if (catalog != null) {
try {
format = catalog.getString(record.getMessage());
} catch (java.util.MissingResourceException ex) {
// Drop through. Use record message as format
format = record.getMessage();
}
}
// Do the formatting.
try {
Object parameters[] = record.getParameters();
if (parameters == null || parameters.length == 0) {
// No parameters. Just return format string.
return format;
}
// Is it a java.text style format?
// Ideally we could match with
// Pattern.compile("\\{\\d").matcher(format).find())
// However the cost is 14% higher, so we cheaply check for
// 1 of the first 4 parameters
if (format.indexOf("{0") >= 0 || format.indexOf("{1") >=0 ||
format.indexOf("{2") >=0|| format.indexOf("{3") >=0) {
return java.text.MessageFormat.format(format, parameters);
}
return format;
} catch (Exception ex) {
// Formatting failed: use localized format string.
return format;
}
}
}

View File

@@ -0,0 +1,313 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.logging;
import java.io.UnsupportedEncodingException;
/**
* A <tt>Handler</tt> object takes log messages from a <tt>Logger</tt> and
* exports them. It might for example, write them to a console
* or write them to a file, or send them to a network logging service,
* or forward them to an OS log, or whatever.
* <p>
* A <tt>Handler</tt> can be disabled by doing a <tt>setLevel(Level.OFF)</tt>
* and can be re-enabled by doing a <tt>setLevel</tt> with an appropriate level.
* <p>
* <tt>Handler</tt> classes typically use <tt>LogManager</tt> properties to set
* default values for the <tt>Handler</tt>'s <tt>Filter</tt>, <tt>Formatter</tt>,
* and <tt>Level</tt>. See the specific documentation for each concrete
* <tt>Handler</tt> class.
*
*
* @since 1.4
*/
public abstract class Handler {
private static final int offValue = Level.OFF.intValue();
private final LogManager manager = LogManager.getLogManager();
// We're using volatile here to avoid synchronizing getters, which
// would prevent other threads from calling isLoggable()
// while publish() is executing.
// On the other hand, setters will be synchronized to exclude concurrent
// execution with more complex methods, such as StreamHandler.publish().
// We wouldn't want 'level' to be changed by another thread in the middle
// of the execution of a 'publish' call.
private volatile Filter filter;
private volatile Formatter formatter;
private volatile Level logLevel = Level.ALL;
private volatile ErrorManager errorManager = new ErrorManager();
private volatile String encoding;
// Package private support for security checking. When sealed
// is true, we access check updates to the class.
boolean sealed = true;
/**
* Default constructor. The resulting <tt>Handler</tt> has a log
* level of <tt>Level.ALL</tt>, no <tt>Formatter</tt>, and no
* <tt>Filter</tt>. A default <tt>ErrorManager</tt> instance is installed
* as the <tt>ErrorManager</tt>.
*/
protected Handler() {
}
/**
* Publish a <tt>LogRecord</tt>.
* <p>
* The logging request was made initially to a <tt>Logger</tt> object,
* which initialized the <tt>LogRecord</tt> and forwarded it here.
* <p>
* The <tt>Handler</tt> is responsible for formatting the message, when and
* if necessary. The formatting should include localization.
*
* @param record description of the log event. A null record is
* silently ignored and is not published
*/
public abstract void publish(LogRecord record);
/**
* Flush any buffered output.
*/
public abstract void flush();
/**
* Close the <tt>Handler</tt> and free all associated resources.
* <p>
* The close method will perform a <tt>flush</tt> and then close the
* <tt>Handler</tt>. After close has been called this <tt>Handler</tt>
* should no longer be used. Method calls may either be silently
* ignored or may throw runtime exceptions.
*
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
public abstract void close() throws SecurityException;
/**
* Set a <tt>Formatter</tt>. This <tt>Formatter</tt> will be used
* to format <tt>LogRecords</tt> for this <tt>Handler</tt>.
* <p>
* Some <tt>Handlers</tt> may not use <tt>Formatters</tt>, in
* which case the <tt>Formatter</tt> will be remembered, but not used.
* <p>
* @param newFormatter the <tt>Formatter</tt> to use (may not be null)
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
public synchronized void setFormatter(Formatter newFormatter) throws SecurityException {
checkPermission();
// Check for a null pointer:
newFormatter.getClass();
formatter = newFormatter;
}
/**
* Return the <tt>Formatter</tt> for this <tt>Handler</tt>.
* @return the <tt>Formatter</tt> (may be null).
*/
public Formatter getFormatter() {
return formatter;
}
/**
* Set the character encoding used by this <tt>Handler</tt>.
* <p>
* The encoding should be set before any <tt>LogRecords</tt> are written
* to the <tt>Handler</tt>.
*
* @param encoding The name of a supported character encoding.
* May be null, to indicate the default platform encoding.
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
* @exception UnsupportedEncodingException if the named encoding is
* not supported.
*/
public synchronized void setEncoding(String encoding)
throws SecurityException, java.io.UnsupportedEncodingException {
checkPermission();
if (encoding != null) {
try {
if(!java.nio.charset.Charset.isSupported(encoding)) {
throw new UnsupportedEncodingException(encoding);
}
} catch (java.nio.charset.IllegalCharsetNameException e) {
throw new UnsupportedEncodingException(encoding);
}
}
this.encoding = encoding;
}
/**
* Return the character encoding for this <tt>Handler</tt>.
*
* @return The encoding name. May be null, which indicates the
* default encoding should be used.
*/
public String getEncoding() {
return encoding;
}
/**
* Set a <tt>Filter</tt> to control output on this <tt>Handler</tt>.
* <P>
* For each call of <tt>publish</tt> the <tt>Handler</tt> will call
* this <tt>Filter</tt> (if it is non-null) to check if the
* <tt>LogRecord</tt> should be published or discarded.
*
* @param newFilter a <tt>Filter</tt> object (may be null)
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
public synchronized void setFilter(Filter newFilter) throws SecurityException {
checkPermission();
filter = newFilter;
}
/**
* Get the current <tt>Filter</tt> for this <tt>Handler</tt>.
*
* @return a <tt>Filter</tt> object (may be null)
*/
public Filter getFilter() {
return filter;
}
/**
* Define an ErrorManager for this Handler.
* <p>
* The ErrorManager's "error" method will be invoked if any
* errors occur while using this Handler.
*
* @param em the new ErrorManager
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
public synchronized void setErrorManager(ErrorManager em) {
checkPermission();
if (em == null) {
throw new NullPointerException();
}
errorManager = em;
}
/**
* Retrieves the ErrorManager for this Handler.
*
* @return the ErrorManager for this Handler
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
public ErrorManager getErrorManager() {
checkPermission();
return errorManager;
}
/**
* Protected convenience method to report an error to this Handler's
* ErrorManager. Note that this method retrieves and uses the ErrorManager
* without doing a security check. It can therefore be used in
* environments where the caller may be non-privileged.
*
* @param msg a descriptive string (may be null)
* @param ex an exception (may be null)
* @param code an error code defined in ErrorManager
*/
protected void reportError(String msg, Exception ex, int code) {
try {
errorManager.error(msg, ex, code);
} catch (Exception ex2) {
System.err.println("Handler.reportError caught:");
ex2.printStackTrace();
}
}
/**
* Set the log level specifying which message levels will be
* logged by this <tt>Handler</tt>. Message levels lower than this
* value will be discarded.
* <p>
* The intention is to allow developers to turn on voluminous
* logging, but to limit the messages that are sent to certain
* <tt>Handlers</tt>.
*
* @param newLevel the new value for the log level
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
public synchronized void setLevel(Level newLevel) throws SecurityException {
if (newLevel == null) {
throw new NullPointerException();
}
checkPermission();
logLevel = newLevel;
}
/**
* Get the log level specifying which messages will be
* logged by this <tt>Handler</tt>. Message levels lower
* than this level will be discarded.
* @return the level of messages being logged.
*/
public Level getLevel() {
return logLevel;
}
/**
* Check if this <tt>Handler</tt> would actually log a given <tt>LogRecord</tt>.
* <p>
* This method checks if the <tt>LogRecord</tt> has an appropriate
* <tt>Level</tt> and whether it satisfies any <tt>Filter</tt>. It also
* may make other <tt>Handler</tt> specific checks that might prevent a
* handler from logging the <tt>LogRecord</tt>. It will return false if
* the <tt>LogRecord</tt> is null.
* <p>
* @param record a <tt>LogRecord</tt>
* @return true if the <tt>LogRecord</tt> would be logged.
*
*/
public boolean isLoggable(LogRecord record) {
final int levelValue = getLevel().intValue();
if (record.getLevel().intValue() < levelValue || levelValue == offValue) {
return false;
}
final Filter filter = getFilter();
if (filter == null) {
return true;
}
return filter.isLoggable(record);
}
// Package-private support method for security checks.
// If "sealed" is true, we check that the caller has
// appropriate security privileges to update Handler
// state and if not throw a SecurityException.
void checkPermission() throws SecurityException {
if (sealed) {
manager.checkPermission();
}
}
}

View File

@@ -0,0 +1,623 @@
/*
* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.logging;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
/**
* The Level class defines a set of standard logging levels that
* can be used to control logging output. The logging Level objects
* are ordered and are specified by ordered integers. Enabling logging
* at a given level also enables logging at all higher levels.
* <p>
* Clients should normally use the predefined Level constants such
* as Level.SEVERE.
* <p>
* The levels in descending order are:
* <ul>
* <li>SEVERE (highest value)
* <li>WARNING
* <li>INFO
* <li>CONFIG
* <li>FINE
* <li>FINER
* <li>FINEST (lowest value)
* </ul>
* In addition there is a level OFF that can be used to turn
* off logging, and a level ALL that can be used to enable
* logging of all messages.
* <p>
* It is possible for third parties to define additional logging
* levels by subclassing Level. In such cases subclasses should
* take care to chose unique integer level values and to ensure that
* they maintain the Object uniqueness property across serialization
* by defining a suitable readResolve method.
*
* @since 1.4
*/
public class Level implements java.io.Serializable {
private static final String defaultBundle = "sun.util.logging.resources.logging";
/**
* @serial The non-localized name of the level.
*/
private final String name;
/**
* @serial The integer value of the level.
*/
private final int value;
/**
* @serial The resource bundle name to be used in localizing the level name.
*/
private final String resourceBundleName;
// localized level name
private transient String localizedLevelName;
private transient Locale cachedLocale;
/**
* OFF is a special level that can be used to turn off logging.
* This level is initialized to <CODE>Integer.MAX_VALUE</CODE>.
*/
public static final Level OFF = new Level("OFF",Integer.MAX_VALUE, defaultBundle);
/**
* SEVERE is a message level indicating a serious failure.
* <p>
* In general SEVERE messages should describe events that are
* of considerable importance and which will prevent normal
* program execution. They should be reasonably intelligible
* to end users and to system administrators.
* This level is initialized to <CODE>1000</CODE>.
*/
public static final Level SEVERE = new Level("SEVERE",1000, defaultBundle);
/**
* WARNING is a message level indicating a potential problem.
* <p>
* In general WARNING messages should describe events that will
* be of interest to end users or system managers, or which
* indicate potential problems.
* This level is initialized to <CODE>900</CODE>.
*/
public static final Level WARNING = new Level("WARNING", 900, defaultBundle);
/**
* INFO is a message level for informational messages.
* <p>
* Typically INFO messages will be written to the console
* or its equivalent. So the INFO level should only be
* used for reasonably significant messages that will
* make sense to end users and system administrators.
* This level is initialized to <CODE>800</CODE>.
*/
public static final Level INFO = new Level("INFO", 800, defaultBundle);
/**
* CONFIG is a message level for static configuration messages.
* <p>
* CONFIG messages are intended to provide a variety of static
* configuration information, to assist in debugging problems
* that may be associated with particular configurations.
* For example, CONFIG message might include the CPU type,
* the graphics depth, the GUI look-and-feel, etc.
* This level is initialized to <CODE>700</CODE>.
*/
public static final Level CONFIG = new Level("CONFIG", 700, defaultBundle);
/**
* FINE is a message level providing tracing information.
* <p>
* All of FINE, FINER, and FINEST are intended for relatively
* detailed tracing. The exact meaning of the three levels will
* vary between subsystems, but in general, FINEST should be used
* for the most voluminous detailed output, FINER for somewhat
* less detailed output, and FINE for the lowest volume (and
* most important) messages.
* <p>
* In general the FINE level should be used for information
* that will be broadly interesting to developers who do not have
* a specialized interest in the specific subsystem.
* <p>
* FINE messages might include things like minor (recoverable)
* failures. Issues indicating potential performance problems
* are also worth logging as FINE.
* This level is initialized to <CODE>500</CODE>.
*/
public static final Level FINE = new Level("FINE", 500, defaultBundle);
/**
* FINER indicates a fairly detailed tracing message.
* By default logging calls for entering, returning, or throwing
* an exception are traced at this level.
* This level is initialized to <CODE>400</CODE>.
*/
public static final Level FINER = new Level("FINER", 400, defaultBundle);
/**
* FINEST indicates a highly detailed tracing message.
* This level is initialized to <CODE>300</CODE>.
*/
public static final Level FINEST = new Level("FINEST", 300, defaultBundle);
/**
* ALL indicates that all messages should be logged.
* This level is initialized to <CODE>Integer.MIN_VALUE</CODE>.
*/
public static final Level ALL = new Level("ALL", Integer.MIN_VALUE, defaultBundle);
/**
* Create a named Level with a given integer value.
* <p>
* Note that this constructor is "protected" to allow subclassing.
* In general clients of logging should use one of the constant Level
* objects such as SEVERE or FINEST. However, if clients need to
* add new logging levels, they may subclass Level and define new
* constants.
* @param name the name of the Level, for example "SEVERE".
* @param value an integer value for the level.
* @throws NullPointerException if the name is null
*/
protected Level(String name, int value) {
this(name, value, null);
}
/**
* Create a named Level with a given integer value and a
* given localization resource name.
* <p>
* @param name the name of the Level, for example "SEVERE".
* @param value an integer value for the level.
* @param resourceBundleName name of a resource bundle to use in
* localizing the given name. If the resourceBundleName is null
* or an empty string, it is ignored.
* @throws NullPointerException if the name is null
*/
protected Level(String name, int value, String resourceBundleName) {
this(name, value, resourceBundleName, true);
}
// private constructor to specify whether this instance should be added
// to the KnownLevel list from which Level.parse method does its look up
private Level(String name, int value, String resourceBundleName, boolean visible) {
if (name == null) {
throw new NullPointerException();
}
this.name = name;
this.value = value;
this.resourceBundleName = resourceBundleName;
this.localizedLevelName = resourceBundleName == null ? name : null;
this.cachedLocale = null;
if (visible) {
KnownLevel.add(this);
}
}
/**
* Return the level's localization resource bundle name, or
* null if no localization bundle is defined.
*
* @return localization resource bundle name
*/
public String getResourceBundleName() {
return resourceBundleName;
}
/**
* Return the non-localized string name of the Level.
*
* @return non-localized name
*/
public String getName() {
return name;
}
/**
* Return the localized string name of the Level, for
* the current default locale.
* <p>
* If no localization information is available, the
* non-localized name is returned.
*
* @return localized name
*/
public String getLocalizedName() {
return getLocalizedLevelName();
}
// package-private getLevelName() is used by the implementation
// instead of getName() to avoid calling the subclass's version
final String getLevelName() {
return this.name;
}
private String computeLocalizedLevelName(Locale newLocale) {
// If this is a custom Level, load resource bundles on the
// classpath and return.
if (!defaultBundle.equals(resourceBundleName)) {
return ResourceBundle.getBundle(resourceBundleName, newLocale,
ClassLoader.getSystemClassLoader()).getString(name);
}
// The default bundle "sun.util.logging.resources.logging" should only
// be loaded from the runtime; so use the extension class loader;
final ResourceBundle rb = ResourceBundle.getBundle(defaultBundle, newLocale);
final String localizedName = rb.getString(name);
// This is a trick to determine whether the name has been translated
// or not. If it has not been translated, we need to use Locale.ROOT
// when calling toUpperCase().
final Locale rbLocale = rb.getLocale();
final Locale locale =
Locale.ROOT.equals(rbLocale)
|| name.equals(localizedName.toUpperCase(Locale.ROOT))
? Locale.ROOT : rbLocale;
// ALL CAPS in a resource bundle's message indicates no translation
// needed per Oracle translation guideline. To workaround this
// in Oracle JDK implementation, convert the localized level name
// to uppercase for compatibility reason.
return Locale.ROOT.equals(locale) ? name : localizedName.toUpperCase(locale);
}
// Avoid looking up the localizedLevelName twice if we already
// have it.
final String getCachedLocalizedLevelName() {
if (localizedLevelName != null) {
if (cachedLocale != null) {
if (cachedLocale.equals(Locale.getDefault())) {
// OK: our cached value was looked up with the same
// locale. We can use it.
return localizedLevelName;
}
}
}
if (resourceBundleName == null) {
// No resource bundle: just use the name.
return name;
}
// We need to compute the localized name.
// Either because it's the first time, or because our cached
// value is for a different locale. Just return null.
return null;
}
final synchronized String getLocalizedLevelName() {
// See if we have a cached localized name
final String cachedLocalizedName = getCachedLocalizedLevelName();
if (cachedLocalizedName != null) {
return cachedLocalizedName;
}
// No cached localized name or cache invalid.
// Need to compute the localized name.
final Locale newLocale = Locale.getDefault();
try {
localizedLevelName = computeLocalizedLevelName(newLocale);
} catch (Exception ex) {
localizedLevelName = name;
}
cachedLocale = newLocale;
return localizedLevelName;
}
// Returns a mirrored Level object that matches the given name as
// specified in the Level.parse method. Returns null if not found.
//
// It returns the same Level object as the one returned by Level.parse
// method if the given name is a non-localized name or integer.
//
// If the name is a localized name, findLevel and parse method may
// return a different level value if there is a custom Level subclass
// that overrides Level.getLocalizedName() to return a different string
// than what's returned by the default implementation.
//
static Level findLevel(String name) {
if (name == null) {
throw new NullPointerException();
}
KnownLevel level;
// Look for a known Level with the given non-localized name.
level = KnownLevel.findByName(name);
if (level != null) {
return level.mirroredLevel;
}
// Now, check if the given name is an integer. If so,
// first look for a Level with the given value and then
// if necessary create one.
try {
int x = Integer.parseInt(name);
level = KnownLevel.findByValue(x);
if (level == null) {
// add new Level
Level levelObject = new Level(name, x);
level = KnownLevel.findByValue(x);
}
return level.mirroredLevel;
} catch (NumberFormatException ex) {
// Not an integer.
// Drop through.
}
level = KnownLevel.findByLocalizedLevelName(name);
if (level != null) {
return level.mirroredLevel;
}
return null;
}
/**
* Returns a string representation of this Level.
*
* @return the non-localized name of the Level, for example "INFO".
*/
@Override
public final String toString() {
return name;
}
/**
* Get the integer value for this level. This integer value
* can be used for efficient ordering comparisons between
* Level objects.
* @return the integer value for this level.
*/
public final int intValue() {
return value;
}
private static final long serialVersionUID = -8176160795706313070L;
// Serialization magic to prevent "doppelgangers".
// This is a performance optimization.
private Object readResolve() {
KnownLevel o = KnownLevel.matches(this);
if (o != null) {
return o.levelObject;
}
// Woops. Whoever sent us this object knows
// about a new log level. Add it to our list.
Level level = new Level(this.name, this.value, this.resourceBundleName);
return level;
}
/**
* Parse a level name string into a Level.
* <p>
* The argument string may consist of either a level name
* or an integer value.
* <p>
* For example:
* <ul>
* <li> "SEVERE"
* <li> "1000"
* </ul>
*
* @param name string to be parsed
* @throws NullPointerException if the name is null
* @throws IllegalArgumentException if the value is not valid.
* Valid values are integers between <CODE>Integer.MIN_VALUE</CODE>
* and <CODE>Integer.MAX_VALUE</CODE>, and all known level names.
* Known names are the levels defined by this class (e.g., <CODE>FINE</CODE>,
* <CODE>FINER</CODE>, <CODE>FINEST</CODE>), or created by this class with
* appropriate package access, or new levels defined or created
* by subclasses.
*
* @return The parsed value. Passing an integer that corresponds to a known name
* (e.g., 700) will return the associated name (e.g., <CODE>CONFIG</CODE>).
* Passing an integer that does not (e.g., 1) will return a new level name
* initialized to that value.
*/
public static synchronized Level parse(String name) throws IllegalArgumentException {
// Check that name is not null.
name.length();
KnownLevel level;
// Look for a known Level with the given non-localized name.
level = KnownLevel.findByName(name);
if (level != null) {
return level.levelObject;
}
// Now, check if the given name is an integer. If so,
// first look for a Level with the given value and then
// if necessary create one.
try {
int x = Integer.parseInt(name);
level = KnownLevel.findByValue(x);
if (level == null) {
// add new Level
Level levelObject = new Level(name, x);
level = KnownLevel.findByValue(x);
}
return level.levelObject;
} catch (NumberFormatException ex) {
// Not an integer.
// Drop through.
}
// Finally, look for a known level with the given localized name,
// in the current default locale.
// This is relatively expensive, but not excessively so.
level = KnownLevel.findByLocalizedLevelName(name);
if (level != null) {
return level.levelObject;
}
// OK, we've tried everything and failed
throw new IllegalArgumentException("Bad level \"" + name + "\"");
}
/**
* Compare two objects for value equality.
* @return true if and only if the two objects have the same level value.
*/
@Override
public boolean equals(Object ox) {
try {
Level lx = (Level)ox;
return (lx.value == this.value);
} catch (Exception ex) {
return false;
}
}
/**
* Generate a hashcode.
* @return a hashcode based on the level value
*/
@Override
public int hashCode() {
return this.value;
}
// KnownLevel class maintains the global list of all known levels.
// The API allows multiple custom Level instances of the same name/value
// be created. This class provides convenient methods to find a level
// by a given name, by a given value, or by a given localized name.
//
// KnownLevel wraps the following Level objects:
// 1. levelObject: standard Level object or custom Level object
// 2. mirroredLevel: Level object representing the level specified in the
// logging configuration.
//
// Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
// are non-final but the name and resource bundle name are parameters to
// the Level constructor. Use the mirroredLevel object instead of the
// levelObject to prevent the logging framework to execute foreign code
// implemented by untrusted Level subclass.
//
// Implementation Notes:
// If Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
// were final, the following KnownLevel implementation can be removed.
// Future API change should take this into consideration.
static final class KnownLevel {
private static Map<String, List<KnownLevel>> nameToLevels = new HashMap<>();
private static Map<Integer, List<KnownLevel>> intToLevels = new HashMap<>();
final Level levelObject; // instance of Level class or Level subclass
final Level mirroredLevel; // mirror of the custom Level
KnownLevel(Level l) {
this.levelObject = l;
if (l.getClass() == Level.class) {
this.mirroredLevel = l;
} else {
// this mirrored level object is hidden
this.mirroredLevel = new Level(l.name, l.value, l.resourceBundleName, false);
}
}
static synchronized void add(Level l) {
// the mirroredLevel object is always added to the list
// before the custom Level instance
KnownLevel o = new KnownLevel(l);
List<KnownLevel> list = nameToLevels.get(l.name);
if (list == null) {
list = new ArrayList<>();
nameToLevels.put(l.name, list);
}
list.add(o);
list = intToLevels.get(l.value);
if (list == null) {
list = new ArrayList<>();
intToLevels.put(l.value, list);
}
list.add(o);
}
// Returns a KnownLevel with the given non-localized name.
static synchronized KnownLevel findByName(String name) {
List<KnownLevel> list = nameToLevels.get(name);
if (list != null) {
return list.get(0);
}
return null;
}
// Returns a KnownLevel with the given value.
static synchronized KnownLevel findByValue(int value) {
List<KnownLevel> list = intToLevels.get(value);
if (list != null) {
return list.get(0);
}
return null;
}
// Returns a KnownLevel with the given localized name matching
// by calling the Level.getLocalizedLevelName() method (i.e. found
// from the resourceBundle associated with the Level object).
// This method does not call Level.getLocalizedName() that may
// be overridden in a subclass implementation
static synchronized KnownLevel findByLocalizedLevelName(String name) {
for (List<KnownLevel> levels : nameToLevels.values()) {
for (KnownLevel l : levels) {
String lname = l.levelObject.getLocalizedLevelName();
if (name.equals(lname)) {
return l;
}
}
}
return null;
}
static synchronized KnownLevel matches(Level l) {
List<KnownLevel> list = nameToLevels.get(l.name);
if (list != null) {
for (KnownLevel level : list) {
Level other = level.mirroredLevel;
Class<? extends Level> type = level.levelObject.getClass();
if (l.value == other.value &&
(l.resourceBundleName == other.resourceBundleName ||
(l.resourceBundleName != null &&
l.resourceBundleName.equals(other.resourceBundleName)))) {
if (type == l.getClass()) {
return level;
}
}
}
}
return null;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,583 @@
/*
* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.logging;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.io.*;
import sun.misc.JavaLangAccess;
import sun.misc.SharedSecrets;
/**
* LogRecord objects are used to pass logging requests between
* the logging framework and individual log Handlers.
* <p>
* When a LogRecord is passed into the logging framework it
* logically belongs to the framework and should no longer be
* used or updated by the client application.
* <p>
* Note that if the client application has not specified an
* explicit source method name and source class name, then the
* LogRecord class will infer them automatically when they are
* first accessed (due to a call on getSourceMethodName or
* getSourceClassName) by analyzing the call stack. Therefore,
* if a logging Handler wants to pass off a LogRecord to another
* thread, or to transmit it over RMI, and if it wishes to subsequently
* obtain method name or class name information it should call
* one of getSourceClassName or getSourceMethodName to force
* the values to be filled in.
* <p>
* <b> Serialization notes:</b>
* <ul>
* <li>The LogRecord class is serializable.
*
* <li> Because objects in the parameters array may not be serializable,
* during serialization all objects in the parameters array are
* written as the corresponding Strings (using Object.toString).
*
* <li> The ResourceBundle is not transmitted as part of the serialized
* form, but the resource bundle name is, and the recipient object's
* readObject method will attempt to locate a suitable resource bundle.
*
* </ul>
*
* @since 1.4
*/
public class LogRecord implements java.io.Serializable {
private static final AtomicLong globalSequenceNumber
= new AtomicLong(0);
/**
* The default value of threadID will be the current thread's
* thread id, for ease of correlation, unless it is greater than
* MIN_SEQUENTIAL_THREAD_ID, in which case we try harder to keep
* our promise to keep threadIDs unique by avoiding collisions due
* to 32-bit wraparound. Unfortunately, LogRecord.getThreadID()
* returns int, while Thread.getId() returns long.
*/
private static final int MIN_SEQUENTIAL_THREAD_ID = Integer.MAX_VALUE / 2;
private static final AtomicInteger nextThreadId
= new AtomicInteger(MIN_SEQUENTIAL_THREAD_ID);
private static final ThreadLocal<Integer> threadIds = new ThreadLocal<>();
/**
* @serial Logging message level
*/
private Level level;
/**
* @serial Sequence number
*/
private long sequenceNumber;
/**
* @serial Class that issued logging call
*/
private String sourceClassName;
/**
* @serial Method that issued logging call
*/
private String sourceMethodName;
/**
* @serial Non-localized raw message text
*/
private String message;
/**
* @serial Thread ID for thread that issued logging call.
*/
private int threadID;
/**
* @serial Event time in milliseconds since 1970
*/
private long millis;
/**
* @serial The Throwable (if any) associated with log message
*/
private Throwable thrown;
/**
* @serial Name of the source Logger.
*/
private String loggerName;
/**
* @serial Resource bundle name to localized log message.
*/
private String resourceBundleName;
private transient boolean needToInferCaller;
private transient Object parameters[];
private transient ResourceBundle resourceBundle;
/**
* Returns the default value for a new LogRecord's threadID.
*/
private int defaultThreadID() {
long tid = Thread.currentThread().getId();
if (tid < MIN_SEQUENTIAL_THREAD_ID) {
return (int) tid;
} else {
Integer id = threadIds.get();
if (id == null) {
id = nextThreadId.getAndIncrement();
threadIds.set(id);
}
return id;
}
}
/**
* Construct a LogRecord with the given level and message values.
* <p>
* The sequence property will be initialized with a new unique value.
* These sequence values are allocated in increasing order within a VM.
* <p>
* The millis property will be initialized to the current time.
* <p>
* The thread ID property will be initialized with a unique ID for
* the current thread.
* <p>
* All other properties will be initialized to "null".
*
* @param level a logging level value
* @param msg the raw non-localized logging message (may be null)
*/
public LogRecord(Level level, String msg) {
// Make sure level isn't null, by calling random method.
level.getClass();
this.level = level;
message = msg;
// Assign a thread ID and a unique sequence number.
sequenceNumber = globalSequenceNumber.getAndIncrement();
threadID = defaultThreadID();
millis = System.currentTimeMillis();
needToInferCaller = true;
}
/**
* Get the source Logger's name.
*
* @return source logger name (may be null)
*/
public String getLoggerName() {
return loggerName;
}
/**
* Set the source Logger's name.
*
* @param name the source logger name (may be null)
*/
public void setLoggerName(String name) {
loggerName = name;
}
/**
* Get the localization resource bundle
* <p>
* This is the ResourceBundle that should be used to localize
* the message string before formatting it. The result may
* be null if the message is not localizable, or if no suitable
* ResourceBundle is available.
* @return the localization resource bundle
*/
public ResourceBundle getResourceBundle() {
return resourceBundle;
}
/**
* Set the localization resource bundle.
*
* @param bundle localization bundle (may be null)
*/
public void setResourceBundle(ResourceBundle bundle) {
resourceBundle = bundle;
}
/**
* Get the localization resource bundle name
* <p>
* This is the name for the ResourceBundle that should be
* used to localize the message string before formatting it.
* The result may be null if the message is not localizable.
* @return the localization resource bundle name
*/
public String getResourceBundleName() {
return resourceBundleName;
}
/**
* Set the localization resource bundle name.
*
* @param name localization bundle name (may be null)
*/
public void setResourceBundleName(String name) {
resourceBundleName = name;
}
/**
* Get the logging message level, for example Level.SEVERE.
* @return the logging message level
*/
public Level getLevel() {
return level;
}
/**
* Set the logging message level, for example Level.SEVERE.
* @param level the logging message level
*/
public void setLevel(Level level) {
if (level == null) {
throw new NullPointerException();
}
this.level = level;
}
/**
* Get the sequence number.
* <p>
* Sequence numbers are normally assigned in the LogRecord
* constructor, which assigns unique sequence numbers to
* each new LogRecord in increasing order.
* @return the sequence number
*/
public long getSequenceNumber() {
return sequenceNumber;
}
/**
* Set the sequence number.
* <p>
* Sequence numbers are normally assigned in the LogRecord constructor,
* so it should not normally be necessary to use this method.
* @param seq the sequence number
*/
public void setSequenceNumber(long seq) {
sequenceNumber = seq;
}
/**
* Get the name of the class that (allegedly) issued the logging request.
* <p>
* Note that this sourceClassName is not verified and may be spoofed.
* This information may either have been provided as part of the
* logging call, or it may have been inferred automatically by the
* logging framework. In the latter case, the information may only
* be approximate and may in fact describe an earlier call on the
* stack frame.
* <p>
* May be null if no information could be obtained.
*
* @return the source class name
*/
public String getSourceClassName() {
if (needToInferCaller) {
inferCaller();
}
return sourceClassName;
}
/**
* Set the name of the class that (allegedly) issued the logging request.
*
* @param sourceClassName the source class name (may be null)
*/
public void setSourceClassName(String sourceClassName) {
this.sourceClassName = sourceClassName;
needToInferCaller = false;
}
/**
* Get the name of the method that (allegedly) issued the logging request.
* <p>
* Note that this sourceMethodName is not verified and may be spoofed.
* This information may either have been provided as part of the
* logging call, or it may have been inferred automatically by the
* logging framework. In the latter case, the information may only
* be approximate and may in fact describe an earlier call on the
* stack frame.
* <p>
* May be null if no information could be obtained.
*
* @return the source method name
*/
public String getSourceMethodName() {
if (needToInferCaller) {
inferCaller();
}
return sourceMethodName;
}
/**
* Set the name of the method that (allegedly) issued the logging request.
*
* @param sourceMethodName the source method name (may be null)
*/
public void setSourceMethodName(String sourceMethodName) {
this.sourceMethodName = sourceMethodName;
needToInferCaller = false;
}
/**
* Get the "raw" log message, before localization or formatting.
* <p>
* May be null, which is equivalent to the empty string "".
* <p>
* This message may be either the final text or a localization key.
* <p>
* During formatting, if the source logger has a localization
* ResourceBundle and if that ResourceBundle has an entry for
* this message string, then the message string is replaced
* with the localized value.
*
* @return the raw message string
*/
public String getMessage() {
return message;
}
/**
* Set the "raw" log message, before localization or formatting.
*
* @param message the raw message string (may be null)
*/
public void setMessage(String message) {
this.message = message;
}
/**
* Get the parameters to the log message.
*
* @return the log message parameters. May be null if
* there are no parameters.
*/
public Object[] getParameters() {
return parameters;
}
/**
* Set the parameters to the log message.
*
* @param parameters the log message parameters. (may be null)
*/
public void setParameters(Object parameters[]) {
this.parameters = parameters;
}
/**
* Get an identifier for the thread where the message originated.
* <p>
* This is a thread identifier within the Java VM and may or
* may not map to any operating system ID.
*
* @return thread ID
*/
public int getThreadID() {
return threadID;
}
/**
* Set an identifier for the thread where the message originated.
* @param threadID the thread ID
*/
public void setThreadID(int threadID) {
this.threadID = threadID;
}
/**
* Get event time in milliseconds since 1970.
*
* @return event time in millis since 1970
*/
public long getMillis() {
return millis;
}
/**
* Set event time.
*
* @param millis event time in millis since 1970
*/
public void setMillis(long millis) {
this.millis = millis;
}
/**
* Get any throwable associated with the log record.
* <p>
* If the event involved an exception, this will be the
* exception object. Otherwise null.
*
* @return a throwable
*/
public Throwable getThrown() {
return thrown;
}
/**
* Set a throwable associated with the log event.
*
* @param thrown a throwable (may be null)
*/
public void setThrown(Throwable thrown) {
this.thrown = thrown;
}
private static final long serialVersionUID = 5372048053134512534L;
/**
* @serialData Default fields, followed by a two byte version number
* (major byte, followed by minor byte), followed by information on
* the log record parameter array. If there is no parameter array,
* then -1 is written. If there is a parameter array (possible of zero
* length) then the array length is written as an integer, followed
* by String values for each parameter. If a parameter is null, then
* a null String is written. Otherwise the output of Object.toString()
* is written.
*/
private void writeObject(ObjectOutputStream out) throws IOException {
// We have to call defaultWriteObject first.
out.defaultWriteObject();
// Write our version number.
out.writeByte(1);
out.writeByte(0);
if (parameters == null) {
out.writeInt(-1);
return;
}
out.writeInt(parameters.length);
// Write string values for the parameters.
for (int i = 0; i < parameters.length; i++) {
if (parameters[i] == null) {
out.writeObject(null);
} else {
out.writeObject(parameters[i].toString());
}
}
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
// We have to call defaultReadObject first.
in.defaultReadObject();
// Read version number.
byte major = in.readByte();
byte minor = in.readByte();
if (major != 1) {
throw new IOException("LogRecord: bad version: " + major + "." + minor);
}
int len = in.readInt();
if (len < -1) {
throw new NegativeArraySizeException();
} else if (len == -1) {
parameters = null;
} else if (len < 255) {
parameters = new Object[len];
for (int i = 0; i < parameters.length; i++) {
parameters[i] = in.readObject();
}
} else {
List<Object> params = new ArrayList<>(Math.min(len, 1024));
for (int i = 0; i < len; i++) {
params.add(in.readObject());
}
parameters = params.toArray(new Object[params.size()]);
}
// If necessary, try to regenerate the resource bundle.
if (resourceBundleName != null) {
try {
// use system class loader to ensure the ResourceBundle
// instance is a different instance than null loader uses
final ResourceBundle bundle =
ResourceBundle.getBundle(resourceBundleName,
Locale.getDefault(),
ClassLoader.getSystemClassLoader());
resourceBundle = bundle;
} catch (MissingResourceException ex) {
// This is not a good place to throw an exception,
// so we simply leave the resourceBundle null.
resourceBundle = null;
}
}
needToInferCaller = false;
}
// Private method to infer the caller's class and method names
private void inferCaller() {
needToInferCaller = false;
JavaLangAccess access = SharedSecrets.getJavaLangAccess();
Throwable throwable = new Throwable();
int depth = access.getStackTraceDepth(throwable);
boolean lookingForLogger = true;
for (int ix = 0; ix < depth; ix++) {
// Calling getStackTraceElement directly prevents the VM
// from paying the cost of building the entire stack frame.
StackTraceElement frame =
access.getStackTraceElement(throwable, ix);
String cname = frame.getClassName();
boolean isLoggerImpl = isLoggerImplFrame(cname);
if (lookingForLogger) {
// Skip all frames until we have found the first logger frame.
if (isLoggerImpl) {
lookingForLogger = false;
}
} else {
if (!isLoggerImpl) {
// skip reflection call
if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) {
// We've found the relevant frame.
setSourceClassName(cname);
setSourceMethodName(frame.getMethodName());
return;
}
}
}
}
// We haven't found a suitable frame, so just punt. This is
// OK as we are only committed to making a "best effort" here.
}
private boolean isLoggerImplFrame(String cname) {
// the log record could be created for a platform logger
return (cname.equals("java.util.logging.Logger") ||
cname.startsWith("java.util.logging.LoggingProxyImpl") ||
cname.startsWith("sun.util.logging."));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,119 @@
/*
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.logging;
import java.util.Enumeration;
import java.util.List;
import java.util.ArrayList;
/**
* Logging is the implementation class of LoggingMXBean.
*
* The <tt>LoggingMXBean</tt> interface provides a standard
* method for management access to the individual
* {@code Logger} objects available at runtime.
*
* @author Ron Mann
* @author Mandy Chung
* @since 1.5
*
* @see javax.management
* @see Logger
* @see LogManager
*/
class Logging implements LoggingMXBean {
private static LogManager logManager = LogManager.getLogManager();
/** Constructor of Logging which is the implementation class
* of LoggingMXBean.
*/
Logging() {
}
public List<String> getLoggerNames() {
Enumeration<String> loggers = logManager.getLoggerNames();
ArrayList<String> array = new ArrayList<>();
for (; loggers.hasMoreElements();) {
array.add(loggers.nextElement());
}
return array;
}
private static String EMPTY_STRING = "";
public String getLoggerLevel(String loggerName) {
Logger l = logManager.getLogger(loggerName);
if (l == null) {
return null;
}
Level level = l.getLevel();
if (level == null) {
return EMPTY_STRING;
} else {
return level.getLevelName();
}
}
public void setLoggerLevel(String loggerName, String levelName) {
if (loggerName == null) {
throw new NullPointerException("loggerName is null");
}
Logger logger = logManager.getLogger(loggerName);
if (logger == null) {
throw new IllegalArgumentException("Logger " + loggerName +
"does not exist");
}
Level level = null;
if (levelName != null) {
// parse will throw IAE if logLevel is invalid
level = Level.findLevel(levelName);
if (level == null) {
throw new IllegalArgumentException("Unknown level \"" + levelName + "\"");
}
}
logger.setLevel(level);
}
public String getParentLoggerName( String loggerName ) {
Logger l = logManager.getLogger( loggerName );
if (l == null) {
return null;
}
Logger p = l.getParent();
if (p == null) {
// root logger
return EMPTY_STRING;
} else {
return p.getName();
}
}
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.logging;
/**
* The management interface for the logging facility. It is recommended
* to use the {@link java.lang.management.PlatformLoggingMXBean} management
* interface that implements all attributes defined in this
* {@code LoggingMXBean}. The
* {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)
* ManagementFactory.getPlatformMXBean} method can be used to obtain
* the {@code PlatformLoggingMXBean} object representing the management
* interface for logging.
*
* <p>There is a single global instance of the <tt>LoggingMXBean</tt>.
* This instance is an {@link javax.management.MXBean MXBean} that
* can be obtained by calling the {@link LogManager#getLoggingMXBean}
* method or from the
* {@linkplain java.lang.management.ManagementFactory#getPlatformMBeanServer
* platform <tt>MBeanServer</tt>}.
* <p>
* The {@link javax.management.ObjectName ObjectName} that uniquely identifies
* the management interface for logging within the {@code MBeanServer} is:
* <pre>
* {@link LogManager#LOGGING_MXBEAN_NAME java.util.logging:type=Logging}
* </pre>
* <p>
* The instance registered in the platform {@code MBeanServer}
* is also a {@link java.lang.management.PlatformLoggingMXBean}.
*
* @author Ron Mann
* @author Mandy Chung
* @since 1.5
*
* @see java.lang.management.PlatformLoggingMXBean
*/
public interface LoggingMXBean {
/**
* Returns the list of currently registered logger names. This method
* calls {@link LogManager#getLoggerNames} and returns a list
* of the logger names.
*
* @return A list of <tt>String</tt> each of which is a
* currently registered <tt>Logger</tt> name.
*/
public java.util.List<String> getLoggerNames();
/**
* Gets the name of the log level associated with the specified logger.
* If the specified logger does not exist, <tt>null</tt>
* is returned.
* This method first finds the logger of the given name and
* then returns the name of the log level by calling:
* <blockquote>
* {@link Logger#getLevel Logger.getLevel()}.{@link Level#getName getName()};
* </blockquote>
*
* <p>
* If the <tt>Level</tt> of the specified logger is <tt>null</tt>,
* which means that this logger's effective level is inherited
* from its parent, an empty string will be returned.
*
* @param loggerName The name of the <tt>Logger</tt> to be retrieved.
*
* @return The name of the log level of the specified logger; or
* an empty string if the log level of the specified logger
* is <tt>null</tt>. If the specified logger does not
* exist, <tt>null</tt> is returned.
*
* @see Logger#getLevel
*/
public String getLoggerLevel(String loggerName);
/**
* Sets the specified logger to the specified new level.
* If the <tt>levelName</tt> is not <tt>null</tt>, the level
* of the specified logger is set to the parsed <tt>Level</tt>
* matching the <tt>levelName</tt>.
* If the <tt>levelName</tt> is <tt>null</tt>, the level
* of the specified logger is set to <tt>null</tt> and
* the effective level of the logger is inherited from
* its nearest ancestor with a specific (non-null) level value.
*
* @param loggerName The name of the <tt>Logger</tt> to be set.
* Must be non-null.
* @param levelName The name of the level to set on the specified logger,
* or <tt>null</tt> if setting the level to inherit
* from its nearest ancestor.
*
* @throws IllegalArgumentException if the specified logger
* does not exist, or <tt>levelName</tt> is not a valid level name.
*
* @throws SecurityException if a security manager exists and if
* the caller does not have LoggingPermission("control").
*
* @see Logger#setLevel
*/
public void setLoggerLevel(String loggerName, String levelName);
/**
* Returns the name of the parent for the specified logger.
* If the specified logger does not exist, <tt>null</tt> is returned.
* If the specified logger is the root <tt>Logger</tt> in the namespace,
* the result will be an empty string.
*
* @param loggerName The name of a <tt>Logger</tt>.
*
* @return the name of the nearest existing parent logger;
* an empty string if the specified logger is the root logger.
* If the specified logger does not exist, <tt>null</tt>
* is returned.
*/
public String getParentLoggerName(String loggerName);
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.logging;
import java.security.*;
/**
* The permission which the SecurityManager will check when code
* that is running with a SecurityManager calls one of the logging
* control methods (such as Logger.setLevel).
* <p>
* Currently there is only one named LoggingPermission. This is "control"
* and it grants the ability to control the logging configuration, for
* example by adding or removing Handlers, by adding or removing Filters,
* or by changing logging levels.
* <p>
* Programmers do not normally create LoggingPermission objects directly.
* Instead they are created by the security policy code based on reading
* the security policy file.
*
*
* @since 1.4
* @see java.security.BasicPermission
* @see java.security.Permission
* @see java.security.Permissions
* @see java.security.PermissionCollection
* @see java.lang.SecurityManager
*
*/
public final class LoggingPermission extends java.security.BasicPermission {
private static final long serialVersionUID = 63564341580231582L;
/**
* Creates a new LoggingPermission object.
*
* @param name Permission name. Must be "control".
* @param actions Must be either null or the empty string.
*
* @throws NullPointerException if <code>name</code> is <code>null</code>.
* @throws IllegalArgumentException if <code>name</code> is empty or if
* arguments are invalid.
*/
public LoggingPermission(String name, String actions) throws IllegalArgumentException {
super(name);
if (!name.equals("control")) {
throw new IllegalArgumentException("name: " + name);
}
if (actions != null && actions.length() > 0) {
throw new IllegalArgumentException("actions: " + actions);
}
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.logging;
import sun.util.logging.LoggingProxy;
/**
* Implementation of LoggingProxy when java.util.logging classes exist.
*/
class LoggingProxyImpl implements LoggingProxy {
static final LoggingProxy INSTANCE = new LoggingProxyImpl();
private LoggingProxyImpl() { }
@Override
public Object getLogger(String name) {
// always create a platform logger with the resource bundle name
return Logger.getPlatformLogger(name);
}
@Override
public Object getLevel(Object logger) {
return ((Logger) logger).getLevel();
}
@Override
public void setLevel(Object logger, Object newLevel) {
((Logger) logger).setLevel((Level) newLevel);
}
@Override
public boolean isLoggable(Object logger, Object level) {
return ((Logger) logger).isLoggable((Level) level);
}
@Override
public void log(Object logger, Object level, String msg) {
((Logger) logger).log((Level) level, msg);
}
@Override
public void log(Object logger, Object level, String msg, Throwable t) {
((Logger) logger).log((Level) level, msg, t);
}
@Override
public void log(Object logger, Object level, String msg, Object... params) {
((Logger) logger).log((Level) level, msg, params);
}
@Override
public java.util.List<String> getLoggerNames() {
return LogManager.getLoggingMXBean().getLoggerNames();
}
@Override
public String getLoggerLevel(String loggerName) {
return LogManager.getLoggingMXBean().getLoggerLevel(loggerName);
}
@Override
public void setLoggerLevel(String loggerName, String levelName) {
LogManager.getLoggingMXBean().setLoggerLevel(loggerName, levelName);
}
@Override
public String getParentLoggerName(String loggerName) {
return LogManager.getLoggingMXBean().getParentLoggerName(loggerName);
}
@Override
public Object parseLevel(String levelName) {
Level level = Level.findLevel(levelName);
if (level == null) {
throw new IllegalArgumentException("Unknown level \"" + levelName + "\"");
}
return level;
}
@Override
public String getLevelName(Object level) {
return ((Level) level).getLevelName();
}
@Override
public int getLevelValue(Object level) {
return ((Level) level).intValue();
}
@Override
public String getProperty(String key) {
return LogManager.getLogManager().getProperty(key);
}
}

View File

@@ -0,0 +1,292 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.logging;
/**
* <tt>Handler</tt> that buffers requests in a circular buffer in memory.
* <p>
* Normally this <tt>Handler</tt> simply stores incoming <tt>LogRecords</tt>
* into its memory buffer and discards earlier records. This buffering
* is very cheap and avoids formatting costs. On certain trigger
* conditions, the <tt>MemoryHandler</tt> will push out its current buffer
* contents to a target <tt>Handler</tt>, which will typically publish
* them to the outside world.
* <p>
* There are three main models for triggering a push of the buffer:
* <ul>
* <li>
* An incoming <tt>LogRecord</tt> has a type that is greater than
* a pre-defined level, the <tt>pushLevel</tt>. </li>
* <li>
* An external class calls the <tt>push</tt> method explicitly. </li>
* <li>
* A subclass overrides the <tt>log</tt> method and scans each incoming
* <tt>LogRecord</tt> and calls <tt>push</tt> if a record matches some
* desired criteria. </li>
* </ul>
* <p>
* <b>Configuration:</b>
* By default each <tt>MemoryHandler</tt> is initialized using the following
* <tt>LogManager</tt> configuration properties where <tt>&lt;handler-name&gt;</tt>
* refers to the fully-qualified class name of the handler.
* If properties are not defined
* (or have invalid values) then the specified default values are used.
* If no default value is defined then a RuntimeException is thrown.
* <ul>
* <li> &lt;handler-name&gt;.level
* specifies the level for the <tt>Handler</tt>
* (defaults to <tt>Level.ALL</tt>). </li>
* <li> &lt;handler-name&gt;.filter
* specifies the name of a <tt>Filter</tt> class to use
* (defaults to no <tt>Filter</tt>). </li>
* <li> &lt;handler-name&gt;.size
* defines the buffer size (defaults to 1000). </li>
* <li> &lt;handler-name&gt;.push
* defines the <tt>pushLevel</tt> (defaults to <tt>level.SEVERE</tt>). </li>
* <li> &lt;handler-name&gt;.target
* specifies the name of the target <tt>Handler </tt> class.
* (no default). </li>
* </ul>
* <p>
* For example, the properties for {@code MemoryHandler} would be:
* <ul>
* <li> java.util.logging.MemoryHandler.level=INFO </li>
* <li> java.util.logging.MemoryHandler.formatter=java.util.logging.SimpleFormatter </li>
* </ul>
* <p>
* For a custom handler, e.g. com.foo.MyHandler, the properties would be:
* <ul>
* <li> com.foo.MyHandler.level=INFO </li>
* <li> com.foo.MyHandler.formatter=java.util.logging.SimpleFormatter </li>
* </ul>
* <p>
* @since 1.4
*/
public class MemoryHandler extends Handler {
private final static int DEFAULT_SIZE = 1000;
private volatile Level pushLevel;
private int size;
private Handler target;
private LogRecord buffer[];
int start, count;
// Private method to configure a MemoryHandler from LogManager
// properties and/or default values as specified in the class
// javadoc.
private void configure() {
LogManager manager = LogManager.getLogManager();
String cname = getClass().getName();
pushLevel = manager.getLevelProperty(cname +".push", Level.SEVERE);
size = manager.getIntProperty(cname + ".size", DEFAULT_SIZE);
if (size <= 0) {
size = DEFAULT_SIZE;
}
setLevel(manager.getLevelProperty(cname +".level", Level.ALL));
setFilter(manager.getFilterProperty(cname +".filter", null));
setFormatter(manager.getFormatterProperty(cname +".formatter", new SimpleFormatter()));
}
/**
* Create a <tt>MemoryHandler</tt> and configure it based on
* <tt>LogManager</tt> configuration properties.
*/
public MemoryHandler() {
sealed = false;
configure();
sealed = true;
LogManager manager = LogManager.getLogManager();
String handlerName = getClass().getName();
String targetName = manager.getProperty(handlerName+".target");
if (targetName == null) {
throw new RuntimeException("The handler " + handlerName
+ " does not specify a target");
}
Class<?> clz;
try {
clz = ClassLoader.getSystemClassLoader().loadClass(targetName);
target = (Handler) clz.newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new RuntimeException("MemoryHandler can't load handler target \"" + targetName + "\"" , e);
}
init();
}
// Initialize. Size is a count of LogRecords.
private void init() {
buffer = new LogRecord[size];
start = 0;
count = 0;
}
/**
* Create a <tt>MemoryHandler</tt>.
* <p>
* The <tt>MemoryHandler</tt> is configured based on <tt>LogManager</tt>
* properties (or their default values) except that the given <tt>pushLevel</tt>
* argument and buffer size argument are used.
*
* @param target the Handler to which to publish output.
* @param size the number of log records to buffer (must be greater than zero)
* @param pushLevel message level to push on
*
* @throws IllegalArgumentException if {@code size is <= 0}
*/
public MemoryHandler(Handler target, int size, Level pushLevel) {
if (target == null || pushLevel == null) {
throw new NullPointerException();
}
if (size <= 0) {
throw new IllegalArgumentException();
}
sealed = false;
configure();
sealed = true;
this.target = target;
this.pushLevel = pushLevel;
this.size = size;
init();
}
/**
* Store a <tt>LogRecord</tt> in an internal buffer.
* <p>
* If there is a <tt>Filter</tt>, its <tt>isLoggable</tt>
* method is called to check if the given log record is loggable.
* If not we return. Otherwise the given record is copied into
* an internal circular buffer. Then the record's level property is
* compared with the <tt>pushLevel</tt>. If the given level is
* greater than or equal to the <tt>pushLevel</tt> then <tt>push</tt>
* is called to write all buffered records to the target output
* <tt>Handler</tt>.
*
* @param record description of the log event. A null record is
* silently ignored and is not published
*/
@Override
public synchronized void publish(LogRecord record) {
if (!isLoggable(record)) {
return;
}
int ix = (start+count)%buffer.length;
buffer[ix] = record;
if (count < buffer.length) {
count++;
} else {
start++;
start %= buffer.length;
}
if (record.getLevel().intValue() >= pushLevel.intValue()) {
push();
}
}
/**
* Push any buffered output to the target <tt>Handler</tt>.
* <p>
* The buffer is then cleared.
*/
public synchronized void push() {
for (int i = 0; i < count; i++) {
int ix = (start+i)%buffer.length;
LogRecord record = buffer[ix];
target.publish(record);
}
// Empty the buffer.
start = 0;
count = 0;
}
/**
* Causes a flush on the target <tt>Handler</tt>.
* <p>
* Note that the current contents of the <tt>MemoryHandler</tt>
* buffer are <b>not</b> written out. That requires a "push".
*/
@Override
public void flush() {
target.flush();
}
/**
* Close the <tt>Handler</tt> and free all associated resources.
* This will also close the target <tt>Handler</tt>.
*
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
@Override
public void close() throws SecurityException {
target.close();
setLevel(Level.OFF);
}
/**
* Set the <tt>pushLevel</tt>. After a <tt>LogRecord</tt> is copied
* into our internal buffer, if its level is greater than or equal to
* the <tt>pushLevel</tt>, then <tt>push</tt> will be called.
*
* @param newLevel the new value of the <tt>pushLevel</tt>
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
public synchronized void setPushLevel(Level newLevel) throws SecurityException {
if (newLevel == null) {
throw new NullPointerException();
}
checkPermission();
pushLevel = newLevel;
}
/**
* Get the <tt>pushLevel</tt>.
*
* @return the value of the <tt>pushLevel</tt>
*/
public Level getPushLevel() {
return pushLevel;
}
/**
* Check if this <tt>Handler</tt> would actually log a given
* <tt>LogRecord</tt> into its internal buffer.
* <p>
* This method checks if the <tt>LogRecord</tt> has an appropriate level and
* whether it satisfies any <tt>Filter</tt>. However it does <b>not</b>
* check whether the <tt>LogRecord</tt> would result in a "push" of the
* buffer contents. It will return false if the <tt>LogRecord</tt> is null.
* <p>
* @param record a <tt>LogRecord</tt>
* @return true if the <tt>LogRecord</tt> would be logged.
*
*/
@Override
public boolean isLoggable(LogRecord record) {
return super.isLoggable(record);
}
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.logging;
import java.io.*;
import java.text.*;
import java.util.Date;
import sun.util.logging.LoggingSupport;
/**
* Print a brief summary of the {@code LogRecord} in a human readable
* format. The summary will typically be 1 or 2 lines.
*
* <p>
* <a name="formatting">
* <b>Configuration:</b></a>
* The {@code SimpleFormatter} is initialized with the
* <a href="../Formatter.html#syntax">format string</a>
* specified in the {@code java.util.logging.SimpleFormatter.format}
* property to {@linkplain #format format} the log messages.
* This property can be defined
* in the {@linkplain LogManager#getProperty logging properties}
* configuration file
* or as a system property. If this property is set in both
* the logging properties and system properties,
* the format string specified in the system property will be used.
* If this property is not defined or the given format string
* is {@linkplain java.util.IllegalFormatException illegal},
* the default format is implementation-specific.
*
* @since 1.4
* @see java.util.Formatter
*/
public class SimpleFormatter extends Formatter {
// format string for printing the log record
private static final String format = LoggingSupport.getSimpleFormat();
private final Date dat = new Date();
/**
* Format the given LogRecord.
* <p>
* The formatting can be customized by specifying the
* <a href="../Formatter.html#syntax">format string</a>
* in the <a href="#formatting">
* {@code java.util.logging.SimpleFormatter.format}</a> property.
* The given {@code LogRecord} will be formatted as if by calling:
* <pre>
* {@link String#format String.format}(format, date, source, logger, level, message, thrown);
* </pre>
* where the arguments are:<br>
* <ol>
* <li>{@code format} - the {@link java.util.Formatter
* java.util.Formatter} format string specified in the
* {@code java.util.logging.SimpleFormatter.format} property
* or the default format.</li>
* <li>{@code date} - a {@link Date} object representing
* {@linkplain LogRecord#getMillis event time} of the log record.</li>
* <li>{@code source} - a string representing the caller, if available;
* otherwise, the logger's name.</li>
* <li>{@code logger} - the logger's name.</li>
* <li>{@code level} - the {@linkplain Level#getLocalizedName
* log level}.</li>
* <li>{@code message} - the formatted log message
* returned from the {@link Formatter#formatMessage(LogRecord)}
* method. It uses {@link java.text.MessageFormat java.text}
* formatting and does not use the {@code java.util.Formatter
* format} argument.</li>
* <li>{@code thrown} - a string representing
* the {@linkplain LogRecord#getThrown throwable}
* associated with the log record and its backtrace
* beginning with a newline character, if any;
* otherwise, an empty string.</li>
* </ol>
*
* <p>Some example formats:<br>
* <ul>
* <li> {@code java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"}
* <p>This prints 1 line with the log level ({@code 4$}),
* the log message ({@code 5$}) and the timestamp ({@code 1$}) in
* a square bracket.
* <pre>
* WARNING: warning message [Tue Mar 22 13:11:31 PDT 2011]
* </pre></li>
* <li> {@code java.util.logging.SimpleFormatter.format="%1$tc %2$s%n%4$s: %5$s%6$s%n"}
* <p>This prints 2 lines where the first line includes
* the timestamp ({@code 1$}) and the source ({@code 2$});
* the second line includes the log level ({@code 4$}) and
* the log message ({@code 5$}) followed with the throwable
* and its backtrace ({@code 6$}), if any:
* <pre>
* Tue Mar 22 13:11:31 PDT 2011 MyClass fatal
* SEVERE: several message with an exception
* java.lang.IllegalArgumentException: invalid argument
* at MyClass.mash(MyClass.java:9)
* at MyClass.crunch(MyClass.java:6)
* at MyClass.main(MyClass.java:3)
* </pre></li>
* <li> {@code java.util.logging.SimpleFormatter.format="%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS %1$Tp %2$s%n%4$s: %5$s%n"}
* <p>This prints 2 lines similar to the example above
* with a different date/time formatting and does not print
* the throwable and its backtrace:
* <pre>
* Mar 22, 2011 1:11:31 PM MyClass fatal
* SEVERE: several message with an exception
* </pre></li>
* </ul>
* <p>This method can also be overridden in a subclass.
* It is recommended to use the {@link Formatter#formatMessage}
* convenience method to localize and format the message field.
*
* @param record the log record to be formatted.
* @return a formatted log record
*/
public synchronized String format(LogRecord record) {
dat.setTime(record.getMillis());
String source;
if (record.getSourceClassName() != null) {
source = record.getSourceClassName();
if (record.getSourceMethodName() != null) {
source += " " + record.getSourceMethodName();
}
} else {
source = record.getLoggerName();
}
String message = formatMessage(record);
String throwable = "";
if (record.getThrown() != null) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.println();
record.getThrown().printStackTrace(pw);
pw.close();
throwable = sw.toString();
}
return String.format(format,
dat,
source,
record.getLoggerName(),
record.getLevel().getLocalizedLevelName(),
message,
throwable);
}
}

View File

@@ -0,0 +1,206 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.logging;
import java.io.*;
import java.net.*;
/**
* Simple network logging <tt>Handler</tt>.
* <p>
* <tt>LogRecords</tt> are published to a network stream connection. By default
* the <tt>XMLFormatter</tt> class is used for formatting.
* <p>
* <b>Configuration:</b>
* By default each <tt>SocketHandler</tt> is initialized using the following
* <tt>LogManager</tt> configuration properties where <tt>&lt;handler-name&gt;</tt>
* refers to the fully-qualified class name of the handler.
* If properties are not defined
* (or have invalid values) then the specified default values are used.
* <ul>
* <li> &lt;handler-name&gt;.level
* specifies the default level for the <tt>Handler</tt>
* (defaults to <tt>Level.ALL</tt>). </li>
* <li> &lt;handler-name&gt;.filter
* specifies the name of a <tt>Filter</tt> class to use
* (defaults to no <tt>Filter</tt>). </li>
* <li> &lt;handler-name&gt;.formatter
* specifies the name of a <tt>Formatter</tt> class to use
* (defaults to <tt>java.util.logging.XMLFormatter</tt>). </li>
* <li> &lt;handler-name&gt;.encoding
* the name of the character set encoding to use (defaults to
* the default platform encoding). </li>
* <li> &lt;handler-name&gt;.host
* specifies the target host name to connect to (no default). </li>
* <li> &lt;handler-name&gt;.port
* specifies the target TCP port to use (no default). </li>
* </ul>
* <p>
* For example, the properties for {@code SocketHandler} would be:
* <ul>
* <li> java.util.logging.SocketHandler.level=INFO </li>
* <li> java.util.logging.SocketHandler.formatter=java.util.logging.SimpleFormatter </li>
* </ul>
* <p>
* For a custom handler, e.g. com.foo.MyHandler, the properties would be:
* <ul>
* <li> com.foo.MyHandler.level=INFO </li>
* <li> com.foo.MyHandler.formatter=java.util.logging.SimpleFormatter </li>
* </ul>
* <p>
* The output IO stream is buffered, but is flushed after each
* <tt>LogRecord</tt> is written.
*
* @since 1.4
*/
public class SocketHandler extends StreamHandler {
private Socket sock;
private String host;
private int port;
// Private method to configure a SocketHandler from LogManager
// properties and/or default values as specified in the class
// javadoc.
private void configure() {
LogManager manager = LogManager.getLogManager();
String cname = getClass().getName();
setLevel(manager.getLevelProperty(cname +".level", Level.ALL));
setFilter(manager.getFilterProperty(cname +".filter", null));
setFormatter(manager.getFormatterProperty(cname +".formatter", new XMLFormatter()));
try {
setEncoding(manager.getStringProperty(cname +".encoding", null));
} catch (Exception ex) {
try {
setEncoding(null);
} catch (Exception ex2) {
// doing a setEncoding with null should always work.
// assert false;
}
}
port = manager.getIntProperty(cname + ".port", 0);
host = manager.getStringProperty(cname + ".host", null);
}
/**
* Create a <tt>SocketHandler</tt>, using only <tt>LogManager</tt> properties
* (or their defaults).
* @throws IllegalArgumentException if the host or port are invalid or
* are not specified as LogManager properties.
* @throws IOException if we are unable to connect to the target
* host and port.
*/
public SocketHandler() throws IOException {
// We are going to use the logging defaults.
sealed = false;
configure();
try {
connect();
} catch (IOException ix) {
System.err.println("SocketHandler: connect failed to " + host + ":" + port);
throw ix;
}
sealed = true;
}
/**
* Construct a <tt>SocketHandler</tt> using a specified host and port.
*
* The <tt>SocketHandler</tt> is configured based on <tt>LogManager</tt>
* properties (or their default values) except that the given target host
* and port arguments are used. If the host argument is empty, but not
* null String then the localhost is used.
*
* @param host target host.
* @param port target port.
*
* @throws IllegalArgumentException if the host or port are invalid.
* @throws IOException if we are unable to connect to the target
* host and port.
*/
public SocketHandler(String host, int port) throws IOException {
sealed = false;
configure();
sealed = true;
this.port = port;
this.host = host;
connect();
}
private void connect() throws IOException {
// Check the arguments are valid.
if (port == 0) {
throw new IllegalArgumentException("Bad port: " + port);
}
if (host == null) {
throw new IllegalArgumentException("Null host name: " + host);
}
// Try to open a new socket.
sock = new Socket(host, port);
OutputStream out = sock.getOutputStream();
BufferedOutputStream bout = new BufferedOutputStream(out);
setOutputStream(bout);
}
/**
* Close this output stream.
*
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
@Override
public synchronized void close() throws SecurityException {
super.close();
if (sock != null) {
try {
sock.close();
} catch (IOException ix) {
// drop through.
}
}
sock = null;
}
/**
* Format and publish a <tt>LogRecord</tt>.
*
* @param record description of the log event. A null record is
* silently ignored and is not published
*/
@Override
public synchronized void publish(LogRecord record) {
if (!isLoggable(record)) {
return;
}
super.publish(record);
flush();
}
}

View File

@@ -0,0 +1,304 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.logging;
import java.io.*;
/**
* Stream based logging <tt>Handler</tt>.
* <p>
* This is primarily intended as a base class or support class to
* be used in implementing other logging <tt>Handlers</tt>.
* <p>
* <tt>LogRecords</tt> are published to a given <tt>java.io.OutputStream</tt>.
* <p>
* <b>Configuration:</b>
* By default each <tt>StreamHandler</tt> is initialized using the following
* <tt>LogManager</tt> configuration properties where <tt>&lt;handler-name&gt;</tt>
* refers to the fully-qualified class name of the handler.
* If properties are not defined
* (or have invalid values) then the specified default values are used.
* <ul>
* <li> &lt;handler-name&gt;.level
* specifies the default level for the <tt>Handler</tt>
* (defaults to <tt>Level.INFO</tt>). </li>
* <li> &lt;handler-name&gt;.filter
* specifies the name of a <tt>Filter</tt> class to use
* (defaults to no <tt>Filter</tt>). </li>
* <li> &lt;handler-name&gt;.formatter
* specifies the name of a <tt>Formatter</tt> class to use
* (defaults to <tt>java.util.logging.SimpleFormatter</tt>). </li>
* <li> &lt;handler-name&gt;.encoding
* the name of the character set encoding to use (defaults to
* the default platform encoding). </li>
* </ul>
* <p>
* For example, the properties for {@code StreamHandler} would be:
* <ul>
* <li> java.util.logging.StreamHandler.level=INFO </li>
* <li> java.util.logging.StreamHandler.formatter=java.util.logging.SimpleFormatter </li>
* </ul>
* <p>
* For a custom handler, e.g. com.foo.MyHandler, the properties would be:
* <ul>
* <li> com.foo.MyHandler.level=INFO </li>
* <li> com.foo.MyHandler.formatter=java.util.logging.SimpleFormatter </li>
* </ul>
* <p>
* @since 1.4
*/
public class StreamHandler extends Handler {
private OutputStream output;
private boolean doneHeader;
private volatile Writer writer;
// Private method to configure a StreamHandler from LogManager
// properties and/or default values as specified in the class
// javadoc.
private void configure() {
LogManager manager = LogManager.getLogManager();
String cname = getClass().getName();
setLevel(manager.getLevelProperty(cname +".level", Level.INFO));
setFilter(manager.getFilterProperty(cname +".filter", null));
setFormatter(manager.getFormatterProperty(cname +".formatter", new SimpleFormatter()));
try {
setEncoding(manager.getStringProperty(cname +".encoding", null));
} catch (Exception ex) {
try {
setEncoding(null);
} catch (Exception ex2) {
// doing a setEncoding with null should always work.
// assert false;
}
}
}
/**
* Create a <tt>StreamHandler</tt>, with no current output stream.
*/
public StreamHandler() {
sealed = false;
configure();
sealed = true;
}
/**
* Create a <tt>StreamHandler</tt> with a given <tt>Formatter</tt>
* and output stream.
* <p>
* @param out the target output stream
* @param formatter Formatter to be used to format output
*/
public StreamHandler(OutputStream out, Formatter formatter) {
sealed = false;
configure();
setFormatter(formatter);
setOutputStream(out);
sealed = true;
}
/**
* Change the output stream.
* <P>
* If there is a current output stream then the <tt>Formatter</tt>'s
* tail string is written and the stream is flushed and closed.
* Then the output stream is replaced with the new output stream.
*
* @param out New output stream. May not be null.
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
protected synchronized void setOutputStream(OutputStream out) throws SecurityException {
if (out == null) {
throw new NullPointerException();
}
flushAndClose();
output = out;
doneHeader = false;
String encoding = getEncoding();
if (encoding == null) {
writer = new OutputStreamWriter(output);
} else {
try {
writer = new OutputStreamWriter(output, encoding);
} catch (UnsupportedEncodingException ex) {
// This shouldn't happen. The setEncoding method
// should have validated that the encoding is OK.
throw new Error("Unexpected exception " + ex);
}
}
}
/**
* Set (or change) the character encoding used by this <tt>Handler</tt>.
* <p>
* The encoding should be set before any <tt>LogRecords</tt> are written
* to the <tt>Handler</tt>.
*
* @param encoding The name of a supported character encoding.
* May be null, to indicate the default platform encoding.
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
* @exception UnsupportedEncodingException if the named encoding is
* not supported.
*/
@Override
public synchronized void setEncoding(String encoding)
throws SecurityException, java.io.UnsupportedEncodingException {
super.setEncoding(encoding);
if (output == null) {
return;
}
// Replace the current writer with a writer for the new encoding.
flush();
if (encoding == null) {
writer = new OutputStreamWriter(output);
} else {
writer = new OutputStreamWriter(output, encoding);
}
}
/**
* Format and publish a <tt>LogRecord</tt>.
* <p>
* The <tt>StreamHandler</tt> first checks if there is an <tt>OutputStream</tt>
* and if the given <tt>LogRecord</tt> has at least the required log level.
* If not it silently returns. If so, it calls any associated
* <tt>Filter</tt> to check if the record should be published. If so,
* it calls its <tt>Formatter</tt> to format the record and then writes
* the result to the current output stream.
* <p>
* If this is the first <tt>LogRecord</tt> to be written to a given
* <tt>OutputStream</tt>, the <tt>Formatter</tt>'s "head" string is
* written to the stream before the <tt>LogRecord</tt> is written.
*
* @param record description of the log event. A null record is
* silently ignored and is not published
*/
@Override
public synchronized void publish(LogRecord record) {
if (!isLoggable(record)) {
return;
}
String msg;
try {
msg = getFormatter().format(record);
} catch (Exception ex) {
// We don't want to throw an exception here, but we
// report the exception to any registered ErrorManager.
reportError(null, ex, ErrorManager.FORMAT_FAILURE);
return;
}
try {
if (!doneHeader) {
writer.write(getFormatter().getHead(this));
doneHeader = true;
}
writer.write(msg);
} catch (Exception ex) {
// We don't want to throw an exception here, but we
// report the exception to any registered ErrorManager.
reportError(null, ex, ErrorManager.WRITE_FAILURE);
}
}
/**
* Check if this <tt>Handler</tt> would actually log a given <tt>LogRecord</tt>.
* <p>
* This method checks if the <tt>LogRecord</tt> has an appropriate level and
* whether it satisfies any <tt>Filter</tt>. It will also return false if
* no output stream has been assigned yet or the LogRecord is null.
* <p>
* @param record a <tt>LogRecord</tt>
* @return true if the <tt>LogRecord</tt> would be logged.
*
*/
@Override
public boolean isLoggable(LogRecord record) {
if (writer == null || record == null) {
return false;
}
return super.isLoggable(record);
}
/**
* Flush any buffered messages.
*/
@Override
public synchronized void flush() {
if (writer != null) {
try {
writer.flush();
} catch (Exception ex) {
// We don't want to throw an exception here, but we
// report the exception to any registered ErrorManager.
reportError(null, ex, ErrorManager.FLUSH_FAILURE);
}
}
}
private synchronized void flushAndClose() throws SecurityException {
checkPermission();
if (writer != null) {
try {
if (!doneHeader) {
writer.write(getFormatter().getHead(this));
doneHeader = true;
}
writer.write(getFormatter().getTail(this));
writer.flush();
writer.close();
} catch (Exception ex) {
// We don't want to throw an exception here, but we
// report the exception to any registered ErrorManager.
reportError(null, ex, ErrorManager.CLOSE_FAILURE);
}
writer = null;
output = null;
}
}
/**
* Close the current output stream.
* <p>
* The <tt>Formatter</tt>'s "tail" string is written to the stream before it
* is closed. In addition, if the <tt>Formatter</tt>'s "head" string has not
* yet been written to the stream, it will be written before the
* "tail" string.
*
* @exception SecurityException if a security manager exists and if
* the caller does not have LoggingPermission("control").
*/
@Override
public synchronized void close() throws SecurityException {
flushAndClose();
}
}

View File

@@ -0,0 +1,269 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.logging;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
/**
* Format a LogRecord into a standard XML format.
* <p>
* The DTD specification is provided as Appendix A to the
* Java Logging APIs specification.
* <p>
* The XMLFormatter can be used with arbitrary character encodings,
* but it is recommended that it normally be used with UTF-8. The
* character encoding can be set on the output Handler.
*
* @since 1.4
*/
public class XMLFormatter extends Formatter {
private LogManager manager = LogManager.getLogManager();
// Append a two digit number.
private void a2(StringBuilder sb, int x) {
if (x < 10) {
sb.append('0');
}
sb.append(x);
}
// Append the time and date in ISO 8601 format
private void appendISO8601(StringBuilder sb, long millis) {
GregorianCalendar cal = new GregorianCalendar();
cal.setTimeInMillis(millis);
sb.append(cal.get(Calendar.YEAR));
sb.append('-');
a2(sb, cal.get(Calendar.MONTH) + 1);
sb.append('-');
a2(sb, cal.get(Calendar.DAY_OF_MONTH));
sb.append('T');
a2(sb, cal.get(Calendar.HOUR_OF_DAY));
sb.append(':');
a2(sb, cal.get(Calendar.MINUTE));
sb.append(':');
a2(sb, cal.get(Calendar.SECOND));
}
// Append to the given StringBuilder an escaped version of the
// given text string where XML special characters have been escaped.
// For a null string we append "<null>"
private void escape(StringBuilder sb, String text) {
if (text == null) {
text = "<null>";
}
for (int i = 0; i < text.length(); i++) {
char ch = text.charAt(i);
if (ch == '<') {
sb.append("&lt;");
} else if (ch == '>') {
sb.append("&gt;");
} else if (ch == '&') {
sb.append("&amp;");
} else {
sb.append(ch);
}
}
}
/**
* Format the given message to XML.
* <p>
* This method can be overridden in a subclass.
* It is recommended to use the {@link Formatter#formatMessage}
* convenience method to localize and format the message field.
*
* @param record the log record to be formatted.
* @return a formatted log record
*/
public String format(LogRecord record) {
StringBuilder sb = new StringBuilder(500);
sb.append("<record>\n");
sb.append(" <date>");
appendISO8601(sb, record.getMillis());
sb.append("</date>\n");
sb.append(" <millis>");
sb.append(record.getMillis());
sb.append("</millis>\n");
sb.append(" <sequence>");
sb.append(record.getSequenceNumber());
sb.append("</sequence>\n");
String name = record.getLoggerName();
if (name != null) {
sb.append(" <logger>");
escape(sb, name);
sb.append("</logger>\n");
}
sb.append(" <level>");
escape(sb, record.getLevel().toString());
sb.append("</level>\n");
if (record.getSourceClassName() != null) {
sb.append(" <class>");
escape(sb, record.getSourceClassName());
sb.append("</class>\n");
}
if (record.getSourceMethodName() != null) {
sb.append(" <method>");
escape(sb, record.getSourceMethodName());
sb.append("</method>\n");
}
sb.append(" <thread>");
sb.append(record.getThreadID());
sb.append("</thread>\n");
if (record.getMessage() != null) {
// Format the message string and its accompanying parameters.
String message = formatMessage(record);
sb.append(" <message>");
escape(sb, message);
sb.append("</message>");
sb.append("\n");
}
// If the message is being localized, output the key, resource
// bundle name, and params.
ResourceBundle bundle = record.getResourceBundle();
try {
if (bundle != null && bundle.getString(record.getMessage()) != null) {
sb.append(" <key>");
escape(sb, record.getMessage());
sb.append("</key>\n");
sb.append(" <catalog>");
escape(sb, record.getResourceBundleName());
sb.append("</catalog>\n");
}
} catch (Exception ex) {
// The message is not in the catalog. Drop through.
}
Object parameters[] = record.getParameters();
// Check to see if the parameter was not a messagetext format
// or was not null or empty
if ( parameters != null && parameters.length != 0
&& record.getMessage().indexOf("{") == -1 ) {
for (int i = 0; i < parameters.length; i++) {
sb.append(" <param>");
try {
escape(sb, parameters[i].toString());
} catch (Exception ex) {
sb.append("???");
}
sb.append("</param>\n");
}
}
if (record.getThrown() != null) {
// Report on the state of the throwable.
Throwable th = record.getThrown();
sb.append(" <exception>\n");
sb.append(" <message>");
escape(sb, th.toString());
sb.append("</message>\n");
StackTraceElement trace[] = th.getStackTrace();
for (int i = 0; i < trace.length; i++) {
StackTraceElement frame = trace[i];
sb.append(" <frame>\n");
sb.append(" <class>");
escape(sb, frame.getClassName());
sb.append("</class>\n");
sb.append(" <method>");
escape(sb, frame.getMethodName());
sb.append("</method>\n");
// Check for a line number.
if (frame.getLineNumber() >= 0) {
sb.append(" <line>");
sb.append(frame.getLineNumber());
sb.append("</line>\n");
}
sb.append(" </frame>\n");
}
sb.append(" </exception>\n");
}
sb.append("</record>\n");
return sb.toString();
}
/**
* Return the header string for a set of XML formatted records.
*
* @param h The target handler (can be null)
* @return a valid XML string
*/
public String getHead(Handler h) {
StringBuilder sb = new StringBuilder();
String encoding;
sb.append("<?xml version=\"1.0\"");
if (h != null) {
encoding = h.getEncoding();
} else {
encoding = null;
}
if (encoding == null) {
// Figure out the default encoding.
encoding = java.nio.charset.Charset.defaultCharset().name();
}
// Try to map the encoding name to a canonical name.
try {
Charset cs = Charset.forName(encoding);
encoding = cs.name();
} catch (Exception ex) {
// We hit problems finding a canonical name.
// Just use the raw encoding name.
}
sb.append(" encoding=\"");
sb.append(encoding);
sb.append("\"");
sb.append(" standalone=\"no\"?>\n");
sb.append("<!DOCTYPE log SYSTEM \"logger.dtd\">\n");
sb.append("<log>\n");
return sb.toString();
}
/**
* Return the tail string for a set of XML formatted records.
*
* @param h The target handler (can be null)
* @return a valid XML string
*/
public String getTail(Handler h) {
return "</log>\n";
}
}