| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package java.util.logging; |
| |
| /** |
| * A {@code Handler} put the description of log events into a cycled memory |
| * buffer. |
| * <p> |
| * Mostly this {@code MemoryHandler} just puts the given {@code LogRecord} into |
| * the internal buffer and doesn't perform any formatting or any other process. |
| * When the buffer is full, the earliest buffered records will be discarded. |
| * <p> |
| * Every {@code MemoryHandler} has a target handler, and push action can be |
| * triggered so that all buffered records will be output to the target handler |
| * and normally the latter will publish the records. After the push action, the |
| * buffer will be cleared. |
| * <p> |
| * The push method can be called directly, but will also be called automatically |
| * if a new <code>LogRecord</code> is added that has a level greater than or |
| * equal to than the value defined for the property |
| * java.util.logging.MemoryHandler.push. |
| * <p> |
| * {@code MemoryHandler} will read following {@code LogManager} properties for |
| * initialization, if given properties are not defined or has invalid values, |
| * default value will be used. |
| * <ul> |
| * <li>java.util.logging.MemoryHandler.filter specifies the {@code Filter} |
| * class name, defaults to no {@code Filter}.</li> |
| * <li>java.util.logging.MemoryHandler.level specifies the level for this |
| * {@code Handler}, defaults to {@code Level.ALL}.</li> |
| * <li>java.util.logging.MemoryHandler.push specifies the push level, defaults |
| * to level.SEVERE.</li> |
| * <li>java.util.logging.MemoryHandler.size specifies the buffer size in number |
| * of {@code LogRecord}, defaults to 1000.</li> |
| * <li>java.util.logging.MemoryHandler.target specifies the class of the target |
| * {@code Handler}, no default value, which means this property must be |
| * specified either by property setting or by constructor.</li> |
| * </ul> |
| */ |
| 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. |
| * <p> |
| * The given {@code LogRecord} is loggable if and only if it has appropriate |
| * level and it pass any associated filter's check. |
| * <p> |
| * 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; |
| } |
| } |