/*
 * Copyright (c) 2000, 2019, 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> &lt;handler-name&gt;.level * specifies the level for the {@code Handler} * (defaults to {@code Level.ALL}). </li> * <li> &lt;handler-name&gt;.filter * specifies the name of a {@code Filter} class to use * (defaults to no {@code Filter}). </li> * <li> &lt;handler-name&gt;.size * defines the buffer size (defaults to 1000). </li> * <li> &lt;handler-name&gt;.push * defines the {@code pushLevel} (defaults to {@code level.SEVERE}). </li> * <li> &lt;handler-name&gt;.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:
/** * 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 (may be null).
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} (may be null). * @return true if the {@code LogRecord} would be logged. * */
@Override public boolean isLoggable(LogRecord record) { return super.isLoggable(record); } }