/****************************************************************************** * Copyright © 2013-2016 The Nxt Core Developers. * * * * See the AUTHORS.txt, DEVELOPER-AGREEMENT.txt and LICENSE.txt files at * * the top-level directory of this distribution for the individual copyright * * holder information and the developer policies on copyright and licensing. * * * * Unless otherwise agreed in a custom licensing agreement, no part of the * * Nxt software, including this file, may be copied, modified, propagated, * * or distributed except according to the terms contained in the LICENSE.txt * * file. * * * * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ package nxt.util; import java.util.ArrayList; import java.util.List; import java.util.logging.Formatter; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.LogRecord; /** * MemoryHandler maintains a ring buffer of log messages. The GetLog API is used * to retrieve these log messages. * * The following logging.properties entries are used: * <ul> * <li>nxt.util.MemoryHandler.level (default ALL)</li> * <li>nxt.util.MemoryHandler.size (default 100, minimum 10)</li> * </ul> */ public class MemoryHandler extends Handler { /** Default ring buffer size */ private static final int DEFAULT_SIZE = 100; /** Level OFF value */ private static final int OFF_VALUE = Level.OFF.intValue(); /** Ring buffer */ private final LogRecord[] buffer; /** Buffer start */ private int start = 0; /** Number of buffer entries */ private int count = 0; /** Publish level */ private Level level; /** * Create a MemoryHandler and configure it based on LogManager properties */ public MemoryHandler() { LogManager manager = LogManager.getLogManager(); String cname = getClass().getName(); String value; // // Allocate the ring buffer // int bufferSize; try { value = manager.getProperty(cname+".size"); if (value != null) bufferSize = Math.max(Integer.valueOf(value.trim()), 10); else bufferSize = DEFAULT_SIZE; } catch (NumberFormatException exc) { bufferSize = DEFAULT_SIZE; } buffer = new LogRecord[bufferSize]; // // Get publish level // try { value = manager.getProperty(cname+".level"); if (value != null) { level = Level.parse(value.trim()); } else { level = Level.ALL; } } catch (IllegalArgumentException exc) { level = Level.ALL; } } /** * Store a LogRecord in the ring buffer * * @param record Description of the log event. A null record is * silently ignored and is not published */ @Override public void publish(LogRecord record) { if (record != null && record.getLevel().intValue() >= level.intValue() && level.intValue() != OFF_VALUE) { synchronized(buffer) { int ix = (start+count)%buffer.length; buffer[ix] = record; if (count < buffer.length) { count++; } else { start++; start %= buffer.length; } } } } /** * Return the log messages from the ring buffer * * @param msgCount Number of messages to return * @return List of log messages */ public List<String> getMessages(int msgCount) { List<String> rtnList = new ArrayList<>(buffer.length); synchronized(buffer) { int rtnSize = Math.min(msgCount, count); int pos = (start + (count-rtnSize))%buffer.length; Formatter formatter = getFormatter(); for (int i=0; i<rtnSize; i++) { rtnList.add(formatter.format(buffer[pos++])); if (pos == buffer.length) pos = 0; } } return rtnList; } /** * Flush the ring buffer */ @Override public void flush() { synchronized(buffer) { start = 0; count = 0; } } /** * Close the handler */ @Override public void close() { level = Level.OFF; } }