java.util.logging.MemoryHandler Maven / Gradle / Ivy
package java.util.logging;
public class MemoryHandler extends Handler {
// default maximum buffered number of LogRecord
private static final int DEFAULT_SIZE = 1000;
// target handler
private Handler target;
// buffer size
private int size = DEFAULT_SIZE;
// push level
private Level push = Level.SEVERE;
// LogManager instance for convenience
private final LogManager manager = LogManager.getLogManager();
// buffer
private LogRecord[] buffer;
// current position in buffer
private int cursor;
/**
* Default constructor, construct and init a {@code MemoryHandler} using
* {@code LogManager} properties or default values.
*
* @throws RuntimeException
* if property value are invalid and no default value could be
* used.
*/
public MemoryHandler() {
String className = this.getClass().getName();
// init target
final String targetName = manager.getProperty(className + ".target");
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null) {
loader = ClassLoader.getSystemClassLoader();
}
Class> targetClass = loader.loadClass(targetName);
target = (Handler) targetClass.newInstance();
} catch (Exception e) {
throw new RuntimeException("Cannot load target handler '" + targetName + "'");
}
// init size
String sizeString = manager.getProperty(className + ".size");
if (sizeString != null) {
try {
size = Integer.parseInt(sizeString);
if (size <= 0) {
size = DEFAULT_SIZE;
}
} catch (Exception e) {
printInvalidPropMessage(className + ".size", sizeString, e);
}
}
// init push level
String pushName = manager.getProperty(className + ".push");
if (pushName != null) {
try {
push = Level.parse(pushName);
} catch (Exception e) {
printInvalidPropMessage(className + ".push", pushName, e);
}
}
// init other properties which are common for all Handler
initProperties("ALL", null, "java.util.logging.SimpleFormatter", null);
buffer = new LogRecord[size];
}
/**
* Construct and init a {@code MemoryHandler} using given target, size and
* push level, other properties using {@code LogManager} properties or
* default values.
*
* @param target
* the given {@code Handler} to output
* @param size
* the maximum number of buffered {@code LogRecord}, greater than
* zero
* @param pushLevel
* the push level
* @throws IllegalArgumentException
* if {@code size <= 0}
* @throws RuntimeException
* if property value are invalid and no default value could be
* used.
*/
public MemoryHandler(Handler target, int size, Level pushLevel) {
if (size <= 0) {
throw new IllegalArgumentException("size <= 0");
}
target.getLevel();
pushLevel.intValue();
this.target = target;
this.size = size;
this.push = pushLevel;
initProperties("ALL", null, "java.util.logging.SimpleFormatter", null);
buffer = new LogRecord[size];
}
/**
* Close this handler and target handler, free all associated resources.
*/
@Override
public void close() {
manager.checkAccess();
target.close();
setLevel(Level.OFF);
}
/**
* Call target handler to flush any buffered output. Note that this doesn't
* cause this {@code MemoryHandler} to push.
*/
@Override
public void flush() {
target.flush();
}
/**
* Put a given {@code LogRecord} into internal buffer. If given record is
* not loggable, just return. Otherwise it is stored in the buffer.
* Furthermore if the record's level is not less than the push level, the
* push action is triggered to output all the buffered records to the target
* handler, and the target handler will publish them.
*
* @param record
* the log record
*/
@Override public synchronized void publish(LogRecord record) {
if (!isLoggable(record)) {
return;
}
if (cursor >= size) {
cursor = 0;
}
buffer[cursor++] = record;
if (record.getLevel().intValue() >= push.intValue()) {
push();
}
}
/**
* Return the push level.
*
* @return the push level
*/
public Level getPushLevel() {
return push;
}
/**
* Check if given {@code LogRecord} would be put into this
* {@code MemoryHandler}'s internal buffer.
*
* The given {@code LogRecord} is loggable if and only if it has appropriate
* level and it pass any associated filter's check.
*
* Note that the push level is not used for this check.
*
* @param record
* the given {@code LogRecord}
* @return the given {@code LogRecord} if it should be logged, {@code false}
* if {@code LogRecord} is {@code null}.
*/
@Override
public boolean isLoggable(LogRecord record) {
return super.isLoggable(record);
}
/**
* Triggers a push action to output all buffered records to the target handler,
* and the target handler will publish them. Then the buffer is cleared.
*/
public void push() {
for (int i = cursor; i < size; i++) {
if (buffer[i] != null) {
target.publish(buffer[i]);
}
buffer[i] = null;
}
for (int i = 0; i < cursor; i++) {
if (buffer[i] != null) {
target.publish(buffer[i]);
}
buffer[i] = null;
}
cursor = 0;
}
/**
* Set the push level. The push level is used to check the push action
* triggering. When a new {@code LogRecord} is put into the internal
* buffer and its level is not less than the push level, the push action
* will be triggered. Note that set new push level won't trigger push action.
*
* @param newLevel
* the new level to set.
*/
public void setPushLevel(Level newLevel) {
manager.checkAccess();
newLevel.intValue();
this.push = newLevel;
}
}