|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
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><handler-name></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> <handler-name>.level |
|
* specifies the level for the <tt>Handler</tt> |
|
* (defaults to <tt>Level.ALL</tt>). </li> |
|
* <li> <handler-name>.filter |
|
* specifies the name of a <tt>Filter</tt> class to use |
|
* (defaults to no <tt>Filter</tt>). </li> |
|
* <li> <handler-name>.size |
|
* defines the buffer size (defaults to 1000). </li> |
|
* <li> <handler-name>.push |
|
* defines the <tt>pushLevel</tt> (defaults to <tt>level.SEVERE</tt>). </li> |
|
* <li> <handler-name>.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 |
|
|
|
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())); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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(); |
|
} |
|
|
|
|
|
private void init() { |
|
buffer = new LogRecord[size]; |
|
start = 0; |
|
count = 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(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@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(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public synchronized void push() { |
|
for (int i = 0; i < count; i++) { |
|
int ix = (start+i)%buffer.length; |
|
LogRecord record = buffer[ix]; |
|
target.publish(record); |
|
} |
|
|
|
start = 0; |
|
count = 0; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void flush() { |
|
target.flush(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void close() throws SecurityException { |
|
target.close(); |
|
setLevel(Level.OFF); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public synchronized void setPushLevel(Level newLevel) throws SecurityException { |
|
if (newLevel == null) { |
|
throw new NullPointerException(); |
|
} |
|
checkPermission(); |
|
pushLevel = newLevel; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Level getPushLevel() { |
|
return pushLevel; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public boolean isLoggable(LogRecord record) { |
|
return super.isLoggable(record); |
|
} |
|
} |