Path: blob/master/src/java.logging/share/classes/java/util/logging/MemoryHandler.java
41159 views
/*1* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.util.logging;2627/**28* {@code Handler} that buffers requests in a circular buffer in memory.29* <p>30* Normally this {@code Handler} simply stores incoming {@code LogRecords}31* into its memory buffer and discards earlier records. This buffering32* is very cheap and avoids formatting costs. On certain trigger33* conditions, the {@code MemoryHandler} will push out its current buffer34* contents to a target {@code Handler}, which will typically publish35* them to the outside world.36* <p>37* There are three main models for triggering a push of the buffer:38* <ul>39* <li>40* An incoming {@code LogRecord} has a type that is greater than41* a pre-defined level, the {@code pushLevel}. </li>42* <li>43* An external class calls the {@code push} method explicitly. </li>44* <li>45* A subclass overrides the {@code log} method and scans each incoming46* {@code LogRecord} and calls {@code push} if a record matches some47* desired criteria. </li>48* </ul>49* <p>50* <b>Configuration:</b>51* By default each {@code MemoryHandler} is initialized using the following52* {@code LogManager} configuration properties where {@code <handler-name>}53* refers to the fully-qualified class name of the handler.54* If properties are not defined55* (or have invalid values) then the specified default values are used.56* If no default value is defined then a RuntimeException is thrown.57* <ul>58* <li> <handler-name>.level59* specifies the level for the {@code Handler}60* (defaults to {@code Level.ALL}). </li>61* <li> <handler-name>.filter62* specifies the name of a {@code Filter} class to use63* (defaults to no {@code Filter}). </li>64* <li> <handler-name>.size65* defines the buffer size (defaults to 1000). </li>66* <li> <handler-name>.push67* defines the {@code pushLevel} (defaults to {@code level.SEVERE}). </li>68* <li> <handler-name>.target69* specifies the name of the target {@code Handler } class.70* (no default). </li>71* </ul>72* <p>73* For example, the properties for {@code MemoryHandler} would be:74* <ul>75* <li> java.util.logging.MemoryHandler.level=INFO </li>76* <li> java.util.logging.MemoryHandler.formatter=java.util.logging.SimpleFormatter </li>77* </ul>78* <p>79* For a custom handler, e.g. com.foo.MyHandler, the properties would be:80* <ul>81* <li> com.foo.MyHandler.level=INFO </li>82* <li> com.foo.MyHandler.formatter=java.util.logging.SimpleFormatter </li>83* </ul>84*85* @since 1.486*/8788public class MemoryHandler extends Handler {89private static final int DEFAULT_SIZE = 1000;90private volatile Level pushLevel;91private int size;92private Handler target;93private LogRecord buffer[];94int start, count;9596/**97* Create a {@code MemoryHandler} and configure it based on98* {@code LogManager} configuration properties.99*/100public MemoryHandler() {101// configure with specific defaults for MemoryHandler102super(Level.ALL, new SimpleFormatter(), null);103104LogManager manager = LogManager.getLogManager();105String cname = getClass().getName();106pushLevel = manager.getLevelProperty(cname +".push", Level.SEVERE);107size = manager.getIntProperty(cname + ".size", DEFAULT_SIZE);108if (size <= 0) {109size = DEFAULT_SIZE;110}111String targetName = manager.getProperty(cname+".target");112if (targetName == null) {113throw new RuntimeException("The handler " + cname114+ " does not specify a target");115}116Class<?> clz;117try {118clz = ClassLoader.getSystemClassLoader().loadClass(targetName);119@SuppressWarnings("deprecation")120Object o = clz.newInstance();121target = (Handler) o;122} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {123throw new RuntimeException("MemoryHandler can't load handler target \"" + targetName + "\"" , e);124}125init();126}127128// Initialize. Size is a count of LogRecords.129private void init() {130buffer = new LogRecord[size];131start = 0;132count = 0;133}134135/**136* Create a {@code MemoryHandler}.137* <p>138* The {@code MemoryHandler} is configured based on {@code LogManager}139* properties (or their default values) except that the given {@code pushLevel}140* argument and buffer size argument are used.141*142* @param target the Handler to which to publish output.143* @param size the number of log records to buffer (must be greater than zero)144* @param pushLevel message level to push on145*146* @throws IllegalArgumentException if {@code size is <= 0}147*/148public MemoryHandler(Handler target, int size, Level pushLevel) {149// configure with specific defaults for MemoryHandler150super(Level.ALL, new SimpleFormatter(), null);151152if (target == null || pushLevel == null) {153throw new NullPointerException();154}155if (size <= 0) {156throw new IllegalArgumentException();157}158this.target = target;159this.pushLevel = pushLevel;160this.size = size;161init();162}163164/**165* Store a {@code LogRecord} in an internal buffer.166* <p>167* If there is a {@code Filter}, its {@code isLoggable}168* method is called to check if the given log record is loggable.169* If not we return. Otherwise the given record is copied into170* an internal circular buffer. Then the record's level property is171* compared with the {@code pushLevel}. If the given level is172* greater than or equal to the {@code pushLevel} then {@code push}173* is called to write all buffered records to the target output174* {@code Handler}.175*176* @param record description of the log event. A null record is177* silently ignored and is not published178*/179@Override180public synchronized void publish(LogRecord record) {181if (!isLoggable(record)) {182return;183}184int ix = (start+count)%buffer.length;185buffer[ix] = record;186if (count < buffer.length) {187count++;188} else {189start++;190start %= buffer.length;191}192if (record.getLevel().intValue() >= pushLevel.intValue()) {193push();194}195}196197/**198* Push any buffered output to the target {@code Handler}.199* <p>200* The buffer is then cleared.201*/202public synchronized void push() {203for (int i = 0; i < count; i++) {204int ix = (start+i)%buffer.length;205LogRecord record = buffer[ix];206target.publish(record);207}208// Empty the buffer.209start = 0;210count = 0;211}212213/**214* Causes a flush on the target {@code Handler}.215* <p>216* Note that the current contents of the {@code MemoryHandler}217* buffer are <b>not</b> written out. That requires a "push".218*/219@Override220public void flush() {221target.flush();222}223224/**225* Close the {@code Handler} and free all associated resources.226* This will also close the target {@code Handler}.227*228* @throws SecurityException if a security manager exists and if229* the caller does not have {@code LoggingPermission("control")}.230*/231@Override232public void close() throws SecurityException {233target.close();234setLevel(Level.OFF);235}236237/**238* Set the {@code pushLevel}. After a {@code LogRecord} is copied239* into our internal buffer, if its level is greater than or equal to240* the {@code pushLevel}, then {@code push} will be called.241*242* @param newLevel the new value of the {@code pushLevel}243* @throws SecurityException if a security manager exists and if244* the caller does not have {@code LoggingPermission("control")}.245*/246public synchronized void setPushLevel(Level newLevel) throws SecurityException {247if (newLevel == null) {248throw new NullPointerException();249}250checkPermission();251pushLevel = newLevel;252}253254/**255* Get the {@code pushLevel}.256*257* @return the value of the {@code pushLevel}258*/259public Level getPushLevel() {260return pushLevel;261}262263/**264* Check if this {@code Handler} would actually log a given265* {@code LogRecord} into its internal buffer.266* <p>267* This method checks if the {@code LogRecord} has an appropriate level and268* whether it satisfies any {@code Filter}. However it does <b>not</b>269* check whether the {@code LogRecord} would result in a "push" of the270* buffer contents. It will return false if the {@code LogRecord} is null.271*272* @param record a {@code LogRecord} (may be null).273* @return true if the {@code LogRecord} would be logged.274*275*/276@Override277public boolean isLoggable(LogRecord record) {278return super.isLoggable(record);279}280}281282283