/*
* 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;
Handler
that buffers requests in a circular buffer in memory. Normally this Handler
simply stores incoming LogRecords
into its memory buffer and discards earlier records. This buffering is very cheap and avoids formatting costs. On certain trigger conditions, the MemoryHandler
will push out its current buffer contents to a target Handler
, which will typically publish them to the outside world.
There are three main models for triggering a push of the buffer:
- An incoming
LogRecord
has a type that is greater than a pre-defined level, the pushLevel
.
- An external class calls the
push
method explicitly.
- A subclass overrides the
log
method and scans each incoming LogRecord
and calls push
if a record matches some desired criteria.
Configuration: By default each MemoryHandler
is initialized using the following LogManager
configuration properties where <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. If no default value is defined then a RuntimeException is thrown.
- <handler-name>.level specifies the level for the
Handler
(defaults to Level.ALL
).
- <handler-name>.filter specifies the name of a
Filter
class to use (defaults to no Filter
).
- <handler-name>.size
defines the buffer size (defaults to 1000).
- <handler-name>.push defines the
pushLevel
(defaults to level.SEVERE
).
- <handler-name>.target specifies the name of the target
Handler
class. (no default).
For example, the properties for MemoryHandler
would be:
- java.util.logging.MemoryHandler.level=INFO
- java.util.logging.MemoryHandler.formatter=java.util.logging.SimpleFormatter
For a custom handler, e.g. com.foo.MyHandler, the properties would be:
- com.foo.MyHandler.level=INFO
- com.foo.MyHandler.formatter=java.util.logging.SimpleFormatter
Since: 1.4
/**
* {@code Handler} that buffers requests in a circular buffer in memory.
* <p>
* Normally this {@code Handler} simply stores incoming {@code LogRecords}
* into its memory buffer and discards earlier records. This buffering
* is very cheap and avoids formatting costs. On certain trigger
* conditions, the {@code MemoryHandler} will push out its current buffer
* contents to a target {@code Handler}, 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 {@code LogRecord} has a type that is greater than
* a pre-defined level, the {@code pushLevel}. </li>
* <li>
* An external class calls the {@code push} method explicitly. </li>
* <li>
* A subclass overrides the {@code log} method and scans each incoming
* {@code LogRecord} and calls {@code push} if a record matches some
* desired criteria. </li>
* </ul>
* <p>
* <b>Configuration:</b>
* By default each {@code MemoryHandler} is initialized using the following
* {@code LogManager} 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.
* If no default value is defined then a RuntimeException is thrown.
* <ul>
* <li> <handler-name>.level
* specifies the level for the {@code Handler}
* (defaults to {@code Level.ALL}). </li>
* <li> <handler-name>.filter
* specifies the name of a {@code Filter} class to use
* (defaults to no {@code Filter}). </li>
* <li> <handler-name>.size
* defines the buffer size (defaults to 1000). </li>
* <li> <handler-name>.push
* defines the {@code pushLevel} (defaults to {@code level.SEVERE}). </li>
* <li> <handler-name>.target
* specifies the name of the target {@code Handler } 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>
*
* @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;
Create a MemoryHandler
and configure it based on LogManager
configuration properties. /**
* Create a {@code MemoryHandler} and configure it based on
* {@code LogManager} configuration properties.
*/
public MemoryHandler() {
// configure with specific defaults for MemoryHandler
super(Level.ALL, new SimpleFormatter(), null);
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;
}
String targetName = manager.getProperty(cname+".target");
if (targetName == null) {
throw new RuntimeException("The handler " + cname
+ " does not specify a target");
}
Class<?> clz;
try {
clz = ClassLoader.getSystemClassLoader().loadClass(targetName);
@SuppressWarnings("deprecation")
Object o = clz.newInstance();
target = (Handler) o;
} 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 MemoryHandler
. The MemoryHandler
is configured based on LogManager
properties (or their default values) except that the given pushLevel
argument and buffer size argument are used.
Params: - target – the Handler to which to publish output.
- size – the number of log records to buffer (must be greater than zero)
- pushLevel – message level to push on
Throws: - IllegalArgumentException – if
size is <= 0
/**
* Create a {@code MemoryHandler}.
* <p>
* The {@code MemoryHandler} is configured based on {@code LogManager}
* properties (or their default values) except that the given {@code pushLevel}
* 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) {
// configure with specific defaults for MemoryHandler
super(Level.ALL, new SimpleFormatter(), null);
if (target == null || pushLevel == null) {
throw new NullPointerException();
}
if (size <= 0) {
throw new IllegalArgumentException();
}
this.target = target;
this.pushLevel = pushLevel;
this.size = size;
init();
}
Store a LogRecord
in an internal buffer. If there is a Filter
, its isLoggable
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 pushLevel
. If the given level is greater than or equal to the pushLevel
then push
is called to write all buffered records to the target output Handler
.
Params: - record – description of the log event. A null record is
silently ignored and is not published
/**
* Store a {@code LogRecord} in an internal buffer.
* <p>
* If there is a {@code Filter}, its {@code isLoggable}
* 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 {@code pushLevel}. If the given level is
* greater than or equal to the {@code pushLevel} then {@code push}
* is called to write all buffered records to the target output
* {@code Handler}.
*
* @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 Handler
.
The buffer is then cleared.
/**
* Push any buffered output to the target {@code Handler}.
* <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 Handler
. Note that the current contents of the MemoryHandler
buffer are not written out. That requires a "push".
/**
* Causes a flush on the target {@code Handler}.
* <p>
* Note that the current contents of the {@code MemoryHandler}
* buffer are <b>not</b> written out. That requires a "push".
*/
@Override
public void flush() {
target.flush();
}
Close the Handler
and free all associated resources. This will also close the target Handler
. Throws: - SecurityException – if a security manager exists and if the caller does not have
LoggingPermission("control")
.
/**
* Close the {@code Handler} and free all associated resources.
* This will also close the target {@code Handler}.
*
* @exception SecurityException if a security manager exists and if
* the caller does not have {@code LoggingPermission("control")}.
*/
@Override
public void close() throws SecurityException {
target.close();
setLevel(Level.OFF);
}
Set the pushLevel
. After a LogRecord
is copied into our internal buffer, if its level is greater than or equal to the pushLevel
, then push
will be called. Params: - newLevel – the new value of the
pushLevel
Throws: - SecurityException – if a security manager exists and if the caller does not have
LoggingPermission("control")
.
/**
* Set the {@code pushLevel}. After a {@code LogRecord} is copied
* into our internal buffer, if its level is greater than or equal to
* the {@code pushLevel}, then {@code push} will be called.
*
* @param newLevel the new value of the {@code pushLevel}
* @exception SecurityException if a security manager exists and if
* the caller does not have {@code LoggingPermission("control")}.
*/
public synchronized void setPushLevel(Level newLevel) throws SecurityException {
if (newLevel == null) {
throw new NullPointerException();
}
checkPermission();
pushLevel = newLevel;
}
Get the pushLevel
. Returns: the value of the pushLevel
/**
* Get the {@code pushLevel}.
*
* @return the value of the {@code pushLevel}
*/
public Level getPushLevel() {
return pushLevel;
}
Check if this Handler
would actually log a given LogRecord
into its internal buffer. This method checks if the LogRecord
has an appropriate level and whether it satisfies any Filter
. However it does not check whether the LogRecord
would result in a "push" of the buffer contents. It will return false if the LogRecord
is null.
Params: - record – a
LogRecord
Returns: true if the LogRecord
would be logged.
/**
* Check if this {@code Handler} would actually log a given
* {@code LogRecord} into its internal buffer.
* <p>
* This method checks if the {@code LogRecord} has an appropriate level and
* whether it satisfies any {@code Filter}. However it does <b>not</b>
* check whether the {@code LogRecord} would result in a "push" of the
* buffer contents. It will return false if the {@code LogRecord} is null.
*
* @param record a {@code LogRecord}
* @return true if the {@code LogRecord} would be logged.
*
*/
@Override
public boolean isLoggable(LogRecord record) {
return super.isLoggable(record);
}
}